协议文件
QIENavigationViewAnimation.h
//
// QIENavigationViewAnimation.h
// Demo_03
//
// Created by 李超群 on 2021/6/8.
// Copyright © 2021 李超群. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#pragma mark - view显示和隐藏需要遵守的协议
@protocol QIENavigationViewAnimation <NSObject>
/** 显示的动画 */
-(void)showNavigationViewInView:(UIView *)superView animation:(BOOL)animated completion:(void (^__nullable)(void))completion;
/** 隐藏的动画 */
-(void)hiddenNavigationViewWithAnimation:(BOOL)animated completion:(void (^__nullable)(void))completion;
@end
NS_ASSUME_NONNULL_END
声明
UIView+QIENavigationView.h
//
// UIView+QIENavigationView.h
// Demo_03
//
// Created by 李超群 on 2021/6/8.
// Copyright © 2021 李超群. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "QIENavigationViewAnimation.h"
NS_ASSUME_NONNULL_BEGIN
#pragma mark - 导航的view
@interface QIENavigationView : NSObject
/** 最上层的view */
@property(nullable, nonatomic,readonly,strong) UIView <QIENavigationViewAnimation> *topView;
/** 导航栈内的view */
@property(nonatomic,copy) NSArray<__kindof UIView <QIENavigationViewAnimation> *> *views;
/** 初始化方法 */
- (instancetype )initWithRootView:(UIView *)rootView;
/** 跳转显示某个view */
- (void)pushViewController:(UIView <QIENavigationViewAnimation> *)view animated:(BOOL)animated completion:(void (^__nullable)(void))completion;
/** pop 掉上一个视图 */
- (void)popViewAnimated:(BOOL)animated completion:(void (^__nullable)(void))completion;
/** pop到指定视图 */
- (void)popToView:(UIView *_Nonnull)view animated:(BOOL)animated completion:(void (^__nullable)(void))completion;
/** pop到根视图 */
- (void)popToRootViewControllerAnimated:(BOOL)animated completion:(void (^__nullable)(void))completion;
@end
#pragma mark - view添加的导航的类别
@interface UIView (QIENavigationView)
/** 导航的功能 */
@property(nonatomic, strong) QIENavigationView * navigationView;
/** 给view添加导航功能 */
-(void)addNavigation;
@end
NS_ASSUME_NONNULL_END
实现文件
UIView+QIENavigationView.m
//
// UIView+QIENavigationView.m
// Demo_03
//
// Created by 李超群 on 2021/6/8.
// Copyright © 2021 李超群. All rights reserved.
//
#import "UIView+QIENavigationView.h"
#import <objc/message.h>
#pragma mark - 栈
@interface QIEStack <ObjectType> : NSObject
/** 进栈 */
-(void)push:(ObjectType)obj;
/** 出栈 */
-(ObjectType)pop;
/** 栈顶元素 */
-(ObjectType)top;
/** 清空栈 */
-(void)clear;
/** 栈的大小 */
-(int)size;
/** 栈是不是空的 */
-(BOOL)isEmpty;
@end
@implementation QIEStack{
@public
NSMutableArray *_stack;
}
- (instancetype)init
{
self = [super init];
if (self) {
_stack = [NSMutableArray array];
}
return self;
}
/** 进栈 */
-(void)push:(id)obj{
[_stack addObject:obj];
}
/** 出栈 */
-(id)pop{
if (_stack.count) {
id temp = [self top];
[_stack removeObject:temp];
return temp;
}
return nil;
}
/** 栈顶元素 */
-(id)top{
if (_stack.count) {
return _stack.lastObject;
}
return nil;
}
/** 清空栈 */
-(void)clear{
[_stack removeAllObjects];
}
/** 栈的大小 */
-(int)size{
return (int)_stack.count;
}
/** 栈是不是空的 */
-(BOOL)isEmpty{
return (self.size == 0);
}
// - MARK: <-- 生命周期 -->
- (void)dealloc{
NSLog(@"// - 李超群 console [log] ...dealloc QIEStack");
}
@end
#pragma mark - 释放监听
@interface QIEReleaseListener : NSObject @end
@implementation QIEReleaseListener{
NSMutableArray<QIENavigationView *> *_autoReleaseObjs;
}
- (NSMutableArray<QIENavigationView *> *)autoReleaseObjs{
if (!_autoReleaseObjs) {
_autoReleaseObjs = [[NSMutableArray alloc] init];
}
return _autoReleaseObjs;
}
/** 生命周期 */
- (void)dealloc{
for (QIENavigationView *nav in self.autoReleaseObjs) {
[nav popToRootViewControllerAnimated:NO completion:nil];
}
}
@end
@implementation UIView (QIEAutoRelease)
-(void)autoRelease{
// - 属性绑定
QIEReleaseListener * releaseListener = objc_getAssociatedObject(self, @selector(autoRelease));
if (!releaseListener) {
releaseListener = [[QIEReleaseListener alloc] init];
objc_setAssociatedObject(self, @selector(autoRelease), releaseListener, OBJC_ASSOCIATION_RETAIN);
}
// - 绑定需要监听被释放的对象是自己
[releaseListener.autoReleaseObjs addObject:self.navigationView];
}
@end
#pragma mark - 导航的view
@interface QIENavigationView ()
/** 根视图 */
@property (nonatomic, weak) UIView *rootView;
/** 导航栈 */
@property (nonatomic, strong) QIEStack <UIView <QIENavigationViewAnimation> *> *viewStack;
@end
@implementation QIENavigationView
// - MARK: <-- 内部调用的方法 -->
-(BOOL)_view:(UIView *)view shouldImplementSel:(SEL)sel{
// - view不存在
if (!view) return NO;
// - vie存在但没有实现方法
if(![view respondsToSelector:sel]){
#if DEBUG
@throw [NSException exceptionWithName:@"程序被终止" reason:[NSString stringWithFormat:@"view:%@ 需要实现%@方法",view, NSStringFromSelector(sel)] userInfo:nil];
#endif
return NO;
}
// - view实现了方法
return YES;
}
// - MARK: <-- 外部接口的方法 -->
/** 初始化 */
- (instancetype)initWithRootView:(UIView *)rootView{
if (self = [super init]) {
self.viewStack = [[QIEStack alloc]init];
rootView.navigationView = self;
self.rootView = rootView;
[rootView autoRelease];
}
return self;
}
/** 栈顶的view */
- (UIView<QIENavigationViewAnimation> *)topView{
return [_viewStack top];
}
/** 导航站内的所有的view */
- (NSArray<__kindof UIView<QIENavigationViewAnimation> *> *)views{
return [self.viewStack->_stack copy];
}
/** 重新设置导航的每个view */
- (void)setViews:(NSArray<__kindof UIView<QIENavigationViewAnimation> *> *)views{
// - 全部退出栈
[self popToRootViewControllerAnimated:NO completion:nil];
// - 将views中的视图重新入栈
for (UIView<QIENavigationViewAnimation> *tempV in views) {
[self pushViewController:tempV animated:NO completion:nil];
}
}
/** 跳转显示某个view */
- (void)pushViewController:(UIView<QIENavigationViewAnimation> *)view animated:(BOOL)animated completion:(void (^)(void))completion{
[self.viewStack push:view];
view.navigationView = self;
// - viwe 需要实现代理方法
if ([self _view:view shouldImplementSel:@selector(showNavigationViewInView:animation:completion:)]) {
[view showNavigationViewInView:self.rootView animation:animated completion:completion];
}
}
/** pop 掉上一个视图 */
- (void)popViewAnimated:(BOOL)animated completion:(void (^)(void))completion{
UIView<QIENavigationViewAnimation> *tempV = [self.viewStack pop];
if (!tempV) return;
// - tempV 需要实现代理方法
if ([self _view:tempV shouldImplementSel:@selector(hiddenNavigationViewWithAnimation:completion:)]) {
[tempV hiddenNavigationViewWithAnimation:animated completion:completion];
}
}
/** pop到指定视图 */
- (void)popToView:(UIView *)view animated:(BOOL)animated completion:(void (^)(void))completion{
UIView<QIENavigationViewAnimation> *tempV = [self.viewStack top];
// - 空栈不处理
if (!tempV) return;
// - 栈顶就是需要的view
if (tempV == view) return;
// - 遍历到需要的view, 将中间的其他view都hidden
tempV = [self.viewStack pop];
while ([self.viewStack top] != view) {
UIView<QIENavigationViewAnimation> *v = [self.viewStack pop];
if ([self _view:v shouldImplementSel:@selector(hiddenNavigationViewWithAnimation:completion:)]) {
[v hiddenNavigationViewWithAnimation:NO completion:nil];
}
}
popToView:
if ([self _view:tempV shouldImplementSel:@selector(hiddenNavigationViewWithAnimation:completion:)]) {
[tempV hiddenNavigationViewWithAnimation:animated completion:completion];
}
}
/** pop到根视图 */
- (void)popToRootViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion{
UIView<QIENavigationViewAnimation> *tempV = [self.viewStack pop];
if (!tempV) return;
while (![self.viewStack isEmpty]) {
UIView<QIENavigationViewAnimation> *v = [self.viewStack pop];
if ([self _view:v shouldImplementSel:@selector(hiddenNavigationViewWithAnimation:completion:)]) {
[v hiddenNavigationViewWithAnimation:NO completion:nil];
}
}
if ([self _view:tempV shouldImplementSel:@selector(hiddenNavigationViewWithAnimation:completion:)]) {
[tempV hiddenNavigationViewWithAnimation:animated completion:completion];
}
}
// - MARK: <-- 生命周期 -->
- (void)dealloc{
NSLog(@"// - 李超群 console [log] ...dealloc QIENavigationView ");
}
@end
#pragma mark - view添加类别方法
@implementation UIView (QIENavigationView)
// - MARK: <-- runtime 绑定 QIENavigationView -->
/** 绑定属性 */
- (void)setNavigationView:(QIENavigationView *)navigationView{
objc_setAssociatedObject(self, @selector(navigationView), navigationView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (QIENavigationView *)navigationView{
QIENavigationView *navigationView = objc_getAssociatedObject(self, @selector(navigationView));
if(!navigationView){
UIView* supView = self.superview;
while (supView != nil) {
navigationView = supView.navigationView;
if (navigationView) {
return navigationView;
}
supView = supView.superview;
}
}
return navigationView;
}
/** 给view添加导航功能 */
-(void)addNavigation{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused"
[[QIENavigationView alloc]initWithRootView:self];
#pragma clang diagnostic pop
}
@end
使用方式
- (void)viewDidLoad {
[super viewDidLoad];
QGView *v = [[QGView alloc]init];
v.backgroundColor = [UIColor redColor];
v.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:v];
[v addNavigation];
self.v = v;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
static int a = 0;
if ([[touches anyObject] locationInView:self.view].x < self.view.frame.size.width / 2) {
QGView *v = [[QGView alloc]initWithFrame:CGRectMake(0, 200, 100, 100)];
v.text = @(a).stringValue;
a++;
[self.v.navigationView pushViewController:v animated:YES completion:^{
NSLog(@"// - 李超群 console [error] ... push动画完成");
}];
}else{
[self.v.navigationView popViewAnimated:YES completion:^{
NSLog(@"// - 李超群 console [error] ... pop动画完成");
}];
// [self.v.navigationView popToRootViewControllerAnimated:YES completion:^{
// NSLog(@"// - 李超群 console [error] ... pop动画完成");
// }];
//
// NSArray *ar = self.v.navigationView.views;
// [self.v.navigationView popToView:ar[3] animated:YES completion:^{
// NSLog(@"// - 李超群 console [error] ... pop动画完成");
// }];
}
}