当前主流的导航控制器手势滑动效果

效果图



技术点

1.当滑动时的上一界面view是怎么出来的

2.每一个控制器界面的导航栏颜色为什么不同,pop回来还不会变

github地址https://github.com/lsmakethebest/LSNavigationBarTransition

具体实现

思路就是重写父类的push pop方法在push时对屏幕截图,滑动屏幕时显示截的图,pop时移除截图,多次push利用栈的原理管理截图

1.首先写个UINavigationController分类


load方法是在程序第一次启动时,该类加载到内存中会调用,在此交换方法

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        LSSwizzleInstanceMethod([self class],
            @selector(viewDidLoad),
            @selector(ls_navigaionControllerViewDidLoad));
        LSSwizzleInstanceMethod([self class],
            @selector(pushViewController:animated:),
            @selector(ls_pushViewController:animated:));

        LSSwizzleInstanceMethod([self class],
            @selector(popViewControllerAnimated:),
            @selector(ls_popViewControllerAnimated:));

        LSSwizzleInstanceMethod([self class],
            @selector(popToViewController:animated:),
            @selector(ls_popToViewController:animated:));

        LSSwizzleInstanceMethod([self class],
            @selector(popToRootViewControllerAnimated:),
            @selector(ls_popToRootViewControllerAnimated:));
    });
}


方法交换内部实现,不懂可以参考前几篇文章runtime学习

void LSSwizzleInstanceMethod(Class class, SEL originalSelector, SEL swizzledSelector)
{
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    BOOL didAddMethod = class_addMethod(class,
        originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(class,
            swizzledSelector,
            method_getImplementation(originalMethod),
            method_getTypeEncoding(originalMethod));
    }
    else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}
交换完方法内部实现,因为系统自带个边缘手势禁用,添加拖拽手势

- (void)ls_navigaionControllerViewDidLoad
{
    [self ls_navigaionControllerViewDidLoad];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        UIGestureRecognizer* gesture = self.interactivePopGestureRecognizer;
        gesture.enabled = NO;
        UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
        [self.view addGestureRecognizer:pan];
        pan.delegate = self;

    });
}


处理手势方法,就是手势开始时显示截图

滑动时移动navigationController.view的frame同时移动截图的frame

结束时判断比例是还原位置还是pop

- (void)handleGesture:(UIScreenEdgePanGestureRecognizer*)recognizer
{
    CGFloat x = [recognizer translationInView:recognizer.view].x;
    if (x < 0) {
        self.frontControllerView.transform = CGAffineTransformIdentity;
        self.view.transform = CGAffineTransformIdentity;
        self.frontControllerView.scaleAlpha=0;
        return;
    }
    self.topViewController.view.userInteractionEnabled = NO;
    switch (recognizer.state) {
    case UIGestureRecognizerStateBegan: {
        if (self.frontControllerView == nil) {
            LSImageView* imageView = [[LSImageView alloc] init];
            imageView.backgroundColor = [UIColor whiteColor];
            imageView.image = [[LSStack sharedInstance] getTop];
            CGRect frame = self.view.window.bounds;
            frame.origin.x = -LSScreenWidth * LSRightScale;
            imageView.frame = frame;
            [self.view.window insertSubview:imageView belowSubview:self.view];
            self.frontControllerView = imageView;
        }
        break;
    }
    case UIGestureRecognizerStateChanged: {
        ;
        self.frontControllerView.transform = CGAffineTransformMakeTranslation(x * LSRightScale, 0);
        self.view.transform = CGAffineTransformMakeTranslation(x, 0);
        self.frontControllerView.scaleAlpha=x/LSScreenWidth;
        break;
    }
    case UIGestureRecognizerStateCancelled:
    case UIGestureRecognizerStateEnded: {
        //打开用户交互
        self.topViewController.view.userInteractionEnabled = YES;
        if (x >= self.view.frame.size.width / Scale) {
            [UIView animateWithDuration:0.2 animations:^{
                self.frontControllerView.transform = CGAffineTransformMakeTranslation(LSScreenWidth * LSRightScale, 0);
                self.frontControllerView.scaleAlpha=1;
                self.view.transform = CGAffineTransformMakeTranslation(LSScreenWidth, 0);
            }
                completion:^(BOOL finished) {
                    [self.frontControllerView removeFromSuperview];
                    self.frontControllerView = nil;
                    self.view.transform = CGAffineTransformIdentity;
                    [self popViewControllerAnimated:NO];
                }];
        }
        else {
            [UIView animateWithDuration:0.2 animations:^{
                self.frontControllerView.transform = CGAffineTransformIdentity;
                self.frontControllerView.scaleAlpha=0;
                self.view.transform = CGAffineTransformIdentity;
            }];
        }
    } break;
    default:
        break;
    }
}


2.实现每一控制器界面导航栏颜色不同

系统导航栏默认有个背景_backgroundView负责存放背景image等

我们可以把它隐藏,在控制器将要显示时在navigationbar上添加一个和他类似的view,这样每一个界面都有这样一个view即使pop回去导航栏颜色就不会变了,因为显示的是自己添加的而不是系统的

交换方法

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        LSSwizzleInstanceMethod([self class],
                                @selector(viewDidLoad),
                                @selector(ls_viewDidLoad));
        LSSwizzleInstanceMethod([self class],
                                @selector(viewWillLayoutSubviews),
                                @selector(ls_viewWillLayoutSubviews));
        
    });
}
- (void)ls_viewDidLoad
{
    [self ls_viewDidLoad];
    //此处设置全局状态栏背景 其他控制器想修改UINavigationBar背景色可以在viewDidLoad里修改,不会影响其他控制器UINavigationBar背景色
    [self.navigationController.navigationBar setBackgroundImage:[[UIColor colorWithRed:0.671 green:0.184 blue:1.000 alpha:1.000] imageWithColor] forBarMetrics:UIBarMetricsDefault];
}
- (void)ls_viewWillLayoutSubviews
{
    if (self.navigationController.viewControllers.count) {
        
        if (!self.ls_navigationBar) {
            [self ls_addNavigationBarIfNeed];
            self.ls_prefersNavigationBarBackgroundViewHidden = YES;
        }
    }
    [self ls_viewWillLayoutSubviews];
}


默认分类不能添加属性而是生成getter setter方法但是我们可以利用runtine动态添加关联对象来添加属性
#import <UIKit/UIKit.h>

@interface UIViewController (LSNavigationBarTransition)

@property (nonatomic, strong) UINavigationBar *ls_navigationBar;
@property (nonatomic, assign) BOOL ls_prefersNavigationBarBackgroundViewHidden;
-(void)ls_addNavigationBarIfNeed;
@end


动态添加属性
- (void)setLs_navigationBar:(UINavigationBar*)ls_navigationBar
{
    objc_setAssociatedObject(self, @selector(ls_navigationBar), ls_navigationBar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)ls_prefersNavigationBarBackgroundViewHidden
{
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

显示时添加自定义的view
- (void)ls_viewWillLayoutSubviews
{
    if (self.navigationController.viewControllers.count) {
        
        if (!self.ls_navigationBar) {
            [self ls_addNavigationBarIfNeed];
            self.ls_prefersNavigationBarBackgroundViewHidden = YES;//隐藏系统的
        }
    }
    [self ls_viewWillLayoutSubviews];
}

- (BOOL)ls_prefersNavigationBarBackgroundViewHidden
{
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setLs_prefersNavigationBarBackgroundViewHidden:(BOOL)hidden
{
    [[self.navigationController.navigationBar valueForKey:@"_backgroundView"]
     setHidden:hidden];
    objc_setAssociatedObject(self, @selector(ls_prefersNavigationBarBackgroundViewHidden), @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


添加方法实现
- (void)ls_addNavigationBarIfNeed
{
    if (!self.view.window) {
        return;
    }
    if (!self.navigationController.navigationBar) {
        return;
    }
    UIView* backgroundView = [self.navigationController.navigationBar valueForKey:@"_backgroundView"];
    CGRect rect = [backgroundView.superview convertRect:backgroundView.frame toView:self.view];
    UINavigationBar* bar = [[UINavigationBar alloc] initWithFrame:rect];
    bar.barStyle = self.navigationController.navigationBar.barStyle;
    if (bar.translucent != self.navigationController.navigationBar.translucent) {
        bar.translucent = self.navigationController.navigationBar.translucent;
    }
    bar.barTintColor = self.navigationController.navigationBar.barTintColor;
    [bar setBackgroundImage:[self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault];
    bar.shadowImage = self.navigationController.navigationBar.shadowImage;
    bar.backgroundColor = self.navigationController.navigationBar.backgroundColor;
    bar.translucent = self.navigationController.navigationBar.translucent;
    self.ls_navigationBar = bar;
    if (!self.navigationController.navigationBarHidden) {
        [self.view addSubview:self.ls_navigationBar];
    }
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值