TimWhiting
TimWhiting

Reputation: 2497

UIImagePickerController displaying wrong crop when editing is allowed

I am having an issue with the UIImagePickerController, I have enabled editing, but what is happening is when the user crops the image, there is a y offset between what the square crop shows the user will be the crop, and what the actual crop is. It only works out at about 20 pixels, but is now a big issue. I have included two screenshots that demonstrate the issue. In the first, the crop square seems to extend above the image, but when the inage is chosen the top of the image is set properly (the image is just a screenshot so the top of the image is the top of the status bar). In the second screenshot, if you try and put the crop to then very bottom of the photo, it springs back to this position, so the user thinks the bottom of the image is not included, when in fact it is, when it is chosen. Bit of a headache. Seems to do the same in the simulator as on device. Anyone know why this might be?

Top cropbottom crop

Upvotes: 5

Views: 1463

Answers (4)

Ondřej Korol
Ondřej Korol

Reputation: 714

For anyone who has a similar problem. I had a issue with images having a black line at the top of photo when allowEditing was set to true. I was testing this on iPhone 11, iOS 14.

I've discovered that the cause for this was setting a different modalPresentationStyle of UIImagePickerViewController. Once I removed this line:

 picker.modalPresentationStyle = .overFullScreen

edited image retrieved in delegate method has correct size.

Upvotes: 0

Sam Si Tayeb
Sam Si Tayeb

Reputation: 83

I have recently come across this issue. Here is what fixed it for me:

extension UIImagePickerController {
    open override var childForStatusBarHidden: UIViewController? {
        return nil
    }
    
    open override var prefersStatusBarHidden: Bool {
        return true
    }
}

"View controller-based status bar appearance" in plist must be YES, otherwise the above code will not be called.

Upvotes: 0

Albert Renshaw
Albert Renshaw

Reputation: 17882

The issue is with the status bar showing on newer devices, it messes with the crop offset in unpredictable ways. To fix all of this easily, simply:

1. Add the following property to your current ViewController:

@property BOOL hideStatusBar;

2. Add the following methods to your current ViewController:

-(BOOL)prefersStatusBarHidden {
    return _hideStatusBar; 
}

-(UIViewController *)childViewControllerForStatusBarHidden {
    return nil; 
}

3. Add the following to the method file where you display your imagePicker. Also add UINavigationControllerDelegate to the .h file.
(Note: you likely already have these methods declared so just add the snippets inside them)

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {

    YourCurrentViewController.hideStatusBar = YES;
    [YourCurrentViewController setNeedsStatusBarAppearanceUpdate];
    [navigationController setNeedsStatusBarAppearanceUpdate];

    //The rest of your code here

    //!IMPORTANT: ENSURE YOU'VE ADDED <UINavigationControllerDelegate> IN YOUR .H FILE

}

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    YourCurrentViewController.hideStatusBar = NO;
    [YourCurrentViewController setNeedsStatusBarAppearanceUpdate];

    //The rest of your code here

}

-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

    YourCurrentViewController.hideStatusBar = NO;
    [YourCurrentViewController setNeedsStatusBarAppearanceUpdate];

    //The rest of your code here

}

Now the status bar will hide when you launch your imagePicker and will re-appear after you are done picking an image or cancel. With the status bar hiding, the crop offsets will get calculated properly on Apple's end and your edited photo will be correctly cropped!

Upvotes: 0

Albert Bori
Albert Bori

Reputation: 10002

This doesn't solve the display issue where it shows the cropping box 20px off, but you can side-step the final-cropped image bug by doing the following when the user selects an image:

// MARK: - UIImagePickerControllerDelegate

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    picker.dismiss(animated: true, completion: { [weak self] in self?.didCompleteSelection(image: nil) })
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    picker.dismiss(animated: true, completion: { [weak self] in
        guard
            let originalImage = info[UIImagePickerControllerOriginalImage] as? UIImage,
            let cropRect = info[UIImagePickerControllerCropRect] as? CGRect
        else { return }

        //crop with status-bar offset due to iOS bug introduced in iOS 8 with status bar offset
        let offsetRect = cropRect.offsetBy(dx: 0, dy: UIApplication.shared.statusBarFrame.size.height)
        guard let croppedImage = originalImage.cgImage?.cropping(to: offsetRect) else { return }

        self?.didCompleteSelection?(image: croppedImage)
    })
}

Upvotes: 2

Related Questions