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

查看:304
本文介绍了在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. c $ c> 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:


  • UIWindow

    • UITransitionView

      • UIView(Y的视图)

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

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

      如果我发送 dismissViewControllerAnimated:NO code> transitionWithView:,结果视图层次结构是:

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


      • UIWindow

        • UIView(X视图)

        • UIView(Z视图)

        如果我向X发送 dismissViewControllerAnimated:(YES或NO),则执行 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

        对于 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()
                }
        
                /// 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天全站免登陆