ben*_*ado 94 cocoa-touch core-animation uiviewcontroller ios
在调查内存泄漏时,我发现了一个与setRootViewController:
在转换动画块中调用技术相关的问题:
[UIView transitionWithView:self.window
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{ self.window.rootViewController = newController; }
completion:nil];
Run Code Online (Sandbox Code Playgroud)
如果旧视图控制器(正在替换的视图控制器)当前正在呈现另一个视图控制器,则上述代码不会从视图层次结构中删除呈现的视图.
也就是说,这一系列操作......
transitionWithView:
使Z中的新根视图控制器...对用户看起来没问题,但Debug View Hierarchy工具将显示Y的视图仍然在Z的视图后面,在a UITransitionView
.也就是说,在上面的三个步骤之后,视图层次结构是:
我怀疑这是一个问题,因为在转换时,X的视图实际上不是视图层次结构的一部分.
如果我dismissViewControllerAnimated:NO
之前发送到X transitionWithView:
,则生成的视图层次结构为:
如果我向dismissViewControllerAnimated:
X 发送(是或否),然后在completion:
块中执行转换,则视图层次结构正确.不幸的是,这会干扰动画.如果动画解雇,那就浪费时间; 如果没有动画,它看起来很破碎.
我正在尝试其他一些方法(例如,创建一个新的容器视图控制器类作为我的根视图控制器),但没有找到任何有效的方法.我会去的时候更新这个问题.
最终目标是直接从呈现视图转换到新的根视图控制器,而不会留下杂散视图层次结构.
Ric*_*ich 115
我最近遇到过类似的问题.我不得不手动UITransitionView
从窗口中删除它来解决问题,然后在前一个根视图控制器上调用dismiss来确保它被解除分配.
解决方案并不是很好,但除非你发布问题后找到了更好的方法,否则它是我发现的唯一工作方式!viewController
只是newController
你原来的问题.
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];
}];
Run Code Online (Sandbox Code Playgroud)
我希望这也有助于你解决你的问题,这是一个绝对痛苦的屁股!
Swift 3.0
(请参阅其他Swift版本的编辑历史记录)
对于更好的实现,作为UIWindow
允许传递可选转换的扩展.
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()
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
window.set(rootViewController: viewController)
Run Code Online (Sandbox Code Playgroud)
要么
let transition = CATransition()
transition.type = kCATransitionFade
window.set(rootViewController: viewController, withTransition: transition)
Run Code Online (Sandbox Code Playgroud)
我遇到了这个问题,它让我感到烦恼了一整天.我已经尝试过@ 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];
}
Run Code Online (Sandbox Code Playgroud)
好吧,现在你要做的就是[self setRootViewController:newViewController];
当你想要切换根视图控制器时调用.
我尝试在iOs 9.3上为我工作的简单方法:只需在dismissViewControllerAnimated
完成过程中从其层次结构中删除旧的viewController视图。
让我们按照benzado的说明来研究X,Y和Z视图:
也就是说,此操作顺序...
- X成为Root View Controller
- X代表Y,因此Y的视图在屏幕上
- 使用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)
Run Code Online (Sandbox Code Playgroud)
在我的情况下,X和Y很好地解除了分配,并且它们的视图不再处于层次结构中!