iOS 7的UIImagePickerController预览界面是黑色的问题

21

我有一个UIImagePickerController,使用sourceType camera进行调用,但80%的时间里我得到的是一个黑色预览。如果我等待,比如说大约30秒,我就能得到一个好的预览,它会好50%的时间,然后又可能再次崩溃。

这张图片与iDevice camera shows black instead of preview非常相似。

其他人暗示GCD可能会导致摄像头出现一些问题,并且在图像选择器加载时更新UI会使其崩溃。为此,我在每个调用主线程的GCD块上都放了一个锁。

这是一个旋转以模拟活动指示器的图像示例。

-(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];


}

注意:对尚未渲染的视图进行快照会导致空快照。在快照之前,请确保您的视图至少已经渲染一次或在屏幕更新后进行快照。

每当我尝试打开选择器控制器时,就会始终显示此消息,但即使相机正确显示预览,它也会显示。我不认为错误在这里,但这让我很困扰。


你解决了吗? - Nathan H
不,最后我有一些猜测,也许该项目从iOS6升级到iOS7时遇到了一些问题,但如果我想的话,似乎无法复现此问题...解决此问题的关键是使用AVFoundation实现自己的相机。 - Heavy_Bullets
我提交了一个错误报告。我会随时通知你。AVFoundation 对我展示了相同的结果。 - Boni2k
如果预览在某些设备上可以工作,但在其他设备上无法工作,请注意digitalHound的这个答案,它救了我的一天! - DaniEll
7个回答

13

最近的一个项目中,我遇到了同样(或类似)的问题:在iOS7(iPad)上使用UIImagePickerController时,相机预览显示为黑屏。

我的发现是,GCD本身并不是问题所在。问题出现在你尝试在除了主线程之外的线程(作为NSOperation的一部分)中使用UIKit框架中任何东西时。

首先,让我说一句清楚的话,你本来就不应该这么做。但在我具体的情况下,我别无选择。因为我想其他人可能也会遇到相同的UIImagePickerController相机行为,并且具有相同的原因,我编写了一些非常简单的示例代码,以解释发生了什么。

你可以在这里找到它:https://github.com/idamediafoundry/CameraTest

ViewController显示两个简单的按钮。其中一个调用UIImagePickerController,显示相机,另一个启动一些GCD操作并执行一个简单的任务。如果你只是打开相机,一切都正常。如果你先启动操作,然后再打开相机,就会出现黑色预览问题。

再次说明,除了主线程,你不应该在任何地方调用UIKit。但我认为,当你违反此规则时,在主线程中以完全不同的流程后打开相机时,相机出现故障并不正常,对吧?

希望这有所帮助。


1
这似乎是问题的原因... 把这个给苹果开发人员,可以吗?他们要求一个示例项目。 - Heavy_Bullets
2
当然,你能把GitHub仓库的链接发给他们吗? - Bart
我想知道在这段代码/方法中是否还有什么我们可以做来规避这个问题。 - Bart
1
向你致敬,我会发送项目(给予应有的赞誉;)) - Heavy_Bullets
在我的情况下,我正在后台线程中更新UITextField的文本。将其移出线程可以解决该问题。 - Arash

10

我在网上找不到一个好的答案,所以我必须自己研究解决方法。

我使用的是ARC(像大多数人一样),所以上面给出的答案并不能真正帮助我。

这个黑色相机问题是在iOS 7中在主线程之外执行UIKit操作的副作用。总体上,后台UIKit操作会导致iOS 7上发生各种奇怪的事情(内存无法及时释放、性能异常和黑色相机问题等)。

为了避免这种情况,你需要在后台线程上不执行任何UIKit工作。

你不能在后台线程上执行以下操作: - 分配/初始化UIView和其子类 - 修改UIView和其子类(例如,设置frame,设置.image/.text属性等)。

现在,在主线程上执行此操作的问题在于,它可能会影响UI性能,这就是为什么您会看到所有这些iOS 6“后台加载”解决方案,在iOS 7上不起作用的原因之一(即导致黑色相机问题等)。

我做了两件事来提高性能,同时防止后台UIKit操作的不良影响: - 在后台线程上预先创建和初始化所有UIImages。您在主线程上应该只使用UIImages执行“imageView.image = preloadedImageObject;”操作。 - 在可能的情况下重复使用视图。在主线程上进行[[UIView alloc] init]操作仍可能存在问题,如果您在cellForRowAtIndex或其他对响应性要求超高的情况下执行这些操作。

祝你好运!您可以通过监视应用程序运行时的内存使用情况来测试自己的表现。Background UIKit => 快速升级的内存使用量。


我一直有一个问题,就是很难辨别在后台使用的UIKit禁用方法,所以这可能是我的问题原因,尽管我几乎可以确定没有修改任何UIKit... 我会检查一下并让你知道...问候! - Heavy_Bullets
我看到我在下面重复回答了 :) 这确实是正确的答案。 - Bart
这是正确的答案,也感谢Bart提供了一个我发送给苹果的项目.. 谢谢大家。 - Heavy_Bullets
苹果有回复吗?我仔细检查了主线程的问题,但没有发现任何异常。虽然我的应用程序最多使用100 MB的“Live Bytes”,但在打开相机预览时只使用了约20 MB,这应该不算太多... - Boni2k
1
你确定吗?试试这个库,它会帮助你调试是否正在主线程之外尝试一些基本的UIKit操作。它应该在github / google上可以找到:PSPDFUIKitMainThreadGuard.m - Dinkman123
今晚我遇到了与相机预览相同的问题,问题的根本原因是...应用程序委托中的完全不相关的代码在后台队列上修改了UI。一旦我从后台队列中消除了对UI代码的调用,相机预览就变得快速而可靠。这篇文章(http://www.cocoanetics.com/2013/02/uiview-background-queue-debugging/)在追踪UI从后台队列中被修改的位置方面非常有帮助。 - Mike Hay

5
我遇到了相同的问题,但是应用程序从未显示要求授权访问照片的警报,并且在“设置->隐私->照片”中也从未显示出请求权限的选项。它只会显示黑屏而没有权限提示。最终,我将其追踪到info.plist文件。

enter image description here

事实证明,如果在info.plist中添加“Bundle display name”键并将值留空,则权限提示框永远不会显示。看起来权限提示框会拉取此值以在提示框中显示,如果您将其留空,则不会显示提示框。
如果您的plist中有此项但没有值,则可能会导致黑色预览。
编辑:我在某些设备上遇到了此问题,而在其他设备上没有遇到过。经过我的测试,我测试的所有3台iPhone 6设备和iPhone 5s都不会显示警报,并会停留在黑屏上。 iPhone 4s和iPhone 6S +显示了警报并正常工作。

我刚刚测试了一个新的空项目,即使没有Bundle显示名称,相机也可以工作。如果没有Bundle显示名称键,则使用Bundle名称。我不认为这是OP的问题。 - jcesarmobile
1
尝试在运行iOS 9.1的iPhone 6上进行测试。对于我来说,iPhone 4s和iPhone 6S+都可以正常工作并显示警报,而iPhone 5s和6则无法。 - digitalHound
我已经尝试过在运行iOS 9.1的iPhone 6上没有Bundle显示名称键,它可以正常工作,它会显示Bundle名称。 - jcesarmobile
如果您删除了键,则可以正常工作,如果您添加了键但未提供值,则我将无法收到警报。添加键但将值留为空字符串,您应该会看到问题。 - digitalHound
2
你的回答对我非常有用,我花了大约两天的时间来解决它。谢谢,伙计。 - Arvind Patidar
显示剩余2条评论

2
首先,这个问题似乎部分归咎于苹果和开发者,主要是开发者的责任。我能让我的应用在使用UIImagePickerController相机模式时通过切换本地相机应用程序和我的应用程序来产生黑色预览图像。虽然不总是会发生,但只需重复这些步骤5至15次,最终就会变成黑色预览。

另一方面,我通过重新构建代码解决了这个问题。 我不需要像其他人可能使用AVFoundation构建自定义相机一样,而是从我的代码中删除了所有潜在/有效的内存泄漏。请确保使用Instruments测试您的应用程序,消除任何泄漏。同时清理UIImagePickerController,这可能因使用ARC与否而有所不同。在重新构建后,黑色预览问题消失了。 我认为你的应用程序中存在内存泄漏,这是导致视频预览在一段时间后变黑的原因。

最后,我还有另一个应用程序使用zxing扫描器,它使用AVFoundation从实时预览流中解码图像……这个问题非常棘手,因为它涉及到ARC混合与非ARC(zxing非ARC)。由于视图/UIViewController层次结构更加复杂,并且获取包含AVFoundation会话的实际对象进行dealloc是关键,所以我必须重新构建大量代码。经过一天的重构,这个问题得到了解决,因此,在我在iOS 7中遇到黑色图像预览问题的两种情况下,修补内存泄漏是答案。


1
在我的情况下,我不得不将一些方法移动到主线程中。
我的应用程序创建了一个带有新上下文的新图像,我可以在不同的线程中创建新上下文并使用CGContext函数(例如CGContextScaleCTM或CGContextTranslateCTM或CGContextConcatCTM)和[uiimage drawInRect:Mybounds];。
基本上,当我在上下文中绘制层时,我不得不将renderInContext方法移动到主线程中:
CGSize sizeView = viewPrintBase.frame.size;
UIGraphicsBeginImageContext(sizeView);
currentContext = UIGraphicsGetCurrentContext();

dispatch_sync(dispatch_get_main_queue(), ^{
    [viewPrintBase.layer renderInContext:currentContext];
    [imageViewPhotoUser.layer renderInContext:currentContext];
    if (imageViewMask) {
        [imageViewMask.layer renderInContext:currentContext];
    }
});
[imageSnapShotDrawinfView drawInRect:viewPrintBase.bounds];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

CGImageRef imageRef = CGImageCreateWithImageInRect([finalImage CGImage], viewPrintBase.bounds);
// or use the UIImage wherever you like
imageSnapShot = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);

我不得不将所有UI对象的创建移动到主线程中,例如:

    __block UIView *viewPrintBase;
dispatch_sync(dispatch_get_main_queue(), ^{
    viewPrintBase = [[UIView alloc]initWithFrame:CGRectMake(0, 0, WIDTH_FINAL_IMAGE, HEIGHT_FINAL_IMAGE)];
    viewPrintBase.backgroundColor = [UIColor whiteColor];
    [viewPrintBase setClipsToBounds:YES];
    //We get the photo user
});

我希望这有所帮助 :)

1

首先检查您的应用程序是否有访问相机的权限。我认为您在应用程序首次启动相机时选择了“否”选项。现在请执行以下步骤:转到“设置” ->“隐私” ->“相机”,然后选择您的应用程序,在那里检查它是开启还是关闭,如果关闭,请将其打开。现在您可以在应用程序中访问相机。


0

看起来这是iOS 7的问题。我也遇到了类似的情况,当我从我的应用程序内的图像库上传照片,然后尝试打开相机时,会出现黑屏。


我使用AVFoundation手动创建了一个相机应用程序来解决这个问题...我向苹果报告了这个错误,并收到了回复,要求我创建一个能够展示此行为的项目...问题是...我不知道为什么会发生这种情况,所以很难复制这个问题... - Heavy_Bullets

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接