由于前段时间非常忙,很久没有更新博客了。
今天给大家带来一个从底部弹出的视图控制器容器。
此容器比较适合
- 地址选择器
- 性别选择器
- 各种选择器
- 以及从底部弹出的视图
Example
How usage
简单的留给别人,复杂的留给自己
只需要在需要弹出的控制器中指定代理即可
@implementation BTPresentViewController
- (instancetype)init{
[super init];
if (self) {
_aniamtion = [[BTCoverVerticalTransition alloc]initPresentViewController:self withRragDismissEnabal:YES];
self.transitioningDelegate = _aniamtion;
}
return self;
}
@end
如何呈现?
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
BTPresentViewController * vc = [[BTPresentViewController alloc]init];;
[self presentViewController:vc animated:YES completion:nil];
}
和使用系统的呈现方式一样。
如何实现的
大部分轮子使用的是创建一个view,然后添加到window或者view上,然后加上动画来实现。
这种创建方式一旦创建就会一直留在内存中,不会自动释放,只能随着被添加的控制器一起释放,需要手动进行释放内存,不能做到独立管理自己的生命周期。
我认为这种方式并不是很理想。
我们完全可以根据修改转场动画实现这样的效果。
使用modal的方式呈现,dismiss后也会从内存中释放,生命周期独立管理。
- 自定义转场动画
实现自定义转场动画需要指定转场代理对象,和指定呈现方式为自定义。
/// 指定遵循了UIViewControllerTransitioningDelegate的对象,当然self也可以成为其代理对象。
self.transitioningDelegate = _transitioningDelegate;
/// 指定呈现方式为自定义
self.modalPresentationStyle = UIModalPresentationCustom;
- 转场协议
UIViewControllerTransitioningDelegate
包含了几个方法
/// 视图呈现时的动画方法
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
/// 视图dismiss时的动画方法
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
/// 视图呈现执行的手势,例如我们需要向上拖拽来呈现视图
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
/// 视图关闭执行的手势,例如需要向下拖拽来关闭视图
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;
/// 视图管理控制器
- (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source NS_AVAILABLE_IOS(8_0);
创建一个代理对象,写上初始化方法,遵循并实现协议 UIViewControllerTransitioningDelegate
/// 对象遵循 UIViewControllerTransitioningDelegate 转场协议
@interface BTCoverVerticalTransition : NSObject<UIViewControllerAnimatedTransitioning,UIViewControllerTransitioningDelegate>
/// 初始化方法 默认有拖拽dismiss关闭功能
- (instancetype)initPresentViewController:(UIViewController*)viewController;
/// 初始化方法 可以传入是否关闭dismiss关闭功能
- (instancetype)initPresentViewController:(UIViewController*)viewController withRragDismissEnabal:(BOOL)enabel;
@end
实现
@implementation BTCoverVerticalTransition
- (instancetype)initPresentViewController:(UIViewController*)viewController{
self = [super init];
if (self) {
self.viewController = viewController;
viewController.modalPresentationStyle = UIModalPresentationCustom;
}
return self;
}
- (instancetype)initPresentViewController:(UIViewController*)viewController withRragDismissEnabal:(BOOL)enabel{
self = [super init];
if (self) {
self.viewController = viewController;
viewController.modalPresentationStyle = UIModalPresentationCustom;
if (enabel == YES) {
MKInteractiveTransition * interactive = [[MKInteractiveTransition alloc]initWithViewController:viewController];
self.interactive = interactive;
}
}
return self;
}
#pragma mark - UIViewControllerAnimatedTransitioning
/// 这里返回self ,让自己成为它的实现对象。
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
return self;
}
/// 这里返回self ,让自己成为它的实现对象。
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
return self;
}
/// 返回遵循UIViewControllerInteractiveTransitioning协议手势的对象。
- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator{
return self.interactive.interative ? self.interactive : nil;
}
/// 由于我们并不需要拖拽呈现,所以不用实现这个方法
//- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator{
//}
- (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source NS_AVAILABLE_IOS(8_0){
MKPresentationController * vc = [[MKPresentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
return vc;
}
/// 遵循UIViewControllerAnimatedTransitioning协议
@interface BTCoverVerticalTransition : NSObject<UIViewControllerAnimatedTransitioning,...>
...
@end
然后并实现协议
#pragma mark - UIViewControllerTransitioningDelegate
/// 这里的from to 是怎么回事呢?
/// 当呈现时,呈现的对象视图就是to,
/// 当dismiss时,关闭的对象就是from
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController * to = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController * from = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
if (to.isBeingPresented) {
[self animateTransitionForPresentTransition:transitionContext];
}
if (from.beingDismissed) {
[self animateTransitionForDismiss:transitionContext];
}
}
/// 返回一个执行动画时间
- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
return transitionContext.isAnimated ? 0.3 : 0;
}
#pragma mark -
- (void)animateTransitionForPresentTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext{
UIViewController * to = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[transitionContext.containerView addSubview:to.view];
CGRect finalFrame = [transitionContext finalFrameForViewController:to];
to.view.frame = finalFrame;
to.view.transform = CGAffineTransformMakeTranslation(0, finalFrame.size.height);
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.9 initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
to.view.transform = CGAffineTransformMakeTranslation(0, 0);
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (void)animateTransitionForDismiss:(nonnull id<UIViewControllerContextTransitioning>)transitionContext{
UIViewController *from = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
CGRect finalFrame = [transitionContext finalFrameForViewController:from];
[UIView animateWithDuration:0.25 animations:^{
from.view.transform = CGAffineTransformMakeTranslation(0, finalFrame.size.height);
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
这是上面实现的转场动画的关键部分代码,需要了解更多转场动画
建议fork,添加修改成自己想要的转场动画。