#iOS开发#仿Snapchat主页面切换视图

因为最近用了一下Snapchat。发现他的主页的上下左右滑动都可以切换进入一个新的视图。觉得挺好玩的。所以就决定也照着写一个。ʕʘ̅͜ʘ̅ʔ
想了很多解决方法都还是不能很好的将这个效果做出来。于是借鉴了网上的一些Demo。很僵硬。照抄根本没办法把这个代码学习进去。(๑‾᷆д‾᷇๑)
所以想了想还是一边学习一边写一篇文章做个记录。这样学习的过程也可能会清晰一点。

效果如下

未命名.gif

好了。我们进入正题。

1. 那我们先看一下这个项目结构。

WX20170319-181717@2x.png

2. 我们看一下Storyboard吧。

WX20170319-200906@2x.png

这个Storyboard中用到了我之前从没有用过的一个控件。
ContainerViewController

Container view controllers are a way to combine the content from multiple 
view controllers into a single user interface. Container view controllers 
are most often used to facilitate navigation and to create new user interface types 
based on existing content.
Examples of container view controllers in UIKit include UINavigationController, 
UITabBarController, and UISplitViewController,  all of which facilitate 
navigation between different parts of your user interface.

上面那段话截取苹果的开发者平台。大致的意思就是ContainerViewController可以在一个容器视图控制器中添加多个视图控制器。这里有一篇文章对这个控件分析的挺好的。大家可以看看。

ContainerViewCotroller中的视图结构是这样的。它包含两个ContainerView对应的就是另外两个VC的view。这边还用到了Segue。并且给了Identifier。那么说明等会儿会对Segue做处理。

WX20170319-203130@2x.png

3. 轮到看代码了。

我们先看最简单的ContainerChildViewController
定义了三个虚函数。因为都没有实现三个方法所以我们也没什么好看了。这三个方法具体是干什么的通过方法名我大致猜了一下可能是做这些事的。

/// 更新交互式过渡
- (void)updateInteractiveTransition:(CGFloat)progress;
/// 取消交互式过渡
- (void)cancelInteractiveTransition;
/// 完成交互式过渡
- (void)finishInteractiveTransition;

然后看看重量级的ContainerViewController

ContainerViewController.h

#import <UIKit/UIKit.h>

@interface ContainerViewController : UIViewController

/// 过渡动画时长
@property(nonatomic, assign) CGFloat transitionAnimationDuration;
/// 上层视图是否可见(只读)
@property(nonatomic, assign, getter=isOverViewVisible, readonly) BOOL overViewVisible;
/// 交互进度
@property(nonatomic, assign, getter=isInteractionInProgress, readonly) BOOL interactionInProgress;

/// 上层视图消失
- (void)dismissOverViewController;
/// 上层视图显示
- (void)presentOverViewController;

@end

ContainerViewController.m

先看下实现文件里声明的一下属性。

/// 目前还不知道这个常量有什么用
static CGFloat const kActionButtonDisplacement = 55.0;
/// 按钮的最小尺寸
static CGFloat const kActionButtonSmallSize = 50.0;

@interface ContainerViewController()

/// 上层视图的视图控制器
@property(nonatomic, strong) ContainerChildViewController *overViewController;
/// 主视图的视图控制器
@property(nonatomic, strong) ContainerChildViewController *mainViewController;

/// 上层视图是否可见(读写)
@property(nonatomic, assign, getter=isOverViewVisible, readwrite) BOOL overViewVisible;
/// 交互进度(读写)
@property(nonatomic, assign, getter=isInteractionInProgress, readwrite) BOOL interactionInProgress;
/// 是否完成过度
@property(nonatomic, assign) BOOL shouldCompleteTransition;

/// 上层视图的视图容器
@property(nonatomic, weak) IBOutlet UIView *overViewContainer;
/// 主视图的视图容器
@property(nonatomic, weak) IBOutlet UIView *mainViewContainer;
/// 中间的圆圈按钮
@property(nonatomic, weak) IBOutlet UIButton *actionButton;
/// 上层视图容器距离顶部的约束(初始化为负的屏幕高度)
@property(nonatomic, weak) IBOutlet NSLayoutConstraint *overViewTopConstraint;
/// 中间的圆圈按钮距离底部的约束(初始化为50)
@property(nonatomic, weak) IBOutlet NSLayoutConstraint *actionButtonBottomConstraint;
/// 中间的圆圈按钮的宽度约束(初始化为50)
@property(nonatomic, weak) IBOutlet NSLayoutConstraint *actionButtonWidthConstraint;

/// 初始化的中间的圆圈按钮距离底部的约束(初始化为50)
@property(nonatomic, assign) CGFloat originalActionButtonBottomConstraintConstant;
/// 初始化中间的圆圈按钮的宽度约束(初始化为50)
@property(nonatomic, assign) CGFloat originalActionButtonWidthConstraintConstant;
/// 上层视图容器距离顶部的预估值
@property(nonatomic, assign) CGFloat overViewTopEstimatedValue;

@end

好了。接下来我们一个方法一个方法去看。这样方便我们学习。

- (void)awakeFromNib
初始化overViewVisibleoverViewTopEstimatedValue

- (void)awakeFromNib
{
    [super awakeFromNib];
    self.overViewVisible = NO;
    /// 也可以通过 self.overViewTopEstimatedValue = self.overViewTopConstraint.constant; 获取
    self.overViewTopEstimatedValue = -[UIScreen mainScreen].bounds.size.height;
}

- (void)viewDidLoad
为视图添加拖拽手势并且初始化
overViewTopConstraintoriginalActionButtonBottomConstraintConstant
originalActionButtonWidthConstraintConstant

- (void)viewDidLoad
{
    [super viewDidLoad];
    /// 添加拖拽手势
    [self addGestureRecogniserOnView:self.view];
    
    self.overViewTopConstraint.constant =
    -[UIScreen mainScreen].bounds.size.height;
    
    self.originalActionButtonBottomConstraintConstant =
    self.actionButtonBottomConstraint.constant;
    
    self.originalActionButtonWidthConstraintConstant =
    self.actionButtonWidthConstraint.constant;
}

- (BOOL)prefersStatusBarHidden
设置隐藏状态栏。因为这个容器视图控制器中有另外两个子视图控制器。所以隐藏状态栏写在这里。下面会有一个方法当拖拽时回去调用setNeedsStatusBarAppearanceUpdate。所以系统会调用prefersStatusBarHidden重新获取是否隐藏状态栏。

- (BOOL)prefersStatusBarHidden
{
    /// 设置状态栏显示范围
    static CGFloat const kVisibleStatusBarRange = -10.0;
    /// 当上层视图控制器距离上边距只有10的时候状态栏隐藏
    return (self.overViewTopEstimatedValue < kVisibleStatusBarRange);
}

- (void)prepareForSegue:(UIStoryboardSegue)segue sender:(id)sender*
通知视图控制器即将要跳转。segue中包含了跳转的信息。

- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
    /// 通过segue.identifier去判断跳转至那个VC
    if ([segue.identifier isEqualToString:@"embedOver"])
    {
        ContainerChildViewController *viewController = segue.destinationViewController;
        viewController.containerViewController = self;
        self.overViewController = segue.destinationViewController;
    }
    else if ([segue.identifier isEqualToString:@"embedMain"])
    {
        ContainerChildViewController *viewController = segue.destinationViewController;
        viewController.containerViewController = self;
        self.mainViewController = segue.destinationViewController;
    }
}

- (IBAction)actionButtonTouched:(id)sender
按钮的点击事件。

- (IBAction)actionButtonTouched:(id)sender
{
    /// 判断上层视图是否显示
    if (self.isOverViewVisible)
    {
        /// 如果显示上层视图。当点击按钮之后dismiss上层视图
        [self dismissOverViewController];
    }
}

- (void)transformContentWithProgress:(CGFloat)progress
通过交互的进度使内容变形。

- (void)transformContentWithProgress:(CGFloat)progress
{
    /// 使上层视图变形
    /// Y轴上的移动量
    CGFloat translationY = progress * self.view.frame.size.height;
    /// CGAffineTransformMakeTranslation每次都会相对于初始的中心点做变化
    self.overViewContainer.transform = CGAffineTransformMakeTranslation(0, translationY);
#warning 这里为什么要赋值给overViewTopEstimatedValue???
    /// 将上层视图的位置赋值给overViewTopEstimatedValue
    self.overViewTopEstimatedValue = self.overViewTopConstraint.constant + translationY;
    
    /// --------------------------------------------------------------------------------- ///
    /// 按钮的形变
    
    /// 上层视图是否显示。如果显示则为progress的绝对值。如果不显示则为1 - progress
    /// 当上层视图显示并且从下向上滑时。progress的值从0到-1
    NSLog(@"%lf", progress);
    CGFloat value =
    (self.isOverViewVisible ?
     fabs(progress) :
     1.0 - progress);
    
    /// 按钮的新大小。最大为80。最小为50。
    CGFloat newActionButtonSize =
    kActionButtonSmallSize + value *
    (self.originalActionButtonWidthConstraintConstant - kActionButtonSmallSize);
    
    /// 这里的 self.actionButtonWidthConstraint.constant 并不是常量。当过渡完成时改变
    CGFloat actionButtonScale = newActionButtonSize / self.actionButtonWidthConstraint.constant;
    
    CGAffineTransform actionButtonScaleTransform = CGAffineTransformMakeScale(actionButtonScale, actionButtonScale);

    /// --------------------------------------------------------------------------------- ///
    /// 按钮的偏移
    
    /// 因为我们同时让按钮偏移并且缩放。所以我们要将两个形变拼起来
#warning 这边为什么除以2.0???
    CGFloat compensationBecauseOfMakingScale = (self.actionButtonWidthConstraint.constant - newActionButtonSize) / 2.0;
    
    CGFloat actionButtonTranslationY =
    (progress * kActionButtonDisplacement) +
    compensationBecauseOfMakingScale;
   
    CGAffineTransform actionButtonTranslateTransform =
    CGAffineTransformMakeTranslation(0, actionButtonTranslationY);
    
    CGAffineTransform actionButtonTransform =
    CGAffineTransformConcat(actionButtonScaleTransform, actionButtonTranslateTransform);
    
    self.actionButton.transform = actionButtonTransform;
}

- (CGFloat)transitionAnimationDuration
过渡动画的时间。

- (CGFloat)transitionAnimationDuration
{
    return 0.25;
}

- (void)dismissOverViewController
上层视图dismiss。

- (void)dismissOverViewController
{
    [self finishInteractiveTransition];
}

- (void)cancelInteractiveTransition
取消交互式过渡。

- (void)cancelInteractiveTransition
{
    /// 重新获取上层视图相对顶部的距离。
    self.overViewTopEstimatedValue = self.overViewTopConstraint.constant;
    
    /// 通知主视图控制器与上层视图控制器 取消交互式过渡。
    [self.mainViewController cancelInteractiveTransition];
    [self.overViewController cancelInteractiveTransition];
    
    /// 对弱引用self。避免循环引用。
    __weak typeof(self) blockSelf = self;
    
    void (^AnimationBlock)(void) = ^void (void)
    {
        /// 将形变还原。
        blockSelf.overViewContainer.transform = CGAffineTransformIdentity;
        blockSelf.actionButton.transform = CGAffineTransformIdentity;
    };

    void (^CompletionBlock)(BOOL finished) = ^void (BOOL finished)
    {
        /// 完成后通知系统重新获取状态栏是否隐藏。
        [blockSelf setNeedsStatusBarAppearanceUpdate];
    };
    
    /// 执行动画。
    [UIView animateWithDuration:[self transitionAnimationDuration]
                          delay:0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:AnimationBlock
                     completion:CompletionBlock];
}

- (void)finishInteractiveTransition
完成交互式过渡。

- (void)finishInteractiveTransition
{
    /// 通知主视图控制器与上层视图控制器完成交互式过渡
    [self.mainViewController finishInteractiveTransition];
    [self.overViewController finishInteractiveTransition];
    
    /// 判断当前进度。如果上层视图显示则为 -1,否则为 1。
    CGFloat progress = (self.isOverViewVisible ? - 1 : 1);
    
    /// 重新获取上层视图相对顶部的距离。
    CGFloat newOverViewTopConstraintConstant =
    (self.isOverViewVisible ?
     -[UIScreen mainScreen].bounds.size.height :
     0);
    
    /// 重新获取按钮相对于底部的距离
    CGFloat newActionButtonBottomConstraintConstant =
    (self.isOverViewVisible ?
     self.originalActionButtonBottomConstraintConstant :
     self.originalActionButtonBottomConstraintConstant -
     kActionButtonDisplacement);
    
    /// 重新获取按钮的宽度
    CGFloat newActionButtonWidthConstraintConstant =
    (self.isOverViewVisible ?
     self.originalActionButtonWidthConstraintConstant :
     kActionButtonSmallSize);
    
    __weak typeof(self) blockSelf = self;
    
    void (^AnimationBlock)(void) = ^void (void)
    {
        /// 通过进度发生形变
        [blockSelf transformContentWithProgress:progress];
    };
    
    void (^CompletionBlock)(BOOL finished) = ^void (BOOL finished)
    {
        /// 赋值新的约束
        blockSelf.overViewTopConstraint.constant =
        newOverViewTopConstraintConstant;
        
        blockSelf.actionButtonBottomConstraint.constant =
        newActionButtonBottomConstraintConstant;
        
        blockSelf.actionButtonWidthConstraint.constant =
        newActionButtonWidthConstraintConstant;
        
        /// 获取新的上层视图相对顶部的约束
        blockSelf.overViewTopEstimatedValue = newOverViewTopConstraintConstant;
        
#warning 这里为什么要将形变还原???
        /// 还原形变
        blockSelf.overViewContainer.transform = CGAffineTransformIdentity;
        blockSelf.actionButton.transform = CGAffineTransformIdentity;
        
        /// 刷新页面
        [blockSelf.view layoutIfNeeded];
        
        blockSelf.overViewVisible = !blockSelf.isOverViewVisible;
        
        [blockSelf setNeedsStatusBarAppearanceUpdate];
    };
    
    [UIView animateWithDuration:[self transitionAnimationDuration]
                          delay:0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:AnimationBlock
                     completion:CompletionBlock];
}

- (void)addGestureRecogniserOnView:(UIView)view*
给View添加手势。

- (void)addGestureRecogniserOnView:(UIView*)view
{
    UIPanGestureRecognizer *panGesture =
    [[UIPanGestureRecognizer alloc] initWithTarget:self
                                            action:@selector(handleGestureRecognizer:)];
    
    [view addGestureRecognizer:panGesture];
}

- (void)handleGestureRecognizer:(UIPanGestureRecognizer)gesture*
手势触发的事件。

- (void)handleGestureRecognizer:(UIPanGestureRecognizer*)gesture
{
    /// 获取手势位移(位置的偏移量,所有点都相对于动作起点的距离)。
    CGPoint translation = [gesture translationInView:self.view];
    /// 手势移动的速度。
    CGPoint velocity = [gesture velocityInView:self.view];
    
    /// 获取progress的值。
    CGFloat progress = translation.y / self.view.frame.size.height;
    /// 避免过度向上滑或者过度向下滑。
    progress =
    (self.isOverViewVisible ?
     fmin(0.0, fmax(-1.0, progress)) :
     fmin(1.0, fmax(0.0, progress)));
    //    NSLog(@"%f", progress);
    //    NSLog(@"%f", translation.y);
    //    NSLog(@"%f", velocity.y);
    
    /// 速度的上限。
    static CGFloat const kVelocityLimit = 2000.0;
   /// 滑动距离的上限。
    static CGFloat const kTranslationLimit = 0.30;
    
    /// 通过判断手势的不同状态来做不同事情。
    switch (gesture.state)
    {
            /// 刚开始拖拽时。
        case UIGestureRecognizerStateBegan:
            
            self.shouldCompleteTransition = NO;
            self.interactionInProgress = YES;
            
            break;
            /// 开始拖拽。
        case UIGestureRecognizerStateChanged:
            
            /// 判断是否在交互中。
            if (self.isInteractionInProgress)
            {
                /// 如果滑动速度太快。直接完成动画。
                if ((self.isOverViewVisible && velocity.y < -kVelocityLimit) ||
                    (!self.isOverViewVisible && velocity.y > kVelocityLimit))
                {
                    /// 完成过渡。
                    self.interactionInProgress = NO;
                    [self finishInteractiveTransition];
                }
                else
                {
                    /// 当手势完成的时候动画也会完成。
                    self.shouldCompleteTransition =
                    (self.isOverViewVisible ?
                     progress < -kTranslationLimit:
                     progress > kTranslationLimit);
                    
                    [self updateInteractiveTransition:progress];
                }
            }
            
            break;
        /// 手势识别失败或者取消的时候。
        case UIGestureRecognizerStateFailed:
        case UIGestureRecognizerStateCancelled:
            
            /// 如果在交互过程中的就直接取消。
            if (self.isInteractionInProgress)
            {
                self.interactionInProgress = NO;
                [self cancelInteractiveTransition];
            }
            
            break;
        /// 手势结束之后。
        case UIGestureRecognizerStateEnded:
            
            /// 判断是否在交互中。
            if (self.isInteractionInProgress)
            {
                self.interactionInProgress = NO;
                
                /// 判断是否应该完成移动。
                if (self.shouldCompleteTransition)
                {
                    /// 如果是。完成交互。
                    [self finishInteractiveTransition];
                }
                else
                {
                    /// 如果不是。取消交互。
                    [self cancelInteractiveTransition];
                }
            }
            
            break;
            
        case UIGestureRecognizerStatePossible:
            // Do nothing
            break;
    }
}

即使这样读完了代码但是还是对这个类理解还不够通透。所以我决定再看一遍。并且重点看看一下疑问的地方。

我想先去搞清楚关于Transition这块的几个方法到底是做了什么事。
过了两天。终于搞清楚了这个类到底做了什么事情。自己的水平不够。加上又是阅读别人的代码真的是好累阿。不过好在学到了东西。
多说不说。先看看实现的效果。

Demo.gif

在原来的代码上做了点修改。实现了四个方向的滑动。虽然没有上面的酷炫但是这个原理是差不多了。
那我们继续看代码吧。这回就直接看核心部分吧。

第一个核心的部分。

首先应该是处理手势的那部分。
通过三个方法去处理动画:
分别是:
1. - (void)updateInteractiveTransition:(CGFloat)progress
2. - (void)cancelInteractiveTransition
3. - (void)finishInteractiveTransition
这三个方法在处理手势的时候都有用到。我们通过手势的不同状态与手势的移动距离去判断分别调用什么方法。
我们先来看看触发手势时候发生了什么。
*- (void)handleGestureRecognizer:(UIPanGestureRecognizer )gesture

- (void)handleGestureRecognizer:(UIPanGestureRecognizer*)gesture
{

    /// 速度的上限。
    static CGFloat const kVelocityLimit = 2000.0;
   /// 滑动距离的上限。
    static CGFloat const kTranslationLimit = 0.30;

    /// 获取手势位移(位置的偏移量,所有点都相对于动作起点的距离)。
    CGPoint translation = [gesture translationInView:self.view];
    /// 手势移动的速度。
    CGPoint velocity = [gesture velocityInView:self.view];
    
    /// 获取progress的值。
    CGFloat progress = translation.y / self.view.frame.size.height;

    /// 避免过度向上滑或者过度向下滑。
    /// 先判断当前所在页面。
    /// 如果是在上层视图。那么就是只能上滑。向上滑动 translation.y 为负数。所以取值范围是 -1 到 0。
    /// 如果是在主视图。道理同上。
    progress =
    (self.isOverViewVisible ?
     fmin(0.0, fmax(-1.0, progress)) :
     fmin(1.0, fmax(0.0, progress)));
    
    /// 通过判断手势的不同状态来做不同事情。
    switch (gesture.state)
    {
            /// 刚开始拖拽时。
        case UIGestureRecognizerStateBegan:
            
            self.shouldCompleteTransition = NO;
            self.interactionInProgress = YES;
            
            break;
            /// 开始拖拽。
        case UIGestureRecognizerStateChanged:
            
            /// 判断是否在交互中。
            if (self.isInteractionInProgress)
            {
                /// 判断手势的速度。
                /// 如果滑动速度太快。直接完成动画。
                /// 不能用绝对值去判断速度。
                /// 如果用绝对值判断会导致在主页面。快速向上滑动。也会触发以下代码。违反交互。
                if ((self.isOverViewVisible && velocity.y < -kVelocityLimit) ||
                    (!self.isOverViewVisible && velocity.y > kVelocityLimit))
                {
                    /// 完成过渡。
                    self.interactionInProgress = NO;
                    [self finishInteractiveTransition];
                }
                else
                {
                    /// 当手势完成的时候动画也会完成。
                    /// 通过已经移动过的比例去判断是否需要完成动画。当超过 kTranslationLimit 设置 shouldCompleteTransition 为YES。
                    self.shouldCompleteTransition =
                    (self.isOverViewVisible ?
                     progress < -kTranslationLimit:
                     progress > kTranslationLimit);

                    /// 通过进度去更新过渡动画。
                    [self updateInteractiveTransition:progress];
                }
            }
            
            break;
        /// 手势识别失败或者取消的时候。
        case UIGestureRecognizerStateFailed:
        case UIGestureRecognizerStateCancelled:
            
            /// 如果在交互过程中的就直接取消。
            if (self.isInteractionInProgress)
            {
                self.interactionInProgress = NO;
                [self cancelInteractiveTransition];
            }
            
            break;
        /// 手势结束之后。
        case UIGestureRecognizerStateEnded:
            
            /// 判断是否在交互中。
            if (self.isInteractionInProgress)
            {
                self.interactionInProgress = NO;
                
                /// 判断是否应该完成移动。
                if (self.shouldCompleteTransition)
                {
                    /// 如果是。完成交互。
                    [self finishInteractiveTransition];
                }
                else
                {
                    /// 如果不是。取消交互。
                    [self cancelInteractiveTransition];
                }
            }
            
            break;
            
        case UIGestureRecognizerStatePossible:
            // Do nothing
            break;
    }
}

然后我们再看看完成手势动画的部分。
这个方法里面我删掉了很多代码。这样可以方便学习。如果想用做一些很酷炫的动画就要靠各位自行发挥了。
- (void)finishInteractiveTransition

- (void)finishInteractiveTransition
{
    /// 因为是直接完成动画所以我们 progress 我们直接写完成的值就好了。
    /// 当当前页面是上层视图的时候。是向上滑动进入主页面。向上滑动为负。所以是-1。
    /// 当当前页面是主视图的时候。道理同上。
    CGFloat progress = (self.isOverViewVisible ? - 1 : 1);
    
    /// 通过判断当前页面来获取新的约束。
    /// 当当前页面是上层视图的时候。我们需要完成上层视图到主视图的过渡。所以约束是 负的屏幕高度。
    /// 当当前页面是主视图的时候。道理同上。
    CGFloat newOverViewTopConstraintConstant = (self.isOverViewVisible ? -[[UIScreen mainScreen] bounds].size.height : 0);
    
    __weak typeof(self) weakSelf = self;
    
    void (^AnimationBlock)(void) = ^void (void)
    {
        /// 形变。
        [weakSelf transformContentWithProgress:progress];
    };
    
    void (^CompletionBlock)(BOOL finished) = ^void (BOOL finished)
    {
        /// 获取新的约束值
        weakSelf.overViewTopConstraint.constant = newOverViewTopConstraintConstant;
        
        /// 还原形变。
        weakSelf.overViewContrainer.transform = CGAffineTransformIdentity;
        
        /// 刷新页面。使约束生效。
        [weakSelf.view layoutIfNeeded];
        
        /// 获得当前显示的视图。
        weakSelf.overViewVisible = !weakSelf.isOverViewVisible;
        
    };
    
    /// 执行动画
    [UIView animateWithDuration:[self transitionAnimationDuration]
                          delay:0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:AnimationBlock
                     completion:CompletionBlock];
    
}

还有取消过渡的部分。
这一部分的代码很简单。如果上面的代码能理解这边就看一眼就明白了。
这一部分代码就是将形变还原回去。
- (void)cancelInteractiveTransition

- (void)cancelInteractiveTransition
{
    self.overViewTopEstimatedValue = self.overViewTopConstraint.constant;
    
    [self.mainVC cancelInteractiveTransition];
    [self.overVC cancelInteractiveTransition];
    
    __weak typeof(self) weakSelf = self;
    
    void (^AnimationBlock)(void) = ^void (void)
    {
        /// 将形变还原
        weakSelf.overViewContrainer.transform = CGAffineTransformIdentity;
        weakSelf.actionButton.transform = CGAffineTransformIdentity;
    };
    
    void (^CompletionBlock)(BOOL finished) = ^void (BOOL finished)
    {
        [weakSelf setNeedsStatusBarAppearanceUpdate];
    };
    
    [UIView animateWithDuration:[self transitionAnimationDuration]
                          delay:0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:AnimationBlock
                     completion:CompletionBlock];
}

还有最后一个通过进度更新过渡。
这个方法是在手势变化的时候调用的。通过进度的改变我们也随之改变形变。但是具体的事情却不是在这个方法里做的。单独有一个方法去做这个事情。
- (void)updateInteractiveTransition:(CGFloat)progress

- (void)updateInteractiveTransition:(CGFloat)progress
{
    [self setNeedsStatusBarAppearanceUpdate];
    
    /// 形变
    [self transformContentWithProgress:progress];
}

第二个核心的部分。

第二个核心的部分就是处理形变的部分。这个方法在完成动画更新动画中都有用到。视图的下滑之类的UI上的变化都是在这里做的。
这一部分我也简化了很多。方便各位去学习。
- (void)transformContentWithProgress:(CGFloat)progress

- (void)transformContentWithProgress:(CGFloat)progress
{
    /// 通过进度获取移动量
    CGFloat translationY = progress * self.view.frame.size.height;
    /// 使上层视图滑动
    self.overViewContrainer.transform = CGAffineTransformMakeTranslation(0, translationY);
    /// 获取当前的距离。通过这个参数去判断状态栏是否消失
    self.overViewTopEstimatedValue = self.overViewTopConstraint.constant + translationY;
}

总结

  看了这份代码之后。一开始看看的很懵逼。虽然这里面没有用到一切比较奇怪的东西。用到的方法什么的都还是看的懂的。但是还是没明白作者的思路。计算机最难的是思想嘛。
  于是之后决定还是自己实践一下好了。从最简单的开始。然后一点点加东西上去。最后就有了上面那个四个方向都可以移动的东西。其实整个项目也挺简单。就是做了两件事。第一件事就是形变。第二件是就是更新约束刷新页面。但是这里面有很多细节的地方一开始是想不到的。
  感谢这份代码。

最后

如果有些的不好的地方请各位直接说。
如果有不明白的地方也请各位直接问。
希望共同进步。希望iOS这个圈子越来越好。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容