UIGraphicsGetImageFromCurrentImageContext 导致导航控制器返回动画不稳定

Posted

技术标签:

【中文标题】UIGraphicsGetImageFromCurrentImageContext 导致导航控制器返回动画不稳定【英文标题】:UIGraphicsGetImageFromCurrentImageContext causes choppy Navigation Controller Back animation 【发布时间】:2017-10-31 21:48:09 【问题描述】:

我有一个 UINavigationController,它有视图控制器 A。我将视图控制器 B 推到它上面。视图控制器 B 包含一个图像视图,它通过以下代码异步加载其图像:

let ss = MKMapSnapshotter(options: options)

ss!.start  (snapshot, error) in

    guard let snappy = snapshot else 
        return
    

    let snap = snappy.image

    ...
    // create a pinView which gets combined with the snapshot image
    ...

    UIGraphicsBeginImageContextWithOptions(snap.size, false, snap.scale)

    snap.draw(at: CGPoint(x: 0, y: 0))

    let rect = CGRect(origin: CGPoint(x: self.frame.width/2 - pinView.frame.width/2, y: self.frame.height/2 - pinView.frame.height/2), size: CGSize(width: pinView.frame.width, height: pinView.frame.height))

    pinView.drawHierarchy(in: rect, afterScreenUpdates: true)

    let finalSnap = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    self.snapshotView.image = finalSnap

每当我从 VC B 按下后退按钮返回到 VC A 时,如果图像尚未完成渲染,则弹出动画非常不稳定/缓慢/卡顿。我注释了行以查看是哪一行导致了呆滞,它发生在 UIGraphicsGetImageFromCurrentImageContext 上(如果我在调用该行之前按

我尝试通过ss.cancel() 取消 MKMapSnapshotter,我尝试将 UIGraphics 上下文部分包装在DispatchQueue.main.async 中,我还尝试在viewWillDisappear 期间设置一个标志并检查isMovingFromParentViewController 是否告诉它不要运行该行,但由于图像创建是异步的并立即触发,因此不能保证它已经加载。他们都没有为我工作。

有没有人知道如何正确地告诉我的 VC B 停止所有进程,或者我该如何解决这个问题?如果我让快照图像完全加载,则不会发生此问题,但我不能指望会发生这种情况。

【问题讨论】:

尝试在后台创建图像。只有图像视图的分配需要在主队列中。 【参考方案1】:

根据@rmaddy 的建议,我将图像创建移至后台线程。然而,我确实遇到了一个问题,drawHierarchy(in: rect, afterScreenUpdates: true) 不能在后台线程上调用,所以我改变了创建 pinView 的方式,这样我只需通过 draw in 将 UIImage 添加到我当前的图形上下文中矩形。

最终的代码是这样工作的:

DispatchQueue.global(qos: .background).async 
    UIGraphicsBeginImageContextWithOptions(snap.size, false, snap.scale)

    snap.draw(at: CGPoint(x: 0, y: 0))

    let rect = CGRect(origin: CGPoint(x: self.frame.width/2 - pinView.frame.width/2, y: self.frame.height/2 - pinView.frame.height/2), size: CGSize(width: pinView.frame.width, height: pinView.frame.height))

    pinView.image?.draw(in: rect) // changed this to allow background thread execution

    let finalSnap = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    // main thread image view assignment    
    DispatchQueue.main.async 
        self.snapshotView.image = finalSnap
              

【讨论】:

以上是关于UIGraphicsGetImageFromCurrentImageContext 导致导航控制器返回动画不稳定的主要内容,如果未能解决你的问题,请参考以下文章