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

使用协议来添加 navigion 动画

parent 3e4f79e9
......@@ -14,7 +14,7 @@ PODS:
- AFNetworking/Serialization (3.1.0)
- AFNetworking/UIKit (3.1.0):
- AFNetworking/NSURLSession
- GMBase (1.1.4):
- GMBase (1.1.5):
- GMHud
- GMJSONModel
- GMKit
......@@ -90,7 +90,7 @@ SPEC REPOS:
- GMNetService
- GMPhobos
- GMRefresh
https://github.com/cocoapods/specs.git:
https://github.com/CocoaPods/Specs.git:
- AFNetworking
- Masonry
- MBProgressHUD
......@@ -104,7 +104,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
GMBase: aa92f9e566cc5807d1c1439d3e8c588b5b413a7e
GMBase: b747cd3ecd7a5109469113cf48ce04ae6d16cdbe
GMCache: b78d8e46db864405e91d226ce640cc80d966c611
GMHud: 45aa755b72a65f89d810430336803086359c0fb1
GMJSONModel: 5e81a98de668e9f93cf6ff77869f77b0d1a806be
......@@ -120,4 +120,4 @@ SPEC CHECKSUMS:
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 @@
#import <UIKit/UIKit.h>
@interface GMNavigationController : UINavigationController
// TODO: wangyang 这个属性的历史需要找人了解清楚,缺少对应的注释
@property (nonatomic, assign) BOOL needPopPresetAnimation;
@end
......@@ -10,6 +10,8 @@
#import "UIViewController+PushType.h"
#import "GMPresentAnimation.h"
@import GMKit;
#import "GMCustomNavigationAnimationProtocol.h"
@interface GMNavigationController ()<UINavigationControllerDelegate>
......@@ -27,31 +29,28 @@
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPush && toVC.isPresent) {
GMPresentAnimation *animation = [[GMPresentAnimation alloc] init];
animation.transitionType = operation;
if (operation == UINavigationControllerOperationPush && [toVC conformsToProtocol:@protocol(GMCustomNavigationAnimationProtocol)]) {
id<GMCustomNavigationAnimationProtocol> controller = (id<GMCustomNavigationAnimationProtocol>)toVC;
id<GMControllerAnimatedTransitioning> animation = controller.navigationAnimation;
animation.transitionType = operation;
return animation;
} else if (operation == UINavigationControllerOperationPop && (fromVC.isPresent || self.needPopPresetAnimation)) {
_needPopPresetAnimation = NO;
GMPresentAnimation *animation = [[GMPresentAnimation alloc] init];
animation.transitionType = operation;
} else if (operation == UINavigationControllerOperationPop && ([fromVC conformsToProtocol:@protocol(GMCustomNavigationAnimationProtocol)] || self.needPopPresetAnimation)) {
id<GMCustomNavigationAnimationProtocol> controller = (id<GMCustomNavigationAnimationProtocol>)fromVC;
id<GMControllerAnimatedTransitioning> animation = controller.navigationAnimation;
animation.transitionType = operation;
return animation;
}
return nil;
}
// 依据supportedInterfaceOrientations文档,控制旋转的代码需要写在root view controller,或者全屏的presenting controller
// app的root view controller就是这个GMNavigationController,所以相关控制代码在这里。
- (BOOL)shouldAutorotate
{
- (BOOL)shouldAutorotate {
return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
@end
......@@ -6,9 +6,13 @@
// 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
......@@ -8,15 +8,19 @@
#import "GMPresentAnimation.h"
#import "UIViewController+PushType.h"
@interface GMPresentAnimation ()
@interface GMPresentAnimation () {
/// 与 needMask 属性配合使用。当 needMask = YES 时,_mask 将会展示在 fromVC 与 toVC 层级的中间。默认不展示。
UIView *_mask;
}
@property(nonatomic, assign) NSTimeInterval duration;
@end
@implementation GMPresentAnimation
@synthesize transitionType;
- (instancetype)init {
// 默认 push 动画时间0.6
if (self = [super init]) {
self.duration = 0.3;
self.needMask = NO;
}
return self;
}
......@@ -33,11 +37,12 @@
[[transitionContext containerView] addSubview:fromVC.snapshot];
// 添加一个半透明的蒙层,展示在 from view 下面
UIView *mask = [[UIView alloc] initWithFrame:bounds];
mask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.64];
mask.alpha = 0;
mask.tag = 100;
[[transitionContext containerView] addSubview:mask];
_mask = [[UIView alloc] initWithFrame:bounds];
_mask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.64];
_mask.alpha = 0;
if (self.needMask) {
[[transitionContext containerView] addSubview:_mask];
}
// 系统不会为我们自动添加 toView,所以我们需要自己添加,以保证 toView 的页面及动画正常展示
[[transitionContext containerView] addSubview:toView];
......@@ -50,7 +55,7 @@
// 从下到上的平移动画
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
toView.frame = bounds;
mask.alpha = 1;
_mask.alpha = 1;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
......@@ -63,8 +68,8 @@
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
// 必须把先前的 mask view 拿出来,显示的标记为 alpha = 1,要不然会发现该蒙层是突然消失
// 经打断点发现,其 alpha 此时为0,所以必须要设置一下 alpha = 1
[[transitionContext containerView] viewWithTag:100].alpha = 1;
// 经打断点发现,其 alpha 此时为0,所以必须要设置一下 alpha = 1
_mask.alpha = 1;
//将toView加到toVC.snapshot 的的下面。
// 不加的话,动画结束后看不到 toView
......@@ -77,9 +82,11 @@
newFrame.origin.y = CGRectGetHeight(bound);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromView.frame = newFrame;
[[transitionContext containerView] viewWithTag:100].alpha = 0;
_mask.alpha = 0;
} completion:^(BOOL finished) {
[[[transitionContext containerView] viewWithTag:100] removeFromSuperview];
if (self.needMask) {
[_mask removeFromSuperview];
}
// 动画结束后移除toVC.snapshot,留着会挡住toVC,用户无法交互
[toVC.snapshot removeFromSuperview];
// 需要将toVC.snapshot清空,下一次弹窗时再重新生成 snapshot,确保每一次 snapshot 为最新画面
......@@ -88,8 +95,7 @@
}];
}
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
{
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
return self.duration;
}
......
......@@ -10,7 +10,5 @@
@interface UIViewController (Push)
@property (nonatomic, strong) UIView *snapshot;
//push type是否是模态动画
@property (nonatomic, assign) BOOL isPresent;
- (void)popToPresentPreviousController;
@end
......@@ -9,6 +9,7 @@
#import "UIViewController+PushType.h"
#import <objc/runtime.h>
#import "GMNavigationController.h"
#import "GMCustomNavigationAnimationProtocol.h"
@implementation UIViewController (PushType)
- (UIView *)snapshot
......@@ -26,20 +27,11 @@
objc_setAssociatedObject(self, @"gmSnapshot", snapshot, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isPresent {
return [objc_getAssociatedObject(self, @"gmIsPresent") boolValue];
}
-(void)setIsPresent:(BOOL)isPresent {
objc_setAssociatedObject(self, @"gmIsPresent", [NSNumber numberWithBool:isPresent], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// TODO: wangyang 从 release 分支看一下此处的逻辑,查清此处应该怎么改
- (void)popToPresentPreviousController {
for (int index = 0; index < self.navigationController.viewControllers.count; index++) {
UIViewController *controller = self.navigationController.viewControllers[index];
if (controller.isPresent) {
GMNavigationController *nav = (GMNavigationController *)self.navigationController;
nav.needPopPresetAnimation = YES;
if ([controller conformsToProtocol:@protocol(GMCustomNavigationAnimationProtocol)]) {
UIViewController *previewController = self.navigationController.viewControllers[index - 1];
[controller.navigationController popToViewController:previewController animated:YES];
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