ios - sharedapplication - uiwindow rootviewcontroller animation




transitionWithView内でrootViewControllerを変更するとビューがリークする (4)

メモリリークの調査中に、遷移アニメーションブロック内で setRootViewController: を呼び出す手法に関連する問題を発見しました。

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

古いView Controller(置き換えられるもの)が現在別のView Controllerを提示している場合、上記のコードは提示されたビューをView階層から削除しません。

つまり、この一連の操作...

  1. Xはルートビューコントローラーになります
  2. XはYを表示するため、Yのビューは画面に表示されます
  3. transitionWithView: 使用 transitionWithView: Zを新しいルートビューコントローラーにする

...ユーザーには問題ないように見えますが、Debug View Hierarchyツールは、YのビューがZのビューの背後にある UITransitionView 内にまだあることを明らかにします。 つまり、上記の3つのステップの後、ビュー階層は次のようになります。

  • UIWindow
    • UITransitionView
      • UIView(Yのビュー)
    • UIView(Zのビュー)

トランジションの時点では、Xのビューは実際にはビュー階層の一部ではないため、これは問題だと思われます。

transitionWithView: 直前に dismissViewControllerAnimated:NO をXに送信すると、結果のビュー階層は次のようになります。

  • UIWindow
    • UIView(Xのビュー)
    • UIView(Zのビュー)

dismissViewControllerAnimated: (YESまたはNO)をXに送信し、 completion: ブロックで遷移を実行すると、ビュー階層が正しくなります。 残念ながら、それはアニメーションに干渉します。 解雇をアニメーション化すると、時間を無駄にします。 アニメートしていない場合、壊れているように見えます。

私は他のいくつかのアプローチ(たとえば、ルートビューコントローラとして機能する新しいコンテナビューコントローラクラスを作成する)を試みていますが、機能するものが見つかりませんでした。 この質問は、今後更新します。

最終的な目標は、表示されているビューから新しいルートビューコントローラーに直接移行し、浮遊ビュー階層を残さないことです。


iOs 9.3で動作する簡単なことを試してみます: dismissViewControllerAnimated 完了時に古いviewControllerのビューを階層から削除するだけです。

説明されているように、X、Y、およびZビューで作業しましょう。

つまり、この一連の操作...

  1. Xはルートビューコントローラーになります
  2. XはYを表示するため、Yのビューは画面に表示されます
  3. transitionWithViewの使用:Zを新しいルートビューコントローラーにする

与えるもの:

////
//Start point :

let X = UIViewController ()
let Y = UIViewController ()
let Z = UIViewController ()

window.rootViewController = X
X.presentViewController (Y, animated:true, completion: nil)

////
//Transition :

UIView.transitionWithView(window,
                          duration: 0.25,
                          options: UIViewAnimationOptions.TransitionFlipFromRight,
                          animations: { () -> Void in
                                X.dismissViewControllerAnimated(false, completion: {
                                        X.view.removeFromSuperview()
                                    })
                                window.rootViewController = Z
                           },
                           completion: nil)

私の場合、XとYは適切にdeallocされており、それらのビューは階層構造ではありません!


同様の問題がありました。 私の場合、viewController階層があり、子View Controllerの1つにView Controllerがありました。 Windowsルートビューコントローラーを変更したとき、何らかの理由で、提示されたビューコントローラーがメモリに残っていました。 そのため、Windowsルートビューコントローラーを変更する前に、すべてのビューコントローラーを閉じることで解決しました。


私はこのコードを使用してこの問題に来ました:

if var tc = self.transitionCoordinator() {

    var animation = tc.animateAlongsideTransitionInView((self.navigationController as VDLNavigationController).filtersVCContainerView, animation: { (context:UIViewControllerTransitionCoordinatorContext!) -> Void in
        var toVC = tc.viewControllerForKey(UITransitionContextToViewControllerKey) as BaseViewController
        (self.navigationController as VDLNavigationController).setFilterBarHiddenWithInteractivity(!toVC.filterable(), animated: true, interactive: true)
    }, completion: { (context:UIViewControllerTransitionCoordinatorContext!) -> Void in

    })
}

このコードを無効にして、問題を修正しました。 アニメーション化するフィルターバーが初期化されたときにのみこの遷移アニメーションを有効にすることで、これを機能させることができました。

それは本当にあなたが探している答えではありませんが、それはあなたの解決策を見つけるための正しいパッドにあなたを連れてくることができます。


私はこの問題に直面し、それが一日中私を悩ませました。 @Richのobj-cソリューションを試しましたが、その後に別のviewControllerを表示したい場合は、空のUITransitionViewでブロックされます。

最後に、私はこの方法を理解し、それは私のために働いた。

- (void)setRootViewController:(UIViewController *)rootViewController {
    // dismiss presented view controllers before switch rootViewController to avoid messed up view hierarchy, or even crash
    UIViewController *presentedViewController = [self findPresentedViewControllerStartingFrom:self.window.rootViewController];
    [self dismissPresentedViewController:presentedViewController completionBlock:^{
        [self.window setRootViewController:rootViewController];
    }];
}

- (void)dismissPresentedViewController:(UIViewController *)vc completionBlock:(void(^)())completionBlock {
    // if vc is presented by other view controller, dismiss it.
    if ([vc presentingViewController]) {
        __block UIViewController* nextVC = vc.presentingViewController;
        [vc dismissViewControllerAnimated:NO completion:^ {
            // if the view controller which is presenting vc is also presented by other view controller, dismiss it
            if ([nextVC presentingViewController]) {
                [self dismissPresentedViewController:nextVC completionBlock:completionBlock];
            } else {
                if (completionBlock != nil) {
                    completionBlock();
                }
            }
        }];
    } else {
        if (completionBlock != nil) {
            completionBlock();
        }
    }
}

+ (UIViewController *)findPresentedViewControllerStartingFrom:(UIViewController *)start {
    if ([start isKindOfClass:[UINavigationController class]]) {
        return [self findPresentedViewControllerStartingFrom:[(UINavigationController *)start topViewController]];
    }

    if ([start isKindOfClass:[UITabBarController class]]) {
        return [self findPresentedViewControllerStartingFrom:[(UITabBarController *)start selectedViewController]];
    }

    if (start.presentedViewController == nil || start.presentedViewController.isBeingDismissed) {
        return start;
    }

    return [self findPresentedViewControllerStartingFrom:start.presentedViewController];
}

さて、あなたがしなければならないことは、 [self setRootViewController:newViewController]; ルートビューコントローラを切り替える場合。





core-animation