调用 UIImagePickerController 提供黑屏

Calling UIImagePickerController provides black screen

我正在尝试从图库中获取照片以将其发送到 api。当我调用 AttachNewFileClick() 时,出现黑屏。

@IBAction func AttachNewFileClick(_ sender: Any) {
    if checkPhotoAccess()==true {
        let imagePicker = UIImagePickerController(rootViewController: self)
        imagePicker.delegate = self
        imagePicker.sourceType = .photoLibrary
        imagePicker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
        present(imagePicker, animated: true, completion: nil)
    }
}

func checkPhotoAccess() -> Bool {
    let status:PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus();
    switch status {
    case PHAuthorizationStatus.notDetermined:
        PHPhotoLibrary.requestAuthorization({status in
            return
        })
        return checkPhotoAccess()
    case .denied:
        return false
    case .restricted:
        let permissionRequestAlert:UIAlertView = UIAlertView(title: "Доступ к галлерее заблокирован", message: "Приложение не может получить доступ к вашей галлерее для прикрепления фото. Вероятно ранее вы заблокировали эту возможность", delegate: self, cancelButtonTitle: "Отмена", otherButtonTitles: "Открыть настройки")
        permissionRequestAlert.tag = PERMISSION_REQUEST_ALERT_TAG
        permissionRequestAlert.show()
        return false
    case .authorized:
        return true
    }
}

然后我给所有的异常添加了断点,并在抛出异常之前高亮了这段代码

libobjc.A.dylib`objc_exception_throw: -> 0x1159b0f11 <+0>: pushq %rbp

和日志中的异常:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKey:]: key cannot be nil'

我 运行 模拟器中的应用 iPhone SE (11.2 Xcode 9.2)。项目最低 iOS 版本为 8.2。 UIImagePickerController 在模态显示的 NavigationViewController 中被调用。

如何解决这个问题?

您崩溃的原因是这一行:

let imagePicker = UIImagePickerController(rootViewController: self)

这不是您创建 UIImagePickerController 的方式。只需创建它!它已经有了自己的根视图控制器;不要试图改变它。就说:

let imagePicker = UIImagePickerController()

此外,您的 checkPhotoAccess 代码存在致命缺陷:

func checkPhotoAccess() -> Bool {
    let status:PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus();
    switch status {
    case PHAuthorizationStatus.notDetermined:
        PHPhotoLibrary.requestAuthorization({status in
            return
        })
        return checkPhotoAccess()
    case .denied:
        return false
    case .restricted:
        let permissionRequestAlert:UIAlertView = UIAlertView(title: "Доступ к галлерее заблокирован", message: "Приложение не может получить доступ к вашей галлерее для прикрепления фото. Вероятно ранее вы заблокировали эту возможность", delegate: self, cancelButtonTitle: "Отмена", otherButtonTitles: "Открыть настройки")
        permissionRequestAlert.tag = PERMISSION_REQUEST_ALERT_TAG
        permissionRequestAlert.show()
        return false
    case .authorized:
        return true
    }
}

为了了解原因,让我们排除除 notDetermined 情况之外的所有内容:

func checkPhotoAccess() -> Bool {
    PHPhotoLibrary.requestAuthorization({status in
        return
    })
    return checkPhotoAccess()
}

现在考虑执行这些行的顺序requestAuthorization 异步的 。因此执行顺序是这样的:

func checkPhotoAccess() -> Bool {
    PHPhotoLibrary.requestAuthorization({status in
        return // 2
    })
    return checkPhotoAccess() // 1
}

好的,但是 2 永远不会 执行,因为 1 return 是第一个。所以我们剩下这个:

func checkPhotoAccess() -> Bool {
    return checkPhotoAccess()
}

但这显然是疯了。我们有一个无限递归。

要点是您不能 return 来自checkPhotoAccess 的值,因为您需要调用异步的requestAuthorization。实现这种事情的正确方法是 return false.notDetermined 情况下,或者(更好)编写一个 checkPhotoAccess 方法来接收 完成函数,如果它发现我们有授权,然后可以调用那个函数。


另外你需要立即停止使用 UIAlertView 并转换为 UIAlertController。 UIAlertView 已在 long 前弃用。编译器会就此警告您,所以请听听它告诉您的内容!