iOS 底部弹出视图正确姿势

由于前段时间非常忙,很久没有更新博客了。
今天给大家带来一个从底部弹出的视图控制器容器。
此容器比较适合

  • 地址选择器
  • 性别选择器
  • 各种选择器
  • 以及从底部弹出的视图

Example

iOS 底部弹出视图正确姿势_第1张图片
例子

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 *)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 )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
/// 视图dismiss时的动画方法
- (nullable id )animationControllerForDismissedController:(UIViewController *)dismissed;
/// 视图呈现执行的手势,例如我们需要向上拖拽来呈现视图
- (nullable id )interactionControllerForPresentation:(id )animator;
/// 视图关闭执行的手势,例如需要向下拖拽来关闭视图
- (nullable id )interactionControllerForDismissal:(id )animator;
/// 视图管理控制器
- (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source NS_AVAILABLE_IOS(8_0);

创建一个代理对象,写上初始化方法,遵循并实现协议 UIViewControllerTransitioningDelegate

/// 对象遵循 UIViewControllerTransitioningDelegate 转场协议
@interface BTCoverVerticalTransition : NSObject
/// 初始化方法 默认有拖拽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)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
    return self;
}
/// 这里返回self ,让自己成为它的实现对象。
- (id)animationControllerForDismissedController:(UIViewController *)dismissed{
    return self;
}

/// 返回遵循UIViewControllerInteractiveTransitioning协议手势的对象。
- (id)interactionControllerForDismissal:(id)animator{
    return self.interactive.interative ? self.interactive : nil;
}

/// 由于我们并不需要拖拽呈现,所以不用实现这个方法
//- (nullable id )interactionControllerForPresentation:(id )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
...
@end

然后并实现协议


#pragma mark - UIViewControllerTransitioningDelegate
/// 这里的from to 是怎么回事呢?
/// 当呈现时,呈现的对象视图就是to,
/// 当dismiss时,关闭的对象就是from
- (void)animateTransition:(nonnull id)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)transitionContext {
    return transitionContext.isAnimated ? 0.3 : 0;
}

#pragma mark - 
- (void)animateTransitionForPresentTransition:(nonnull id)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)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,添加修改成自己想要的转场动画。

over

你可能感兴趣的:(iOS 底部弹出视图正确姿势)