Search code examples
iphoneuiimagepickercontrollerios7

iOS 7 UIImagePickerController has black preview


I have a UIImagePickerController being called with sourceType camera and 80% of the time I get a black preview. If I wait, let's say around 30 seconds, I get a good preview and it will be good for around 50% of the time, then it can break again.

The image in question is very similar to this. iDevice camera shows black instead of preview

Other people imply that GCD might be causing some issues with the camera, and that updates to the UI while the image picker is being loaded break it. I put a lock on every GCD block that calls the main thread for this purpose.

This is an example of an image that rotates to simulate an activity indicator.

-(void)animateLoadingImage {

if (isMainThreadBlocked) {
    return;
}

self.radians += M_PI_4 / 2;
[UIView beginAnimations:@"progress rotation" context:nil];
[UIView setAnimationDuration:.1];
self.loadingImageView.transform = CGAffineTransformMakeRotation(self.radians);
[UIView commitAnimations];


}

PS: Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.

This shows ALWAYS when I try to open the picker controller, but it shows it even if the camera shows the preview correctly. I don't think the error is around here but this bugs me a lot also.


Solution

  • There doesn't seem to be a good answer to this anywhere online, so I had to slog through this myself to figure it out.

    I'm using ARC (like probably most others these days), so the answer above didn't really help me.

    This black camera issue happens as a side effect of doing UIKit stuff off of the main thread on iOS 7. Broadly, background UIKit operations results in all sorts of weird things happening on iOS 7 (memory not being deallocated promptly, weird performance hitches, and the black camera issue).

    To prevent this, you need to not do ANY UIKit stuff on background threads.

    Things you cannot do on background threads: - Allocate/initialize UIViews and subclasses - Modify UIViews and subclasses (e.g., setting frame, setting .image/.text attributes, etc).

    Now, the problem with doing this stuff on the main thread is that it can mess with UI performance, which is why you see all these iOS 6 "background loading" solutions that DO NOT work optimally on iOS 7 (i.e., causing the black camera issue, among other things).

    There are two things that I did to fix performance while preventing the ill effects of background UIKit operations: - Pre-create and initialize all UIImages on a background thread. The only thing you should be doing with UIImages on the main thread is setting "imageView.image = preloadedImageObject;" - Re-use views whenever possible. Doing [[UIView alloc] init] on the main thread can still be problematic if you're doing it in a cellForRowAtIndex or in another situation where responsiveness is super important.

    Best of luck! You can test how you're doing by monitoring memory usage while your app is running. Background UIKit => rapidly escalating memory usage.