UIView 添加导航的功能

协议文件

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动画完成");
//        }];
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值