Bug Description
After returning from the iOS camera picker (PHPickerViewController), the entire app view remains in a corrupted visual state - appearing darkened/dimmed and “pushed back” as if behind an invisible modal, even though the app remains functionally operational.
Environment
- Capacitor: 7.0.0
- @capacitor**/camera**: 7.0.2
- Ionic Framework: 8.6.2 (Vue)
- Platform: iOS (tested on physical device)
- iOS Version: 18.7
- Device: iPhone 15
Steps to Reproduce
- Call
Camera.getPhoto()with any source (Camera or Photos) - iOS native camera picker (PHPickerViewController) opens
- Cancel the picker by tapping “Cancel” button
- Observe that app view is now visually corrupted (darkened/pushed back appearance)
Minimal Reproduction Code
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
const testCamera = async () => {
try {
const photo = await Camera.getPhoto({
quality: 90,
allowEditing: false,
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
});
// Success - works fine
} catch (error: any) {
// Error thrown correctly: "User cancelled photos app"
// BUT: View is now visually corrupted
}
};
Expected Behavior
After canceling the camera picker, the app should return to its normal visual state.
Actual Behavior
After canceling, the app view appears:
- Darkened/dimmed
- “Pushed back” as if behind an invisible modal
- But remains functionally operational (can click buttons, navigate, etc.)
Investigation Results
1. Console Error After Cancel
PHPickerViewControllerDelegate_Private doesn't respond to _pickerDidPerformCancelAction:
ERROR: User cancelled photos app
ERROR: <ion-modal> must be used inside ion-content.
KEY ERROR: PHPickerViewControllerDelegate_Private doesn't respond to _pickerDidPerformCancelAction:
This is the smoking gun - the Capacitor Camera plugin’s delegate is missing the private cancel action handler, causing iOS to fail proper cleanup of the PHPickerViewController dismissal.
2. First-Time Only Behavior
!!!: This bug only occurs on the first camera/gallery use. Subsequent uses work fine.
This suggests:
- First use: iOS expects
_pickerDidPerformCancelAction:delegate method, doesn’t find it, corrupts view state - Subsequent uses: iOS has already handled the missing delegate and works around it
3. CSS/DOM State Analysis
Comprehensive diagnostics show CSS is completely clean:
No stuck modal classes (modal-open, ion-modal-open, etc.)
No transform/filter/opacity changes
No stuck ion-backdrop elements
All z-index values normal
This confirms the corruption is at iOS native view controller level, not web/CSS.
4. Workarounds That DO Fix It
Opening and closing ANY Ionic modal in the app
Navigating to a different view/page
CSS manipulation does NOT fix it
DOM cleanup does NOT fix it
Programmatically created modals do NOT fix it
5. Happens With Bare Camera.getPhoto()
Tested with absolute minimal code - direct Camera.getPhoto() call with:
No composables
No utilities
No custom modals
No action sheets
Bug still occurs, confirming it’s in the Camera plugin itself.
Root Cause Analysis
The Capacitor Camera plugin’s PHPickerViewController delegate is missing the _pickerDidPerformCancelAction: method.
Evidence chain:
-
Critical Error:
PHPickerViewControllerDelegate_Private doesn't respond to _pickerDidPerformCancelAction:- This is a private delegate method iOS expects for proper PHPickerViewController cleanup
- When missing, iOS fails to properly dismiss the picker and corrupts the view hierarchy
-
First-time only behavior:
- First use: Missing delegate method causes iOS to corrupt view state
- Subsequent uses: iOS has cached the missing method and handles it differently
-
Modal error follows:
<ion-modal> must be used inside ion-content- Happens AFTER the delegate error
- Suggests iOS is trying to interact with modal system as part of failed cleanup
-
Only native operations fix it:
- Ionic modals trigger iOS view controller refresh
- CSS manipulation cannot fix native view controller corruption
Impact
This makes this plug-in completely unusable.
Also posted Camera Plugin: iOS View Corruption 1st time run · Issue #2425 · ionic-team/capacitor-plugins · GitHub
