所以我构建了一个自定义呈现过渡动画,除了视图控制器生命周期方法没有被调用时,一切似乎都很好用。
在呈现控制器之前,我使用UIModalPresentationCustom
样式将呈现VC保持在视图层次结构中,但是一旦我关闭了呈现的VC,我的呈现控制器上就不会调用viewWillAppear和viewDidAppear。我错过了一个我需要明确调用以获取这些方法的步骤吗?我知道手动调用这些方法不是正确的解决方案。
这是我的解雇动画代码。我基本上是动画表单覆盖视图,以便在解雇时缩小到集合视图单元格的大小。
- (void)_animateDismissingTransitionWithContext:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UICollectionView *destinationCollectionView = toCollectionViewController.collectionView;
UICollectionViewCell *destinationCollectionViewCell = [self _destinationCellFromContext:transitionContext];
UIView *containerView = transitionContext.containerView;
// Calculate frames
CGRect startFrame = fromEventDetailViewController.detailContainerView.frame;
CGRect endFrame = [destinationCollectionView convertRect:destinationCollectionViewCell.frame toView:containerView];
// Add overlay
UIView *overlayView = [UIView new];
overlayView.backgroundColor = [UIColor overlayBackground];
overlayView.frame = containerView.bounds;
overlayView.alpha = 1.0f;
[containerView addSubview:overlayView];
// Add fake detail container view
UIView *fakeContainerView = [UIView new];
fakeContainerView.backgroundColor = fromEventDetailViewController.detailContainerView.backgroundColor;
fakeContainerView.frame = startFrame;
[containerView addSubview:fakeContainerView];
// Hide from view controller
fromEventDetailViewController.view.alpha = 0.0f;
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:0.2f options:UIViewAnimationOptionCurveEaseOut animations:^{
fakeContainerView.frame = endFrame;
fakeContainerView.backgroundColor = [UIColor eventCellBackground];
overlayView.alpha = 0.0f;
} completion:^(BOOL finished) {
[fromEventDetailViewController.view removeFromSuperview];
[overlayView removeFromSuperview];
[fakeContainerView removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
答案 0 :(得分:34)
另一种解决方案可能是使用 beginAppearanceTransition:和 endAppearanceTransition:。根据文件:
如果要实现自定义容器控制器,请使用此方法 告诉孩子它的观点即将出现或消失。做 不调用viewWillAppear:,viewWillDisappear:,viewDidAppear:或 viewDidDisappear:直接。
以下是我如何使用它们:
- (void)animationEnded:(BOOL)transitionCompleted
{
if (!transitionCompleted)
{
_toViewController.view.transform = CGAffineTransformIdentity;
}
else
{
[_toViewController endAppearanceTransition];
}
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[toViewController beginAppearanceTransition:YES animated:YES];
// ... other code
}
但我仍然认为自定义模态演示没有做到这一点很奇怪。
答案 1 :(得分:13)
如果您使用UIModalPresentationCustom
,则应提供自定义UIPresentationController
类,如果要使用ViewController生命周期调用者,则需要覆盖shouldRemovePresentersView
并返回YES
。
如果您希望保留演示者并仍然具有ViewControlelr生命周期回调,则可以覆盖私有方法_shouldDisablePresentersAppearanceCallbacks
并在自定义NO
类中返回UIPresentationController
。
答案 2 :(得分:8)
经过多次争论,我发现在ios7和ios8中运行的最佳解决方案是保留modalPresentationStyle = UIModalPresentationFullScreen
而不是文档建议的UIModalPresentationCustom
。
如果我这样做以及将transitioningDelegate
设置为我的委托,它仍然尊重我的过渡和将在'from'视图控制器上调用will / diddisappear方法。另外:没有现在然后旋转然后解雇旋转问题才能启动。
答案 3 :(得分:3)
在显示的VC上创建委托协议。创建一个可以从呈现VC调用的委托方法。在显示叠加层时,将呈现VC设置为委托。然后,从呈现的VC中调用该委托方法。然后,在致电dismissViewController
在你的叠加层(ModalViewController.h)中:
@protocol ModalViewDelegate <NSObject>
-(void)didDismissModalView;
@end
@interface ModalViewController : UIViewController
@property(strong, nonatomic) id <ModalViewDelegate> delegate;
在ModalViewController.m中,调用一个调用委托方法的方法:
- (void)dismissModal{
[self.delegate didDismissModalView];
}
在你的演示VC文件中:( PresentingViewController.h),使这个类符合你的模态委托协议:
@interface PresentingViewController : UIViewController <ModalViewDelegate>
在你的演示VC中,当你提出模态:
...
ModalViewController *modalViewController = [[ModalViewController alloc] init];
modalViewController.delegate = self; //the makes the presenting VC the delegate
[self presentViewController:modalViewController animated:YES completion:nil];
...
最后,在你的演示VC中,当你想在解除模态之前执行一些动作时,实现ModalViewDelegate方法:
- (void)didDismissModalView{
//DO SOME COOL STUFF, SET UP STUFF HERE, UPDATE UI, ETC
//Then dismiss the modal
[self dismissViewControllerAnimated:YES completion:^{
//Do more cool stuff
}];
}
所有这些都适用于您当前的自定义转换代码,但可以让您更好地控制在取消模式/叠加之前发生的事情。代表是一件美好的事。
此外,这将使这个动画代码与其他功能的代码分开,这是一个更清洁的IMO。
答案 4 :(得分:2)
@John Tracids'的anser解决了我的问题。谢谢约翰!
但我想稍微提一下答案。
如果要使用modalPresentationStyle = .custom
(objc UIModalPresentationCustom )呈现 UIViewController 实例以保持调用viewcontroller的生命周期方法,则必须明确管理viewcontroller的外观。要做到这一点,只需在动画前调用 beginAppearanceTransition ,在动画完成块调用 endAppearanceTransition 。
此外,您可以传递给转换动画类自定义UIPresentationController子类,其中重写值shouldRemovePresentersView
返回true
而不调用 beginAppearanceTransition
// Swift 4
将它放到您的自定义UIViewControllerAnimatedTransitioning类中 在动画之前
fromViewController.beginAppearanceTransition(false, animated: true)
toViewController.beginAppearanceTransition(true, animated: true)
UIView.animate(withDuration: animationDuration, animations: {
// animation logic…
}) { finished in
fromViewController.endAppearanceTransition()
toViewController.endAppearanceTransition()
let transitionSuccess = !transitionContext.transitionWasCancelled
transitionContext.completeTransition(transitionSuccess)
}
// UIPresentationController subclass
class PresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool { return true }
}
答案 5 :(得分:0)
调用viewWillAppear
和其他生命周期方法存在相同的问题。
我要解决的问题是实现了委托方法func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?
然后使其生效,我执行以下操作:
class ViewController: UIViewController {
...
func showViewController() {
// load your view controller as you want
guard let detailViewController = loadDetailViewcontroller() as? DetailViewController else {
return }
detailViewController.modalPresentationStyle = .custom
detailViewController.transitioningDelegate = self
present(detailViewController, animated: true, completion: nil)
}
}
extension ViewController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return PresentationController(presentedViewController: presented, presenting: presenting)
}
}
PresentationController类似于演示的临时对象。 苹果的文档
从显示视图控制器到关闭它的整个过程,UIKit使用显示控制器来管理该视图控制器的显示过程的各个方面。演示控制器可以在动画对象提供的动画之上添加自己的动画,可以响应大小变化,还可以管理视图控制器在屏幕上的呈现方式的其他方面。