#iOS開發(fā)#仿Snapchat主頁面切換視圖

因為最近用了一下Snapchat。發(fā)現他的主頁的上下左右滑動都可以切換進入一個新的視圖。覺得挺好玩的。所以就決定也照著寫一個。???????
想了很多解決方法都還是不能很好的將這個效果做出來。于是借鑒了網上的一些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.

上面那段話截取蘋果的開發(fā)者平臺。大致的意思就是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
為視圖添加拖拽手勢并且初始化
overViewTopConstraint、originalActionButtonBottomConstraintConstant、
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
設置隱藏狀態(tài)欄。因為這個容器視圖控制器中有另外兩個子視圖控制器。所以隱藏狀態(tài)欄寫在這里。下面會有一個方法當拖拽時回去調用setNeedsStatusBarAppearanceUpdate。所以系統(tǒng)會調用prefersStatusBarHidden重新獲取是否隱藏狀態(tài)欄。

- (BOOL)prefersStatusBarHidden
{
    /// 設置狀態(tài)欄顯示范圍
    static CGFloat const kVisibleStatusBarRange = -10.0;
    /// 當上層視圖控制器距離上邊距只有10的時候狀態(tài)欄隱藏
    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。避免循環(huán)引用。
    __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)
    {
        /// 完成后通知系統(tǒng)重新獲取狀態(tài)欄是否隱藏。
        [blockSelf setNeedsStatusBarAppearanceUpdate];
    };
    
    /// 執(zhí)行動畫。
    [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)
    {
        /// 通過進度發(fā)生形變
        [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*
手勢觸發(fā)的事件。

- (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;
    
    /// 通過判斷手勢的不同狀態(tài)來做不同事情。
    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

在原來的代碼上做了點修改。實現了四個方向的滑動。雖然沒有上面的酷炫但是這個原理是差不多了。
那我們繼續(xù)看代碼吧。這回就直接看核心部分吧。

第一個核心的部分。

首先應該是處理手勢的那部分。
通過三個方法去處理動畫:
分別是:
1. - (void)updateInteractiveTransition:(CGFloat)progress
2. - (void)cancelInteractiveTransition
3. - (void)finishInteractiveTransition
這三個方法在處理手勢的時候都有用到。我們通過手勢的不同狀態(tài)與手勢的移動距離去判斷分別調用什么方法。
我們先來看看觸發(fā)手勢時候發(fā)生了什么。
*- (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)));
    
    /// 通過判斷手勢的不同狀態(tài)來做不同事情。
    switch (gesture.state)
    {
            /// 剛開始拖拽時。
        case UIGestureRecognizerStateBegan:
            
            self.shouldCompleteTransition = NO;
            self.interactionInProgress = YES;
            
            break;
            /// 開始拖拽。
        case UIGestureRecognizerStateChanged:
            
            /// 判斷是否在交互中。
            if (self.isInteractionInProgress)
            {
                /// 判斷手勢的速度。
                /// 如果滑動速度太快。直接完成動畫。
                /// 不能用絕對值去判斷速度。
                /// 如果用絕對值判斷會導致在主頁面??焖傧蛏匣瑒?。也會觸發(fā)以下代碼。違反交互。
                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;
    }
}

然后我們再看看完成手勢動畫的部分。
這個方法里面我刪掉了很多代碼。這樣可以方便學習。如果想用做一些很酷炫的動畫就要靠各位自行發(fā)揮了。
- (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;
        
    };
    
    /// 執(zhí)行動畫
    [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);
    /// 獲取當前的距離。通過這個參數去判斷狀態(tài)欄是否消失
    self.overViewTopEstimatedValue = self.overViewTopConstraint.constant + translationY;
}

總結

  看了這份代碼之后。一開始看看的很懵逼。雖然這里面沒有用到一切比較奇怪的東西。用到的方法什么的都還是看的懂的。但是還是沒明白作者的思路。計算機最難的是思想嘛。
  于是之后決定還是自己實踐一下好了。從最簡單的開始。然后一點點加東西上去。最后就有了上面那個四個方向都可以移動的東西。其實整個項目也挺簡單。就是做了兩件事。第一件事就是形變。第二件是就是更新約束刷新頁面。但是這里面有很多細節(jié)的地方一開始是想不到的。
  感謝這份代碼。

最后

如果有些的不好的地方請各位直接說。
如果有不明白的地方也請各位直接問。
希望共同進步。希望iOS這個圈子越來越好。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容