因?yàn)樽罱昧艘幌耂napchat。發(fā)現(xiàn)他的主頁的上下左右滑動(dòng)都可以切換進(jìn)入一個(gè)新的視圖。覺得挺好玩的。所以就決定也照著寫一個(gè)。???????
想了很多解決方法都還是不能很好的將這個(gè)效果做出來。于是借鑒了網(wǎng)上的一些Demo。很僵硬。照抄根本沒辦法把這個(gè)代碼學(xué)習(xí)進(jìn)去。(? ̄?д ̄??)
所以想了想還是一邊學(xué)習(xí)一邊寫一篇文章做個(gè)記錄。這樣學(xué)習(xí)的過程也可能會(huì)清晰一點(diǎn)。
效果如下
好了。我們進(jìn)入正題。
1. 那我們先看一下這個(gè)項(xiàng)目結(jié)構(gòu)。
2. 我們看一下Storyboard吧。
這個(gè)Storyboard中用到了我之前從沒有用過的一個(gè)控件。
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ā)者平臺(tái)。大致的意思就是ContainerViewController可以在一個(gè)容器視圖控制器中添加多個(gè)視圖控制器。這里有一篇文章對(duì)這個(gè)控件分析的挺好的。大家可以看看。
ContainerViewCotroller中的視圖結(jié)構(gòu)是這樣的。它包含兩個(gè)ContainerView對(duì)應(yīng)的就是另外兩個(gè)VC的view。這邊還用到了Segue。并且給了Identifier。那么說明等會(huì)兒會(huì)對(duì)Segue做處理。
3. 輪到看代碼了。
我們先看最簡單的ContainerChildViewController。
定義了三個(gè)虛函數(shù)。因?yàn)槎紱]有實(shí)現(xiàn)三個(gè)方法所以我們也沒什么好看了。這三個(gè)方法具體是干什么的通過方法名我大致猜了一下可能是做這些事的。
/// 更新交互式過渡
- (void)updateInteractiveTransition:(CGFloat)progress;
/// 取消交互式過渡
- (void)cancelInteractiveTransition;
/// 完成交互式過渡
- (void)finishInteractiveTransition;
然后看看重量級(jí)的ContainerViewController。
ContainerViewController.h
#import <UIKit/UIKit.h>
@interface ContainerViewController : UIViewController
/// 過渡動(dòng)畫時(shí)長
@property(nonatomic, assign) CGFloat transitionAnimationDuration;
/// 上層視圖是否可見(只讀)
@property(nonatomic, assign, getter=isOverViewVisible, readonly) BOOL overViewVisible;
/// 交互進(jìn)度
@property(nonatomic, assign, getter=isInteractionInProgress, readonly) BOOL interactionInProgress;
/// 上層視圖消失
- (void)dismissOverViewController;
/// 上層視圖顯示
- (void)presentOverViewController;
@end
ContainerViewController.m
先看下實(shí)現(xiàn)文件里聲明的一下屬性。
/// 目前還不知道這個(gè)常量有什么用
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;
/// 交互進(jìn)度(讀寫)
@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;
/// 上層視圖容器距離頂部的約束(初始化為負(fù)的屏幕高度)
@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;
/// 上層視圖容器距離頂部的預(yù)估值
@property(nonatomic, assign) CGFloat overViewTopEstimatedValue;
@end
好了。接下來我們一個(gè)方法一個(gè)方法去看。這樣方便我們學(xué)習(xí)。
- (void)awakeFromNib
初始化overViewVisible與overViewTopEstimatedValue。
- (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
設(shè)置隱藏狀態(tài)欄。因?yàn)檫@個(gè)容器視圖控制器中有另外兩個(gè)子視圖控制器。所以隱藏狀態(tài)欄寫在這里。下面會(huì)有一個(gè)方法當(dāng)拖拽時(shí)回去調(diào)用setNeedsStatusBarAppearanceUpdate。所以系統(tǒng)會(huì)調(diào)用prefersStatusBarHidden重新獲取是否隱藏狀態(tài)欄。
- (BOOL)prefersStatusBarHidden
{
/// 設(shè)置狀態(tài)欄顯示范圍
static CGFloat const kVisibleStatusBarRange = -10.0;
/// 當(dāng)上層視圖控制器距離上邊距只有10的時(shí)候狀態(tài)欄隱藏
return (self.overViewTopEstimatedValue < kVisibleStatusBarRange);
}
- (void)prepareForSegue:(UIStoryboardSegue)segue sender:(id)sender*
通知視圖控制器即將要跳轉(zhuǎn)。segue中包含了跳轉(zhuǎn)的信息。
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
/// 通過segue.identifier去判斷跳轉(zhuǎn)至那個(gè)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
按鈕的點(diǎn)擊事件。
- (IBAction)actionButtonTouched:(id)sender
{
/// 判斷上層視圖是否顯示
if (self.isOverViewVisible)
{
/// 如果顯示上層視圖。當(dāng)點(diǎn)擊按鈕之后dismiss上層視圖
[self dismissOverViewController];
}
}
- (void)transformContentWithProgress:(CGFloat)progress
通過交互的進(jìn)度使內(nèi)容變形。
- (void)transformContentWithProgress:(CGFloat)progress
{
/// 使上層視圖變形
/// Y軸上的移動(dòng)量
CGFloat translationY = progress * self.view.frame.size.height;
/// CGAffineTransformMakeTranslation每次都會(huì)相對(duì)于初始的中心點(diǎn)做變化
self.overViewContainer.transform = CGAffineTransformMakeTranslation(0, translationY);
#warning 這里為什么要賦值給overViewTopEstimatedValue???
/// 將上層視圖的位置賦值給overViewTopEstimatedValue
self.overViewTopEstimatedValue = self.overViewTopConstraint.constant + translationY;
/// --------------------------------------------------------------------------------- ///
/// 按鈕的形變
/// 上層視圖是否顯示。如果顯示則為progress的絕對(duì)值。如果不顯示則為1 - progress
/// 當(dāng)上層視圖顯示并且從下向上滑時(shí)。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 并不是常量。當(dāng)過渡完成時(shí)改變
CGFloat actionButtonScale = newActionButtonSize / self.actionButtonWidthConstraint.constant;
CGAffineTransform actionButtonScaleTransform = CGAffineTransformMakeScale(actionButtonScale, actionButtonScale);
/// --------------------------------------------------------------------------------- ///
/// 按鈕的偏移
/// 因?yàn)槲覀兺瑫r(shí)讓按鈕偏移并且縮放。所以我們要將兩個(gè)形變拼起來
#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
過渡動(dòng)畫的時(shí)間。
- (CGFloat)transitionAnimationDuration
{
return 0.25;
}
- (void)dismissOverViewController
上層視圖dismiss。
- (void)dismissOverViewController
{
[self finishInteractiveTransition];
}
- (void)cancelInteractiveTransition
取消交互式過渡。
- (void)cancelInteractiveTransition
{
/// 重新獲取上層視圖相對(duì)頂部的距離。
self.overViewTopEstimatedValue = self.overViewTopConstraint.constant;
/// 通知主視圖控制器與上層視圖控制器 取消交互式過渡。
[self.mainViewController cancelInteractiveTransition];
[self.overViewController cancelInteractiveTransition];
/// 對(duì)弱引用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í)行動(dòng)畫。
[UIView animateWithDuration:[self transitionAnimationDuration]
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:AnimationBlock
completion:CompletionBlock];
}
- (void)finishInteractiveTransition
完成交互式過渡。
- (void)finishInteractiveTransition
{
/// 通知主視圖控制器與上層視圖控制器完成交互式過渡
[self.mainViewController finishInteractiveTransition];
[self.overViewController finishInteractiveTransition];
/// 判斷當(dāng)前進(jìn)度。如果上層視圖顯示則為 -1,否則為 1。
CGFloat progress = (self.isOverViewVisible ? - 1 : 1);
/// 重新獲取上層視圖相對(duì)頂部的距離。
CGFloat newOverViewTopConstraintConstant =
(self.isOverViewVisible ?
-[UIScreen mainScreen].bounds.size.height :
0);
/// 重新獲取按鈕相對(duì)于底部的距離
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)
{
/// 通過進(jìn)度發(fā)生形變
[blockSelf transformContentWithProgress:progress];
};
void (^CompletionBlock)(BOOL finished) = ^void (BOOL finished)
{
/// 賦值新的約束
blockSelf.overViewTopConstraint.constant =
newOverViewTopConstraintConstant;
blockSelf.actionButtonBottomConstraint.constant =
newActionButtonBottomConstraintConstant;
blockSelf.actionButtonWidthConstraint.constant =
newActionButtonWidthConstraintConstant;
/// 獲取新的上層視圖相對(duì)頂部的約束
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
{
/// 獲取手勢位移(位置的偏移量,所有點(diǎn)都相對(duì)于動(dòng)作起點(diǎn)的距離)。
CGPoint translation = [gesture translationInView:self.view];
/// 手勢移動(dòng)的速度。
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;
/// 滑動(dòng)距離的上限。
static CGFloat const kTranslationLimit = 0.30;
/// 通過判斷手勢的不同狀態(tài)來做不同事情。
switch (gesture.state)
{
/// 剛開始拖拽時(shí)。
case UIGestureRecognizerStateBegan:
self.shouldCompleteTransition = NO;
self.interactionInProgress = YES;
break;
/// 開始拖拽。
case UIGestureRecognizerStateChanged:
/// 判斷是否在交互中。
if (self.isInteractionInProgress)
{
/// 如果滑動(dòng)速度太快。直接完成動(dòng)畫。
if ((self.isOverViewVisible && velocity.y < -kVelocityLimit) ||
(!self.isOverViewVisible && velocity.y > kVelocityLimit))
{
/// 完成過渡。
self.interactionInProgress = NO;
[self finishInteractiveTransition];
}
else
{
/// 當(dāng)手勢完成的時(shí)候動(dòng)畫也會(huì)完成。
self.shouldCompleteTransition =
(self.isOverViewVisible ?
progress < -kTranslationLimit:
progress > kTranslationLimit);
[self updateInteractiveTransition:progress];
}
}
break;
/// 手勢識(shí)別失敗或者取消的時(shí)候。
case UIGestureRecognizerStateFailed:
case UIGestureRecognizerStateCancelled:
/// 如果在交互過程中的就直接取消。
if (self.isInteractionInProgress)
{
self.interactionInProgress = NO;
[self cancelInteractiveTransition];
}
break;
/// 手勢結(jié)束之后。
case UIGestureRecognizerStateEnded:
/// 判斷是否在交互中。
if (self.isInteractionInProgress)
{
self.interactionInProgress = NO;
/// 判斷是否應(yīng)該完成移動(dòng)。
if (self.shouldCompleteTransition)
{
/// 如果是。完成交互。
[self finishInteractiveTransition];
}
else
{
/// 如果不是。取消交互。
[self cancelInteractiveTransition];
}
}
break;
case UIGestureRecognizerStatePossible:
// Do nothing
break;
}
}
即使這樣讀完了代碼但是還是對(duì)這個(gè)類理解還不夠通透。所以我決定再看一遍。并且重點(diǎn)看看一下疑問的地方。
我想先去搞清楚關(guān)于Transition這塊的幾個(gè)方法到底是做了什么事。
過了兩天。終于搞清楚了這個(gè)類到底做了什么事情。自己的水平不夠。加上又是閱讀別人的代碼真的是好累阿。不過好在學(xué)到了東西。
多說不說。先看看實(shí)現(xiàn)的效果。
在原來的代碼上做了點(diǎn)修改。實(shí)現(xiàn)了四個(gè)方向的滑動(dòng)。雖然沒有上面的酷炫但是這個(gè)原理是差不多了。
那我們繼續(xù)看代碼吧。這回就直接看核心部分吧。
第一個(gè)核心的部分。
首先應(yīng)該是處理手勢的那部分。
通過三個(gè)方法去處理動(dòng)畫:
分別是:
1. - (void)updateInteractiveTransition:(CGFloat)progress
2. - (void)cancelInteractiveTransition
3. - (void)finishInteractiveTransition
這三個(gè)方法在處理手勢的時(shí)候都有用到。我們通過手勢的不同狀態(tài)與手勢的移動(dòng)距離去判斷分別調(diào)用什么方法。
我們先來看看觸發(fā)手勢時(shí)候發(fā)生了什么。
*- (void)handleGestureRecognizer:(UIPanGestureRecognizer )gesture
- (void)handleGestureRecognizer:(UIPanGestureRecognizer*)gesture
{
/// 速度的上限。
static CGFloat const kVelocityLimit = 2000.0;
/// 滑動(dòng)距離的上限。
static CGFloat const kTranslationLimit = 0.30;
/// 獲取手勢位移(位置的偏移量,所有點(diǎn)都相對(duì)于動(dòng)作起點(diǎn)的距離)。
CGPoint translation = [gesture translationInView:self.view];
/// 手勢移動(dòng)的速度。
CGPoint velocity = [gesture velocityInView:self.view];
/// 獲取progress的值。
CGFloat progress = translation.y / self.view.frame.size.height;
/// 避免過度向上滑或者過度向下滑。
/// 先判斷當(dāng)前所在頁面。
/// 如果是在上層視圖。那么就是只能上滑。向上滑動(dòng) translation.y 為負(fù)數(shù)。所以取值范圍是 -1 到 0。
/// 如果是在主視圖。道理同上。
progress =
(self.isOverViewVisible ?
fmin(0.0, fmax(-1.0, progress)) :
fmin(1.0, fmax(0.0, progress)));
/// 通過判斷手勢的不同狀態(tài)來做不同事情。
switch (gesture.state)
{
/// 剛開始拖拽時(shí)。
case UIGestureRecognizerStateBegan:
self.shouldCompleteTransition = NO;
self.interactionInProgress = YES;
break;
/// 開始拖拽。
case UIGestureRecognizerStateChanged:
/// 判斷是否在交互中。
if (self.isInteractionInProgress)
{
/// 判斷手勢的速度。
/// 如果滑動(dòng)速度太快。直接完成動(dòng)畫。
/// 不能用絕對(duì)值去判斷速度。
/// 如果用絕對(duì)值判斷會(huì)導(dǎo)致在主頁面??焖傧蛏匣瑒?dòng)。也會(huì)觸發(fā)以下代碼。違反交互。
if ((self.isOverViewVisible && velocity.y < -kVelocityLimit) ||
(!self.isOverViewVisible && velocity.y > kVelocityLimit))
{
/// 完成過渡。
self.interactionInProgress = NO;
[self finishInteractiveTransition];
}
else
{
/// 當(dāng)手勢完成的時(shí)候動(dòng)畫也會(huì)完成。
/// 通過已經(jīng)移動(dòng)過的比例去判斷是否需要完成動(dòng)畫。當(dāng)超過 kTranslationLimit 設(shè)置 shouldCompleteTransition 為YES。
self.shouldCompleteTransition =
(self.isOverViewVisible ?
progress < -kTranslationLimit:
progress > kTranslationLimit);
/// 通過進(jìn)度去更新過渡動(dòng)畫。
[self updateInteractiveTransition:progress];
}
}
break;
/// 手勢識(shí)別失敗或者取消的時(shí)候。
case UIGestureRecognizerStateFailed:
case UIGestureRecognizerStateCancelled:
/// 如果在交互過程中的就直接取消。
if (self.isInteractionInProgress)
{
self.interactionInProgress = NO;
[self cancelInteractiveTransition];
}
break;
/// 手勢結(jié)束之后。
case UIGestureRecognizerStateEnded:
/// 判斷是否在交互中。
if (self.isInteractionInProgress)
{
self.interactionInProgress = NO;
/// 判斷是否應(yīng)該完成移動(dòng)。
if (self.shouldCompleteTransition)
{
/// 如果是。完成交互。
[self finishInteractiveTransition];
}
else
{
/// 如果不是。取消交互。
[self cancelInteractiveTransition];
}
}
break;
case UIGestureRecognizerStatePossible:
// Do nothing
break;
}
}
然后我們?cè)倏纯赐瓿墒謩輨?dòng)畫的部分。
這個(gè)方法里面我刪掉了很多代碼。這樣可以方便學(xué)習(xí)。如果想用做一些很酷炫的動(dòng)畫就要靠各位自行發(fā)揮了。
- (void)finishInteractiveTransition
- (void)finishInteractiveTransition
{
/// 因?yàn)槭侵苯油瓿蓜?dòng)畫所以我們 progress 我們直接寫完成的值就好了。
/// 當(dāng)當(dāng)前頁面是上層視圖的時(shí)候。是向上滑動(dòng)進(jìn)入主頁面。向上滑動(dòng)為負(fù)。所以是-1。
/// 當(dāng)當(dāng)前頁面是主視圖的時(shí)候。道理同上。
CGFloat progress = (self.isOverViewVisible ? - 1 : 1);
/// 通過判斷當(dāng)前頁面來獲取新的約束。
/// 當(dāng)當(dāng)前頁面是上層視圖的時(shí)候。我們需要完成上層視圖到主視圖的過渡。所以約束是 負(fù)的屏幕高度。
/// 當(dāng)當(dāng)前頁面是主視圖的時(shí)候。道理同上。
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];
/// 獲得當(dāng)前顯示的視圖。
weakSelf.overViewVisible = !weakSelf.isOverViewVisible;
};
/// 執(zhí)行動(dòng)畫
[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];
}
還有最后一個(gè)通過進(jìn)度更新過渡。
這個(gè)方法是在手勢變化的時(shí)候調(diào)用的。通過進(jìn)度的改變我們也隨之改變形變。但是具體的事情卻不是在這個(gè)方法里做的。單獨(dú)有一個(gè)方法去做這個(gè)事情。
- (void)updateInteractiveTransition:(CGFloat)progress
- (void)updateInteractiveTransition:(CGFloat)progress
{
[self setNeedsStatusBarAppearanceUpdate];
/// 形變
[self transformContentWithProgress:progress];
}
第二個(gè)核心的部分。
第二個(gè)核心的部分就是處理形變的部分。這個(gè)方法在完成動(dòng)畫與更新動(dòng)畫中都有用到。視圖的下滑之類的UI上的變化都是在這里做的。
這一部分我也簡化了很多。方便各位去學(xué)習(xí)。
- (void)transformContentWithProgress:(CGFloat)progress
- (void)transformContentWithProgress:(CGFloat)progress
{
/// 通過進(jìn)度獲取移動(dòng)量
CGFloat translationY = progress * self.view.frame.size.height;
/// 使上層視圖滑動(dòng)
self.overViewContrainer.transform = CGAffineTransformMakeTranslation(0, translationY);
/// 獲取當(dāng)前的距離。通過這個(gè)參數(shù)去判斷狀態(tài)欄是否消失
self.overViewTopEstimatedValue = self.overViewTopConstraint.constant + translationY;
}
總結(jié)
看了這份代碼之后。一開始看看的很懵逼。雖然這里面沒有用到一切比較奇怪的東西。用到的方法什么的都還是看的懂的。但是還是沒明白作者的思路。計(jì)算機(jī)最難的是思想嘛。
于是之后決定還是自己實(shí)踐一下好了。從最簡單的開始。然后一點(diǎn)點(diǎn)加?xùn)|西上去。最后就有了上面那個(gè)四個(gè)方向都可以移動(dòng)的東西。其實(shí)整個(gè)項(xiàng)目也挺簡單。就是做了兩件事。第一件事就是形變。第二件是就是更新約束刷新頁面。但是這里面有很多細(xì)節(jié)的地方一開始是想不到的。
感謝這份代碼。
最后
如果有些的不好的地方請(qǐng)各位直接說。
如果有不明白的地方也請(qǐng)各位直接問。
希望共同進(jìn)步。希望iOS這個(gè)圈子越來越好。