在transitionWithView中更改rootViewController时泄漏视图 [英] Leaking views when changing rootViewController inside transitionWithView

查看:15
本文介绍了在transitionWithView中更改rootViewController时泄漏视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在调查内存泄漏时,我发现了一个与在过渡动画块中调用 setRootViewController: 的技术相关的问题:

While investigating a memory leak I discovered a problem related to the technique of calling setRootViewController: inside a transition animation block:

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{ self.window.rootViewController = newController; }
                completion:nil];

如果旧的视图控制器(被替换的那个)当前正在呈现另一个视图控制器,那么上面的代码不会从视图层次结构中删除呈现的视图.

If the old view controller (the one being replaced) is currently presenting another view controller, then the above code does not remove the presented view from the view hierarchy.

也就是这个操作序列...

That is, this sequence of operations...

  1. X 成为根视图控制器
  2. X 呈现 Y,因此 Y 的视图显示在屏幕上
  3. 使用 transitionWithView: 使 Z 成为新的根视图控制器
  1. X becomes Root View Controller
  2. X presents Y, so that Y's view is on screen
  3. Using transitionWithView: to make Z the new Root View Controller

...对用户来说看起来没问题,但是调试视图层次结构工具会显示 Y 的视图仍然存在于 Z 的视图​​后面,在 UITransitionView 内.即经过以上三步后,视图层次为:

...looks OK to the user, but the Debug View Hierarchy tool will reveal that Y's view is still there behind Z's view, inside a UITransitionView. That is, after the three steps above, the view hierarchy is:

  • 界面窗口
    • UITransitionView
      • UIView(Y 的视图)

      我怀疑这是一个问题,因为在转换时,X 的视图实际上并不是视图层次结构的一部分.

      I suspect this is a problem because, at the time of the transition, X's view isn't actually part of the view hierarchy.

      如果我在 transitionWithView: 之前立即将 dismissViewControllerAnimated:NO 发送到 X,则生成的视图层次结构为:

      If I send dismissViewControllerAnimated:NO to X immediately before transitionWithView:, the resulting view hierarchy is:

      • 界面窗口
        • UIView(X 的视图)
        • UIView(Z 的视图​​)

        如果我将dismissViewControllerAnimated:(YES 或NO)发送给X,然后在completion: 块中执行转换,则视图层次结构是正确的.不幸的是,这会干扰动画.如果动画解雇,则浪费时间;如果没有动画,它看起来坏了.

        If I send dismissViewControllerAnimated: (YES or NO) to X, then perform the transition in the completion: block, then the view hierarchy is correct. Unfortunately, that interferes with the animation. If animating the dismissal, it wastes time; if not animating, it looks broken.

        我正在尝试其他一些方法(例如,创建一个新的容器视图控制器类作为我的根视图控制器),但没有找到任何有效的方法.我会随时更新这个问题.

        I'm trying some other approaches (e.g., making a new container view controller class to serve as my root view controller) but haven't found anything that works. I'll update this question as I go.

        最终目标是直接从呈现的视图过渡到新的根视图控制器,而不会留下杂散的视图层次结构.

        The ultimate goal is to transition from the presented view to a new root view controller directly, and without leaving stray view hierarchies around.

        推荐答案

        我最近遇到了类似的问题.我不得不手动从窗口中删除 UITransitionView 以解决问题,然后在之前的根视图控制器上调用dismiss 以确保其被释放.

        I had a similar issue recently. I had to manually remove that UITransitionView from the window to fix the problem, then call dismiss on the previous root view controller to ensure its deallocated.

        修复并不是很好,但是除非您在发布问题后找到了更好的方法,否则我发现这是唯一可行的方法!viewController 只是您原始问题中的 newController.

        The fix is not really very nice but unless you've found a better way since posting the question, its the only thing I've found to work! viewController is just the newController from your original question.

        UIViewController *previousRootViewController = self.window.rootViewController;
        
        self.window.rootViewController = viewController;
        
        // Nasty hack to fix http://stackoverflow.com/questions/26763020/leaking-views-when-changing-rootviewcontroller-inside-transitionwithview
        // The presenting view controllers view doesn't get removed from the window as its currently transistioning and presenting a view controller
        for (UIView *subview in self.window.subviews) {
            if ([subview isKindOfClass:NSClassFromString(@"UITransitionView")]) {
                [subview removeFromSuperview];
            }
        }
        // Allow the view controller to be deallocated
        [previousRootViewController dismissViewControllerAnimated:NO completion:^{
            // Remove the root view in case its still showing
            [previousRootViewController.view removeFromSuperview];
        }];
        

        我希望这也能帮助你解决你的问题,这绝对是个屁!

        I hope this helps you fix your problem too, it's an absolute pain in the arse!

        Swift 3.0

        (查看其他 Swift 版本的编辑历史)

        (See edit history for other Swift versions)

        为了更好地实现作为 UIWindow 的扩展,允许传入一个可选的转换.

        For a nicer implementation as a extension on UIWindow allowing an optional transition to be passed in.

        extension UIWindow {
        
            /// Fix for http://stackoverflow.com/a/27153956/849645
            func set(rootViewController newRootViewController: UIViewController, withTransition transition: CATransition? = nil) {
        
                let previousViewController = rootViewController
        
                if let transition = transition {
                    // Add the transition
                    layer.add(transition, forKey: kCATransition)
                }
        
                rootViewController = newRootViewController
        
                // Update status bar appearance using the new view controllers appearance - animate if needed
                if UIView.areAnimationsEnabled {
                    UIView.animate(withDuration: CATransaction.animationDuration()) {
                        newRootViewController.setNeedsStatusBarAppearanceUpdate()
                    }
                } else {
                    newRootViewController.setNeedsStatusBarAppearanceUpdate()
                }
        
                if #available(iOS 13.0, *) {
                    // In iOS 13 we don't want to remove the transition view as it'll create a blank screen
                } else {
                    // The presenting view controllers view doesn't get removed from the window as its currently transistioning and presenting a view controller
                    if let transitionViewClass = NSClassFromString("UITransitionView") {
                        for subview in subviews where subview.isKind(of: transitionViewClass) {
                            subview.removeFromSuperview()
                        }
                    }
                }
                if let previousViewController = previousViewController {
                    // Allow the view controller to be deallocated
                    previousViewController.dismiss(animated: false) {
                        // Remove the root view in case its still showing
                        previousViewController.view.removeFromSuperview()
                    }
                }
            }
        }
        

        用法:

        window.set(rootViewController: viewController)
        

        let transition = CATransition()
        transition.type = kCATransitionFade
        window.set(rootViewController: viewController, withTransition: transition)
        

        这篇关于在transitionWithView中更改rootViewController时泄漏视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆