Commit 86d3092a authored by 汪洋's avatar 汪洋

使用协议来添加 navigion 动画

parent 3e4f79e9
...@@ -14,7 +14,7 @@ PODS: ...@@ -14,7 +14,7 @@ PODS:
- AFNetworking/Serialization (3.1.0) - AFNetworking/Serialization (3.1.0)
- AFNetworking/UIKit (3.1.0): - AFNetworking/UIKit (3.1.0):
- AFNetworking/NSURLSession - AFNetworking/NSURLSession
- GMBase (1.1.4): - GMBase (1.1.5):
- GMHud - GMHud
- GMJSONModel - GMJSONModel
- GMKit - GMKit
...@@ -90,7 +90,7 @@ SPEC REPOS: ...@@ -90,7 +90,7 @@ SPEC REPOS:
- GMNetService - GMNetService
- GMPhobos - GMPhobos
- GMRefresh - GMRefresh
https://github.com/cocoapods/specs.git: https://github.com/CocoaPods/Specs.git:
- AFNetworking - AFNetworking
- Masonry - Masonry
- MBProgressHUD - MBProgressHUD
...@@ -104,7 +104,7 @@ EXTERNAL SOURCES: ...@@ -104,7 +104,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
GMBase: aa92f9e566cc5807d1c1439d3e8c588b5b413a7e GMBase: b747cd3ecd7a5109469113cf48ce04ae6d16cdbe
GMCache: b78d8e46db864405e91d226ce640cc80d966c611 GMCache: b78d8e46db864405e91d226ce640cc80d966c611
GMHud: 45aa755b72a65f89d810430336803086359c0fb1 GMHud: 45aa755b72a65f89d810430336803086359c0fb1
GMJSONModel: 5e81a98de668e9f93cf6ff77869f77b0d1a806be GMJSONModel: 5e81a98de668e9f93cf6ff77869f77b0d1a806be
...@@ -120,4 +120,4 @@ SPEC CHECKSUMS: ...@@ -120,4 +120,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: b9c24bb8fb1ea50b58c59e079b456b4ca1d9b998 PODFILE CHECKSUM: b9c24bb8fb1ea50b58c59e079b456b4ca1d9b998
COCOAPODS: 1.7.4 COCOAPODS: 1.9.1
//
// GMCustomNavigationAnimationProtocol.h
// Pods
//
// Created by wangyang on 2020/4/7.
//
#ifndef GMCustomNavigationAnimationProtocol_h
#define GMCustomNavigationAnimationProtocol_h
/// 通过此协议来约束所有自定义 navigation 动画类的行为。
@protocol GMControllerAnimatedTransitioning <UIViewControllerAnimatedTransitioning>
/// 自定义 navigation 动画类需要知道某一时刻是 push还是pop。
@property(nonatomic, assign) UINavigationControllerOperation transitionType;
@end
/// 对于想要使用自定义 push、pop 动画的 controller,需要实现该协议。
/// GMNavigationController 会检查 controller 是否实现该协议以决定是否要使用自定义动画
/// 该协议要求controller 需要实现一个属性,
/// 并且要求该属性也需要实现一个协议:GMControllerAnimatedTransitioning。
/// 该属于会用在 GMNavigationController中,具体逻辑请自行查找。
/// 下面有一个 Demo
@protocol GMCustomNavigationAnimationProtocol <NSObject>
@property (nonatomic, strong) id<GMControllerAnimatedTransitioning> navigationAnimation;
@end
#endif
/* Demo
// 挂载协议
@interface GMController () <GMCustomNavigationAnimationProtocol>
@end
@implementation GMController
// 使用synthesize确保协议中要求的属性有正确的 set 和 get 方法
@synthesize navigationAnimation;
- (void)initController {
[super initController];
// 为协议中要求的属性赋值
GMPresentAnimation *navigationAnimation = [GMPresentAnimation new];
navigationAnimation.needMask = YES;
self.navigationAnimation = navigationAnimation;
}
*/
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@interface GMNavigationController : UINavigationController @interface GMNavigationController : UINavigationController
// TODO: wangyang 这个属性的历史需要找人了解清楚,缺少对应的注释
@property (nonatomic, assign) BOOL needPopPresetAnimation; @property (nonatomic, assign) BOOL needPopPresetAnimation;
@end @end
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#import "UIViewController+PushType.h" #import "UIViewController+PushType.h"
#import "GMPresentAnimation.h" #import "GMPresentAnimation.h"
@import GMKit; @import GMKit;
#import "GMCustomNavigationAnimationProtocol.h"
@interface GMNavigationController ()<UINavigationControllerDelegate> @interface GMNavigationController ()<UINavigationControllerDelegate>
...@@ -27,31 +29,28 @@ ...@@ -27,31 +29,28 @@
animationControllerForOperation:(UINavigationControllerOperation)operation animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC { toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPush && [toVC conformsToProtocol:@protocol(GMCustomNavigationAnimationProtocol)]) {
if (operation == UINavigationControllerOperationPush && toVC.isPresent) { id<GMCustomNavigationAnimationProtocol> controller = (id<GMCustomNavigationAnimationProtocol>)toVC;
GMPresentAnimation *animation = [[GMPresentAnimation alloc] init]; id<GMControllerAnimatedTransitioning> animation = controller.navigationAnimation;
animation.transitionType = operation; animation.transitionType = operation;
return animation; return animation;
} else if (operation == UINavigationControllerOperationPop && (fromVC.isPresent || self.needPopPresetAnimation)) { } else if (operation == UINavigationControllerOperationPop && ([fromVC conformsToProtocol:@protocol(GMCustomNavigationAnimationProtocol)] || self.needPopPresetAnimation)) {
_needPopPresetAnimation = NO; id<GMCustomNavigationAnimationProtocol> controller = (id<GMCustomNavigationAnimationProtocol>)fromVC;
GMPresentAnimation *animation = [[GMPresentAnimation alloc] init]; id<GMControllerAnimatedTransitioning> animation = controller.navigationAnimation;
animation.transitionType = operation; animation.transitionType = operation;
return animation; return animation;
} }
return nil; return nil;
} }
// 依据supportedInterfaceOrientations文档,控制旋转的代码需要写在root view controller,或者全屏的presenting controller // 依据supportedInterfaceOrientations文档,控制旋转的代码需要写在root view controller,或者全屏的presenting controller
// app的root view controller就是这个GMNavigationController,所以相关控制代码在这里。 // app的root view controller就是这个GMNavigationController,所以相关控制代码在这里。
- (BOOL)shouldAutorotate - (BOOL)shouldAutorotate {
{
return NO; return NO;
} }
- (UIInterfaceOrientationMask)supportedInterfaceOrientations - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
{
return UIInterfaceOrientationMaskPortrait; return UIInterfaceOrientationMaskPortrait;
} }
@end @end
...@@ -6,9 +6,13 @@ ...@@ -6,9 +6,13 @@
// Copyright © 2017年 更美互动信息科技有限公司. All rights reserved. // Copyright © 2017年 更美互动信息科技有限公司. All rights reserved.
// //
#import <Foundation/Foundation.h> #import "GMCustomNavigationAnimationProtocol.h"
/// 模拟 presentController 动画
/// push 时下往上的位移动画,pop 时从上往下的位移动画
@interface GMPresentAnimation : NSObject <GMControllerAnimatedTransitioning>
// 当 needMask = YES 时,会一个透明 mask 将 fromVC 与 toVC 隔开。默认为 NO。
@property (nonatomic, assign) BOOL needMask;
// 通过使用GMPresentAnimation,来自定义 push 动画
@interface GMPresentAnimation : NSObject <UIViewControllerAnimatedTransitioning>
@property(nonatomic, assign) UINavigationControllerOperation transitionType;
@end @end
...@@ -8,15 +8,19 @@ ...@@ -8,15 +8,19 @@
#import "GMPresentAnimation.h" #import "GMPresentAnimation.h"
#import "UIViewController+PushType.h" #import "UIViewController+PushType.h"
@interface GMPresentAnimation () @interface GMPresentAnimation () {
/// 与 needMask 属性配合使用。当 needMask = YES 时,_mask 将会展示在 fromVC 与 toVC 层级的中间。默认不展示。
UIView *_mask;
}
@property(nonatomic, assign) NSTimeInterval duration; @property(nonatomic, assign) NSTimeInterval duration;
@end @end
@implementation GMPresentAnimation @implementation GMPresentAnimation
@synthesize transitionType;
- (instancetype)init { - (instancetype)init {
// 默认 push 动画时间0.6
if (self = [super init]) { if (self = [super init]) {
self.duration = 0.3; self.duration = 0.3;
self.needMask = NO;
} }
return self; return self;
} }
...@@ -33,11 +37,12 @@ ...@@ -33,11 +37,12 @@
[[transitionContext containerView] addSubview:fromVC.snapshot]; [[transitionContext containerView] addSubview:fromVC.snapshot];
// 添加一个半透明的蒙层,展示在 from view 下面 // 添加一个半透明的蒙层,展示在 from view 下面
UIView *mask = [[UIView alloc] initWithFrame:bounds]; _mask = [[UIView alloc] initWithFrame:bounds];
mask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.64]; _mask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.64];
mask.alpha = 0; _mask.alpha = 0;
mask.tag = 100; if (self.needMask) {
[[transitionContext containerView] addSubview:mask]; [[transitionContext containerView] addSubview:_mask];
}
// 系统不会为我们自动添加 toView,所以我们需要自己添加,以保证 toView 的页面及动画正常展示 // 系统不会为我们自动添加 toView,所以我们需要自己添加,以保证 toView 的页面及动画正常展示
[[transitionContext containerView] addSubview:toView]; [[transitionContext containerView] addSubview:toView];
...@@ -50,7 +55,7 @@ ...@@ -50,7 +55,7 @@
// 从下到上的平移动画 // 从下到上的平移动画
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
toView.frame = bounds; toView.frame = bounds;
mask.alpha = 1; _mask.alpha = 1;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
[transitionContext completeTransition:YES]; [transitionContext completeTransition:YES];
}]; }];
...@@ -63,8 +68,8 @@ ...@@ -63,8 +68,8 @@
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
// 必须把先前的 mask view 拿出来,显示的标记为 alpha = 1,要不然会发现该蒙层是突然消失 // 必须把先前的 mask view 拿出来,显示的标记为 alpha = 1,要不然会发现该蒙层是突然消失
// 经打断点发现,其 alpha 此时为0,所以必须要设置一下 alpha = 1 // 经打断点发现,其 alpha 此时为0,所以必须要设置一下 alpha = 1
[[transitionContext containerView] viewWithTag:100].alpha = 1; _mask.alpha = 1;
//将toView加到toVC.snapshot 的的下面。 //将toView加到toVC.snapshot 的的下面。
// 不加的话,动画结束后看不到 toView // 不加的话,动画结束后看不到 toView
...@@ -77,9 +82,11 @@ ...@@ -77,9 +82,11 @@
newFrame.origin.y = CGRectGetHeight(bound); newFrame.origin.y = CGRectGetHeight(bound);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromView.frame = newFrame; fromView.frame = newFrame;
[[transitionContext containerView] viewWithTag:100].alpha = 0; _mask.alpha = 0;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
[[[transitionContext containerView] viewWithTag:100] removeFromSuperview]; if (self.needMask) {
[_mask removeFromSuperview];
}
// 动画结束后移除toVC.snapshot,留着会挡住toVC,用户无法交互 // 动画结束后移除toVC.snapshot,留着会挡住toVC,用户无法交互
[toVC.snapshot removeFromSuperview]; [toVC.snapshot removeFromSuperview];
// 需要将toVC.snapshot清空,下一次弹窗时再重新生成 snapshot,确保每一次 snapshot 为最新画面 // 需要将toVC.snapshot清空,下一次弹窗时再重新生成 snapshot,确保每一次 snapshot 为最新画面
...@@ -88,8 +95,7 @@ ...@@ -88,8 +95,7 @@
}]; }];
} }
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
{
return self.duration; return self.duration;
} }
......
...@@ -10,7 +10,5 @@ ...@@ -10,7 +10,5 @@
@interface UIViewController (Push) @interface UIViewController (Push)
@property (nonatomic, strong) UIView *snapshot; @property (nonatomic, strong) UIView *snapshot;
//push type是否是模态动画
@property (nonatomic, assign) BOOL isPresent;
- (void)popToPresentPreviousController; - (void)popToPresentPreviousController;
@end @end
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#import "UIViewController+PushType.h" #import "UIViewController+PushType.h"
#import <objc/runtime.h> #import <objc/runtime.h>
#import "GMNavigationController.h" #import "GMNavigationController.h"
#import "GMCustomNavigationAnimationProtocol.h"
@implementation UIViewController (PushType) @implementation UIViewController (PushType)
- (UIView *)snapshot - (UIView *)snapshot
...@@ -26,20 +27,11 @@ ...@@ -26,20 +27,11 @@
objc_setAssociatedObject(self, @"gmSnapshot", snapshot, OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_setAssociatedObject(self, @"gmSnapshot", snapshot, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} }
- (BOOL)isPresent { // TODO: wangyang 从 release 分支看一下此处的逻辑,查清此处应该怎么改
return [objc_getAssociatedObject(self, @"gmIsPresent") boolValue];
}
-(void)setIsPresent:(BOOL)isPresent {
objc_setAssociatedObject(self, @"gmIsPresent", [NSNumber numberWithBool:isPresent], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)popToPresentPreviousController { - (void)popToPresentPreviousController {
for (int index = 0; index < self.navigationController.viewControllers.count; index++) { for (int index = 0; index < self.navigationController.viewControllers.count; index++) {
UIViewController *controller = self.navigationController.viewControllers[index]; UIViewController *controller = self.navigationController.viewControllers[index];
if (controller.isPresent) { if ([controller conformsToProtocol:@protocol(GMCustomNavigationAnimationProtocol)]) {
GMNavigationController *nav = (GMNavigationController *)self.navigationController;
nav.needPopPresetAnimation = YES;
UIViewController *previewController = self.navigationController.viewControllers[index - 1]; UIViewController *previewController = self.navigationController.viewControllers[index - 1];
[controller.navigationController popToViewController:previewController animated:YES]; [controller.navigationController popToViewController:previewController animated:YES];
return; return;
......
//
// GMCustomNavigationAnimationProtocol.h
// Pods
//
// Created by wangyang on 2020/4/7.
//
#ifndef GMCustomNavigationAnimationProtocol_h
#define GMCustomNavigationAnimationProtocol_h
@protocol GMControllerAnimatedTransitioning <UIViewControllerAnimatedTransitioning>
@property(nonatomic, assign) UINavigationControllerOperation transitionType;
@end
@protocol GMCustomNavigationAnimationProtocol <NSObject>
@property (nonatomic, strong) id<GMControllerAnimatedTransitioning> navigationAnimation;
@end
#endif /* GMCustomNavigationAnimationProtocol_h */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment