iOS側(cè)滑返回

效果圖

popAnimation.gif

交互式動(dòng)畫(huà)的實(shí)現(xiàn)過(guò)程

動(dòng)畫(huà)
1悔常、給UINavigationController添加代理护赊,需實(shí)現(xiàn)UINavigationControllerDelegate協(xié)議框仔。
2舀武、創(chuàng)建動(dòng)畫(huà)類(lèi),該類(lèi)需實(shí)現(xiàn)UIViewControllerAnimatedTransitioning協(xié)議离斩。
手勢(shì)交互
3银舱、給UINavigationController添加手勢(shì)和完成手勢(shì)百分比的判定。
4跛梗、實(shí)現(xiàn)UIViewControllerAnimatedTransitioning協(xié)議的手勢(shì)方法寻馏,返回UIPercentDrivenInteractiveTransition對(duì)象。


給UINavigationController添加代理

創(chuàng)建PJNavigationInteractiveTransition類(lèi)用于對(duì)UINavigationController代理的實(shí)現(xiàn)

// PJNavigationInteractiveTransition.h
self.navTransition = 
[[PJNavigationInteractiveTransition alloc] initWithViewController:self];

// PJNavigationInteractiveTransition.h
- (instancetype)initWithViewController:(UINavigationController *)vc {
    
    self = [super init];
    if (self) {
        _navigationController = vc;
        _navigationController.delegate = self;
    }
    return self;
}

實(shí)現(xiàn)UINavigationControllerDelegate協(xié)議

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController *)fromVC
                                                 toViewController:(UIViewController *)toVC {
    
    if (operation == UINavigationControllerOperationPop) {
        return [[PJPopAnimation alloc] init];
    }
    _fromViewController = fromVC;
    
    return nil;
}
  • 當(dāng)調(diào)用- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated方法后系統(tǒng)將自動(dòng)調(diào)用該方法核偿。
  • 當(dāng)返回為nil時(shí)诚欠,將使用默認(rèn)的轉(zhuǎn)場(chǎng)。否者則使用特定的動(dòng)畫(huà)類(lèi)漾岳。

實(shí)現(xiàn)動(dòng)畫(huà)類(lèi)

動(dòng)畫(huà)類(lèi)主要就是實(shí)現(xiàn)UINavigationControllerDelegate協(xié)議中的兩個(gè)required方法

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
    
    return 0.3;
}

這個(gè)方法就是返回動(dòng)畫(huà)執(zhí)行的時(shí)間
</br>

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    
    // 1
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    // 2
    UIView *fromView;
    UIView *toView;
    
    if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
        fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
        toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    } else {
        fromView = fromViewController.view;
        toView = toViewController.view;
    }
    
    // 3
    fromView.frame = [transitionContext initialFrameForViewController:fromViewController];
    toView.frame = [transitionContext finalFrameForViewController:toViewController];
    
    // 4
    UIView *containerView = [transitionContext containerView];

    // 5
    [containerView insertSubview:toView belowSubview:fromView];
    
    // 6
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromView.transform = CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0);
    } completion:^(BOOL finished) {
        fromView.transform = CGAffineTransformIdentity;
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
    }];
}

1轰绵、獲取fromViewController與toViewController
fromViewController與toViewController是相對(duì)的,如下圖所示


屏幕快照 2017-03-30 15.41.21.png

2尼荆、獲取fromView與toView
當(dāng)系統(tǒng)版本為iOS8.0以上時(shí)左腔,需使用viewForKey。

3捅儒、設(shè)置frame液样。

4、獲取containerView
containerView就是轉(zhuǎn)場(chǎng)動(dòng)畫(huà)執(zhí)行中屏幕所展示的View巧还,相當(dāng)于是動(dòng)畫(huà)展示的平臺(tái)鞭莽。

5、將fromView與toView加入至containerView中

6麸祷、設(shè)置動(dòng)畫(huà)
當(dāng)動(dòng)畫(huà)結(jié)束后必須調(diào)用completeTransition方法以告知系統(tǒng)動(dòng)畫(huà)完成澎怒。

給UINavigationController添加手勢(shì)和完成手勢(shì)百分比的判定

給UINavigationController添加手勢(shì)

UIGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
gesture.enabled = NO;

UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] init];
popRecognizer.delegate = self;
popRecognizer.maximumNumberOfTouches = 1;
[gesture.view addGestureRecognizer:popRecognizer];

self.navTransition = [[PJNavigationInteractiveTransition alloc] initWithViewController:self];
[popRecognizer addTarget:self.navTransition action:@selector(handleControllerPop:)];
  • 禁止系統(tǒng)的側(cè)滑返回
  • 添加我們自己的Pan手勢(shì)

手勢(shì)的限制

#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
    
    // Ignore when no view controller is pushed into the navigation stack.
    if (self.viewControllers.count <= 1) {
        return NO;
    }
    
    // Ignore when the beginning location is beyond max allowed initial distance to left edge.
    CGPoint beginningLocation = [gestureRecognizer locationInView:gestureRecognizer.view];
    if (maxAllowedInitialDistance > 0 && beginningLocation.x > maxAllowedInitialDistance) {
        return NO;
    }
    
    // Ignore pan gesture when the navigation controller is currently in transition.
    if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) {
        return NO;
    }
    
    // Prevent calling the handler when the gesture begins in an opposite direction.
    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
    if (translation.x <= 0) {
        return NO;
    }
    
    // When the active view controller allow interactive pop.
    UIViewController *topViewController = self.viewControllers.lastObject;
    if (topViewController.pj_interactivePopDisabled) {
        return NO;
    }
    
    return YES;
}

手勢(shì)百分比的判定

- (void)handleControllerPop:(UIGestureRecognizer *)gestureRecognizer {
    /**
     * 穩(wěn)定進(jìn)度區(qū)間,讓它在0.0(未完成)~1.0(已完成)之間
     */
    CGFloat progress = 0.0;
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
        UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)gestureRecognizer;
        progress = [recognizer translationInView:recognizer.view].x / recognizer.view.bounds.size.width;
        progress = MIN(1.0, MAX(0.0, progress));
    }
    
    [self p_handleGestureRecognizer:gestureRecognizer Progress:progress];
}

- (void)p_handleGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer Progress:(CGFloat)progress {
    
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
        [self.navigationController popViewControllerAnimated:YES];
    } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
        [self.interactivePopTransition updateInteractiveTransition:progress];
    } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded ||
               gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
        if (progress > 0.5) {
            [self.interactivePopTransition finishInteractiveTransition];
            if (_fromViewController && _fromViewController.pj_transitionFinishedBlock) {
                _fromViewController.pj_transitionFinishedBlock();
            }
        } else {
            [self.interactivePopTransition cancelInteractiveTransition];
        }
        self.interactivePopTransition = nil;
    }
}

UIPercentDrivenInteractiveTransition是什么
這是一個(gè)實(shí)現(xiàn)了UIViewControllerInteractiveTransitioning接口的類(lèi)摇锋,為我們預(yù)先實(shí)現(xiàn)和提供了一系列便利的方法丹拯,可以用一個(gè)百分比來(lái)控制交互式切換的過(guò)程。具體有以下幾個(gè)重要方法:
-(void)updateInteractiveTransition:(CGFloat)percentComplete 更新百分比荸恕,一般通過(guò)手勢(shì)識(shí)別的長(zhǎng)度之類(lèi)的來(lái)計(jì)算一個(gè)值乖酬,然后進(jìn)行更新。
-(void)cancelInteractiveTransition 報(bào)告交互取消融求,返回切換前的狀態(tài)
–(void)finishInteractiveTransition 報(bào)告交互完成咬像,更新到切換后的狀態(tài)

實(shí)現(xiàn)UIViewControllerAnimatedTransitioning協(xié)議的手勢(shì)方法

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
                         interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
    
    if ([animationController isKindOfClass:[PJPopAnimation class]]) {
        return self.interactivePopTransition;
    }
    return nil;
}

參考

https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html#//apple_ref/doc/uid/TP40007457-CH16-SW1
https://developer.apple.com/library/content/samplecode/CustomTransitions/Introduction/Intro.html#//apple_ref/doc/uid/TP40015158-Intro-DontLinkElementID_2
https://onevcat.com/2013/10/vc-transition-in-ios7/
https://github.com/forkingdog/FDFullscreenPopGesture

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子县昂,更是在濱河造成了極大的恐慌肮柜,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倒彰,死亡現(xiàn)場(chǎng)離奇詭異审洞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)待讳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)芒澜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人创淡,你說(shuō)我怎么就攤上這事痴晦。” “怎么了琳彩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵誊酌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我露乏,道長(zhǎng)碧浊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任施无,我火速辦了婚禮辉词,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猾骡。我一直安慰自己瑞躺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布兴想。 她就那樣靜靜地躺著幢哨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嫂便。 梳的紋絲不亂的頭發(fā)上捞镰,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音毙替,去河邊找鬼岸售。 笑死,一個(gè)胖子當(dāng)著我的面吹牛厂画,可吹牛的內(nèi)容都是我干的凸丸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼袱院,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼屎慢!你這毒婦竟也來(lái)了瞭稼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤腻惠,失蹤者是張志新(化名)和其女友劉穎环肘,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體集灌,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悔雹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绝页。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荠商。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寂恬,死狀恐怖续誉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情初肉,我是刑警寧澤酷鸦,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站牙咏,受9級(jí)特大地震影響臼隔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妄壶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一摔握、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丁寄,春花似錦氨淌、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至屑埋,卻和暖如春豪筝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摘能。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工续崖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人团搞。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓严望,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親莺丑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子著蟹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1. 使用官方API解決Navigation側(cè)滑導(dǎo)致的Navigationbar異常顯示和隱藏的問(wèn)題 參考:htt...
    曉飛90閱讀 4,199評(píng)論 0 1
  • ios7開(kāi)始 蘋(píng)果增加了頁(yè)面 右滑返回的效果墩蔓;具體的是以UINavigationController為容器的Vie...
    Q6尐漒閱讀 1,277評(píng)論 0 0
  • 前言(其實(shí)就是廢話,可以忽略 = =) 側(cè)滑返回手勢(shì)是從iOS7開(kāi)始增加的一個(gè)返回操作萧豆,經(jīng)歷了兩年時(shí)間估計(jì)iPho...
    LonlyCat閱讀 32,395評(píng)論 20 102
  • 現(xiàn)在開(kāi)發(fā)的APP項(xiàng)目中奸披,如果沒(méi)有實(shí)現(xiàn)側(cè)滑返回的功能,都不好意思拿出來(lái)秀~ 雖然官方提供了側(cè)滑返回的API涮雷,但是很多...
    正在爬坡的iOS程序猿閱讀 26,103評(píng)論 15 95
  • 如果可以回到那天阵面,我希望我是選擇牽是你的手,而不是擦肩而過(guò)洪鸭,請(qǐng)?jiān)彯?dāng)初那個(gè)懦弱的我样刷,對(duì)不起,沒(méi)有對(duì)你說(shuō)我喜歡你览爵,你...
    溴麝香草酚藍(lán)液閱讀 281評(píng)論 0 0