使用一个动画关闭和呈现模态视图控制器
Dismiss and present modal view controller with one animation
问题是我不知道如何关闭和显示只有一个过渡动画的视图控制器。
我的故事板结构是:
我们可以说 A 控制器跟在 NavigationController 之后,B 是启动引用,C 是TabBar ViewController。 B 和 C 都以模态呈现 Cross Dissolve 过渡。
当用户登录到应用程序时(从 B),C 控制器会以模态方式显示 Flip水平 过渡。当用户注销时(从 C),B 以相同的方式呈现。
在 A 控制器上,我根据用户是否已登录,直接转至 B 或 C .
我的问题是,如果我不从 B 或 C 中关闭以前的视图控制器,该控制器就会泄露。相反,如果我关闭它,A 会显示在目标控制器之前(B 或 C)已呈现。
是否可以仅显示 水平翻转 过渡并跳过 A 视图?
这是我用于解决此问题的解决方案。我不知道它如何与故事板集成,因为我不使用它们。
将此方法添加到 UIViewController 的类别中,然后可以在之前调用的任何地方调用 presentViewController:animated:completion
。产生新控制器的无缝动画,同时仍然关闭前一个控制器。
-(void)presentViewControllerDismissingPrevious:(UIViewController* _Nonnull)controller animated:(BOOL)animated completion:(void (^ __nullable)(void))completion {
UIViewController* visibleController = self;
{
UIViewController* temp;
while( ( temp = visibleController.presentedViewController ) != nil ) {
visibleController = temp;
}
}
if( visibleController == self ) {
// no previous controller to dismiss
[self presentViewController:controller animated:animated completion:completion];
} else {
// create a temporary snapshot of the visible controller's entire window
// and add to the current view controller's window until animation completed
UIWindow* visibleWindow = visibleController.view.window;
UIView* tempView = [visibleWindow snapshotViewAfterScreenUpdates:NO];
UIView* rootView = self.view.window.subviews[0];
tempView.frame = [rootView convertRect:visibleWindow.bounds fromView:visibleWindow];
[rootView addSubview:tempView];
[self dismissViewControllerAnimated:NO completion:^(){
[self presentViewController:controller animated:animated completion:^(){
[tempView removeFromSuperview];
if( completion ) {
completion();
}
}];
}];
}
}
我对这个问题的解决方案是替换当前的 rootViewController,支持不同的转换:
static func replaceRootViewController(with viewController: UIViewController, transition: UIViewAnimationOptions, completion: (() -> ())? = nil) {
if transition == .transitionCrossDissolve {
let overlayView = UIScreen.main.snapshotView(afterScreenUpdates: false)
viewController.view.addSubview(overlayView)
UIApplication.shared.keyWindow?.rootViewController = viewController
UIView.animate(withDuration: 0.65, delay: 0, options: transition, animations: {
overlayView.alpha = 0
}, completion: { finished in
overlayView.removeFromSuperview()
if let completion = completion{
completion()
}
})
} else {
_ = viewController.view
UIView.transition(with: UIApplication.shared.keyWindow!, duration: 0.65,options: transition, animations: {
UIApplication.shared.keyWindow?.rootViewController = viewController
}){_ in
if let completion = completion {
completion()
}
}
}
}
两个答案都非常有帮助,这里是 Swift 4 版本:
func presentHidingBehindScreenSnapshot(viewController: UIViewController,
completion: (() -> (Void))? ) {
if let screenSnapshot = UIApplication.shared.keyWindow?.snapshotView(afterScreenUpdates: false),
let rootViewController = UIApplication.shared.keyWindow?.rootViewController {
rootViewController.view.addSubview(screenSnapshot)
rootViewController.view.bringSubview(toFront: screenSnapshot)
rootViewController.dismiss(animated: false, completion: {
rootViewController.present(viewController, animated: false, completion: {
screenSnapshot.removeFromSuperview()
if let existingCompletion = completion {
existingCompletion()
}
})
})
} else {
#if DEBUG
fatalError("Can't hide behind snapshot while presenting other view controller")
#endif
}
}
问题是我不知道如何关闭和显示只有一个过渡动画的视图控制器。
我的故事板结构是:
当用户登录到应用程序时(从 B),C 控制器会以模态方式显示 Flip水平 过渡。当用户注销时(从 C),B 以相同的方式呈现。 在 A 控制器上,我根据用户是否已登录,直接转至 B 或 C .
我的问题是,如果我不从 B 或 C 中关闭以前的视图控制器,该控制器就会泄露。相反,如果我关闭它,A 会显示在目标控制器之前(B 或 C)已呈现。
是否可以仅显示 水平翻转 过渡并跳过 A 视图?
这是我用于解决此问题的解决方案。我不知道它如何与故事板集成,因为我不使用它们。
将此方法添加到 UIViewController 的类别中,然后可以在之前调用的任何地方调用 presentViewController:animated:completion
。产生新控制器的无缝动画,同时仍然关闭前一个控制器。
-(void)presentViewControllerDismissingPrevious:(UIViewController* _Nonnull)controller animated:(BOOL)animated completion:(void (^ __nullable)(void))completion {
UIViewController* visibleController = self;
{
UIViewController* temp;
while( ( temp = visibleController.presentedViewController ) != nil ) {
visibleController = temp;
}
}
if( visibleController == self ) {
// no previous controller to dismiss
[self presentViewController:controller animated:animated completion:completion];
} else {
// create a temporary snapshot of the visible controller's entire window
// and add to the current view controller's window until animation completed
UIWindow* visibleWindow = visibleController.view.window;
UIView* tempView = [visibleWindow snapshotViewAfterScreenUpdates:NO];
UIView* rootView = self.view.window.subviews[0];
tempView.frame = [rootView convertRect:visibleWindow.bounds fromView:visibleWindow];
[rootView addSubview:tempView];
[self dismissViewControllerAnimated:NO completion:^(){
[self presentViewController:controller animated:animated completion:^(){
[tempView removeFromSuperview];
if( completion ) {
completion();
}
}];
}];
}
}
我对这个问题的解决方案是替换当前的 rootViewController,支持不同的转换:
static func replaceRootViewController(with viewController: UIViewController, transition: UIViewAnimationOptions, completion: (() -> ())? = nil) {
if transition == .transitionCrossDissolve {
let overlayView = UIScreen.main.snapshotView(afterScreenUpdates: false)
viewController.view.addSubview(overlayView)
UIApplication.shared.keyWindow?.rootViewController = viewController
UIView.animate(withDuration: 0.65, delay: 0, options: transition, animations: {
overlayView.alpha = 0
}, completion: { finished in
overlayView.removeFromSuperview()
if let completion = completion{
completion()
}
})
} else {
_ = viewController.view
UIView.transition(with: UIApplication.shared.keyWindow!, duration: 0.65,options: transition, animations: {
UIApplication.shared.keyWindow?.rootViewController = viewController
}){_ in
if let completion = completion {
completion()
}
}
}
}
两个答案都非常有帮助,这里是 Swift 4 版本:
func presentHidingBehindScreenSnapshot(viewController: UIViewController,
completion: (() -> (Void))? ) {
if let screenSnapshot = UIApplication.shared.keyWindow?.snapshotView(afterScreenUpdates: false),
let rootViewController = UIApplication.shared.keyWindow?.rootViewController {
rootViewController.view.addSubview(screenSnapshot)
rootViewController.view.bringSubview(toFront: screenSnapshot)
rootViewController.dismiss(animated: false, completion: {
rootViewController.present(viewController, animated: false, completion: {
screenSnapshot.removeFromSuperview()
if let existingCompletion = completion {
existingCompletion()
}
})
})
} else {
#if DEBUG
fatalError("Can't hide behind snapshot while presenting other view controller")
#endif
}
}