【问题标题】:AVCaptureDevice.requestAccess presents unexpected behavior with a UINavigationControllerAVCaptureDevice.requestAccess 使用 UINavigationController 呈现意外行为
【发布时间】:2019-04-11 08:02:51
【问题描述】:

使用 Xcode 10.1 和 Swift 4.2

我有一个复杂的应用程序,它使用在AppDelegate 中实现的UINavigationController

navigationController的rootViewController是一个DashboardController()类(UIViewController的子类)

DashboardController 使用多个 ViewController(使用 self.addChild(viewController))实现了左侧菜单抽屉

一切正常,除非我需要推送 viewController 来呈现 BarCodeScannerView().

准系统 barCodeScannerView 可以按预期推送弹出

当我请求访问相机时出现问题(仅第一次)。

  1. 只要我将Device.requestAccess(for:) 呈现如下:viewController 被弹出 并且前一个视图(rootViewController)被呈现。 (还是用“App想要访问相机”AlertView)

    func requestCameraAccess() { AVCaptureDevice.requestAccess(for: AVMediaType.video) { 授予 如果授予{ self.launchScanner() } 别的 { self.goBack() } } }

  2. 如果我单击“确定”,系统将注册已授予访问权限,但 applicationDidBecomeActive(在 AppDelegate 中)在大约 1 秒后被调用。我在applicationDidBecomeActive 中有一些初始化程序,它们都被再次执行。在短暂的延迟之后,一切正常。

顺便说一句:applicationWillResignActiveapplicationDidEnterBackgroundapplicationWillEnterForeground 未被调用。所以很明显,这不是 App LifeCycle 的一部分。

知道我会在这里做什么吗?什么可以让app内的系统调用applicationDidBecomeActive?并且仍然保持一切正常运行?

提前谢谢...

更新阅读 cmets 后,我能够将问题 #2 隔离如下:

带有UINavigationController 的简单/准系统项目,dashboardViewController 为rootViewController。 dashboardViewController 在viewDidLoad() 中推送一个CameraViewController()。 cameraViewController 请求访问相机。单击确定时,将触发对applicationDidBecomeActive 的调用。

附上完整的项目。 (.plist 中的“隐私 - 相机使用说明”键除外。

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow? = UIWindow()
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let dashboardViewController = DashboardViewController()
        window?.rootViewController = UINavigationController(rootViewController: dashboardViewController)
        window?.makeKeyAndVisible()
        return true
    }
    func applicationDidBecomeActive(_ application: UIApplication) {
        print("applicationDidBecomeActive")
    }
    func applicationWillResignActive(_ application: UIApplication) {}
    func applicationDidEnterBackground(_ application: UIApplication) {}
    func applicationWillEnterForeground(_ application: UIApplication) {}
    func applicationWillTerminate(_ application: UIApplication) {}
}
class DashboardViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        let cameraVC = CameraViewController()
        self.navigationController?.pushViewController(cameraVC, animated: true)
    }
}
import AVFoundation
class CameraViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        AVCaptureDevice.requestAccess(for: AVMediaType.video) { granted in
            if granted {
                print("Access granted")
            }
        }
    }   
}

【问题讨论】:

  • "所以很明显这不是 App LifeCycle 的一部分" 这一点都不清楚。除非你(错误地)调用didBecomeActive,否则只有运行时可以调用它,它调用它是因为我们变得活跃。
  • 但是,我认为我们应该首先关注这一点:“一旦我呈现 Device.requestAccess(for:) 如下:viewController 被弹出并呈现前一个视图 (rootViewController)。 (仍然使用“应用程序想要访问相机”AlertView)“这是错误的,您应该考虑为什么要这样做。一旦调用了授权请求,在您收到异步结果之前什么都不会发生。因此,您需要专注于为什么会在授权对话框后面发生这种弹出。
  • 谢谢@matt。在阅读了您的 cmets 之后,我怀疑这与我有几个由 DashboardController 管理的 ViewControllers (使用 addChild(viewController))...顺便说一句...对 didBecomeActive 的调用是由运行时触发的。我检查了我的代码以确保我没有调用它,并且还在 .didBecomeActiveNotification 通知中添加了一个观察者来验证它。
  • 嗨@matt 我创建了一个新的准系统项目来复制这个问题,我发现了一些事情:1. ViewController 的弹出与相机的 .requestAccess 无关 2.- 它是也没有将 viewControllers 用作根 viewController 的子级。我能够使用一个简单的 NavController 复制问题 #2,该 NavController 推送一个简单的 UIViewController,并且在 viewDidLoad 中请求访问会触发对 applicationDidBecomeActive 的调用。我将代码添加到问题中.....
  • 嗯,您可能根本不应该在视图中请求访问,因为您的视图控制器的视图还没有在界面中。因此,如果这是问题所在,请尽量不要这样做。无论如何,如果您将其发布在 github 或类似网站上,我将很高兴看到该项目。

标签: ios swift uinavigationcontroller avcapturedevice


【解决方案1】:

我想说问题出在您的测试程序上。当我在applicationWillResignActive 中使用print 语句运行您的代码时,我看到的是:

applicationDidBecomeActive
applicationWillResignActive
Access granted
applicationDidBecomeActive

这似乎完全有条不紊和正常。得到一个虚假的didBecomeActive 会很奇怪,但事实并非如此;我们主动辞职,然后再次变得活跃,这很好。您应该期望您的应用程序可以随时退出激活状态并再次激活。正常生命周期中的许多事情都可能导致这种情况,并且像授权对话框这样的进程外对话框的呈现可以合理地成为其中之一。您应该以能够应对这种可能性的方式编写代码。

【讨论】:

  • 我在applicationWillResignActive中也放了一个print(),但由于某种原因,我忽略了它......现在。考虑到这一点,结论是请求访问相机会暂时禁用应用程序,这在应用程序生命周期文档中并不清楚......developer.apple.com/documentation/uikit/core_app/…我不认为警报视图是“中断”,但是我们将不得不管理它。至少我学到了一些东西……谢谢……
  • 在某种程度上你的问题让我想起了我的问题:stackoverflow.com/questions/46350524/… 但是很多事情会导致应用程序“闪烁” - 在 iPad 上,用户可以在多任务模式下更改我们的大小,一个电话可以打进来,用户可以召唤控制中心并将其关闭,用户可以切换到应用程序切换器界面并返回等等。您的代码需要在正确的位置。如果您假设应用程序在启动时只会激活一次,那是一个错误的假设。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-11
相关资源
最近更新 更多