美文网首页零碎知识点iOSotherS
IOS导航栏透明渐变效果二(导航栏侧滑渐变效果)

IOS导航栏透明渐变效果二(导航栏侧滑渐变效果)

作者: 损失了成吨智商的小海Jy | 来源:发表于2017-07-19 09:25 被阅读363次

上一篇已经完成了导航栏效果的渐变。但是侧滑返回的时候,导航栏从不透明界面跳转到透明界面时,总是会突变,感觉很膈应。这里将用runtime截获系统的方法来完成对导航栏动画的逆袭

由于监测侧滑手势的方法是系统管理的,并没有暴露给我们。所以我们先要做一些准备工作:

利用Runtime 获取方法、属性、成员属性。

- (NSArray *)getAllMethods {
    unsigned int count = 0;
    Method *list = class_copyMethodList([self class], &count);
    
    NSMutableArray *methodArray = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        Method method = list[i];
        
        SEL name = method_getName(method);
        unsigned int params = method_getNumberOfArguments(method);
        const char *encode = method_getTypeEncoding(method);
        const char *name_c = sel_getName(name);
        
        NSString *str = [NSString stringWithFormat:@"方法名:%s, 参数个数:%d, 参数类型: %s", name_c, params, encode];
        [methodArray addObject:str];
    }
    free(list);
    return methodArray;
}

- (NSArray *)getAllProperties
{
    unsigned int count = 0;
    objc_property_t *properties  =class_copyPropertyList([self class], &count);
    
    NSMutableArray *propertiesArray = [NSMutableArray arrayWithCapacity:count];
    
    for (int i = 0; i < count ; i++)
    {
        objc_property_t property = properties[i];
        const char* propertyName =property_getName(property);
        [propertiesArray addObject: [NSString stringWithUTF8String:propertyName]];
    }
    free(properties);
    return propertiesArray;
}

- (NSArray *)getAllIvars {
    
    unsigned int count = 0;

    NSMutableArray *ivarArray = [NSMutableArray array];
    
    Ivar *ivars = class_copyIvarList([self class], &count);
    
    for (int i = 0; i< count; i++) {
        Ivar ivar = ivars[i];
        
        const char *name = ivar_getName(ivar);
        const char *encode = ivar_getTypeEncoding(ivar);
        NSString *str = [NSString stringWithFormat:@"%s, %s", name, encode];
        
        [ivarArray addObject:str];
    }
    free(ivars);
    return ivarArray;
}

保存UIViewController导航栏的alpha值:

这里有两种方法:

方法一:所有控制都继承一个RootController。在RootController里面添加alpha属性。
方法二:添加UIViewController的Category,在Category里面添加alpha属性,但是需要runtime重写set 和 get方法。

static NSString *alphaKey = @"alphaKey";

@implementation UIViewController (alpha)

@dynamic navAlpha;

- (CGFloat)navAlpha {
    CGFloat alpha = [objc_getAssociatedObject(self, &alphaKey) floatValue];
    return alpha;
}

- (void)setNavAlpha:(CGFloat)navAlpha {
    objc_setAssociatedObject(self, &alphaKey, @(navAlpha), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self setNavigationBarAlpha:navAlpha];
}

获取UINavigationController方法列表:

NSArray *methodArr = [UINavigationController getAllMethods];
NSLog(@"%@",methodArr);

由于之前自己找UINavigationController中的方法的时候点到UIViewControllerAnimatedTransitioning这个里面翻阅了一下发现有两个协议里面都有下面几个关于 过度交互 的方法,于是互在打印的方法数组里也查下发现也有于是就尝试了一下,发现果然是这几个方法:

- (void)updateInteractiveTransition:(CGFloat)percentComplete;
- (void)cancelInteractiveTransition;
- (void)finishInteractiveTransition;
UINavigationController 过度交互.png

这里参数类型 “v24@0:8d16” 这里v表示返回值类型为void,后面的d16表示参数类型为double类型,与上面方法一致。

然后我们对上面几个方法进行方法交换,系统相应该方法的时候能完成自己想做的事:

NSArray *arr = @[@"_updateInteractiveTransition:", @"_cancelInteractiveTransition:transitionContext:", @"_finishInteractiveTransition:transitionContext:"];
    for (int i = 0; i < arr.count; i++) {
        NSString *mySel = [NSString stringWithFormat:@"yhh%@", arr[i]];
        Method sysMethod = class_getInstanceMethod(self, NSSelectorFromString(arr[i]));
        Method myMethod = class_getInstanceMethod(self, NSSelectorFromString(mySel));
        method_exchangeImplementations(sysMethod, myMethod);
    }
/*
 *此方法在手指在屏幕上,侧滑返回过程中调用。
**/
- (void)yhh_updateInteractiveTransition:(CGFloat)percentComplete {
    // percentComplete是当前移动距离与屏幕的比例
    NSLog(@"%f", percentComplete);
    UIViewController *topvc = self.topViewController;
    
    id <UIViewControllerTransitionCoordinator> tran = topvc.transitionCoordinator;
    // fromvc 从哪个控制来, tovc去哪个控制器
    UIViewController *fromvc = [tran viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *tovc = [tran viewControllerForKey:UITransitionContextToViewControllerKey];
    NSLog(@"fromvc:%f  -----tovc:%f", fromvc.navAlpha, tovc.navAlpha);
    
    CGFloat alpha = fromvc.navAlpha - (fromvc.navAlpha - tovc.navAlpha) * percentComplete;
    [self setNavigationBarAlpha:alpha];
    [self yhh_updateInteractiveTransition:percentComplete];
}
/*
 *侧滑返回手势松手时不能pop回上一界面时调用(滑动小于一半屏幕)。
 *此处context由于不知道是什么类型,里面有什么属性,成员变量所以用了runtime查看context及context的superclass.
 *在context.superclass里面发现了_duration 成员变量利用kvc拿到_duration的值使用
**/
- (void)yhh_cancelInteractiveTransition:(CGFloat)percentComplete transitionContext:(id)context {
    UIViewController *fromvc = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
    
//    NSLog(@"%@", [[context superclass] getAllProperties]);
//    NSLog(@"%f-%f--%@", percentComplete, [[context valueForKey:@"_duration"] floatValue] , NSStringFromClass([context class]));
    
    [UIView animateWithDuration:[[context valueForKey:@"_duration"] floatValue] * percentComplete animations:^{
        [self setNavigationBarAlpha:fromvc.navAlpha];
    }];
    [self yhh_cancelInteractiveTransition:percentComplete transitionContext:context];
}


取消手势时context内部属性.png 取消手势时context_superclass内部属性.png
 /*
 *侧滑返回手势松手时能够pop回上一界面时调用(滑动大于一半屏幕)
**/
- (void)yhh_finishInteractiveTransition:(CGFloat)percentComplete transitionContext:(id)context {
    UIViewController *tovc = [context viewControllerForKey:UITransitionContextToViewControllerKey];
    
    [UIView animateWithDuration:[[context valueForKey:@"_duration"] floatValue]*(1 - percentComplete) animations:^{
        [self setNavigationBarAlpha:tovc.navAlpha];
    }];
    [self yhh_finishInteractiveTransition:percentComplete transitionContext:context];
}

设置导航栏alpha值

/*
 *这里由于直接修改UINavigationBar.subviews[0]的alpha值会导致渐变过程中没有毛玻璃效果。
 *#所以利用runtime查看成员变量拿到然后kvc拿到_backgroundEffectView修改其alpha
**/

- (void)setNavigationBarAlpha:(CGFloat)alpha {
    UIView *backView = self.navigationBar.subviews[0];
    NSLog(@"%s, %f", __func__, alpha);
    UIView *shadow = [backView valueForKey:@"_shadowView"];
    if (shadow) {
        shadow.alpha = alpha;
    }
    
    UIView *effectView = [backView valueForKey:@"_backgroundEffectView"];
    if (effectView) {
        effectView.alpha = alpha;
    }
}

最终效果:

侧滑导航栏渐变.gif
附上gitub链接

相关文章

网友评论

本文标题:IOS导航栏透明渐变效果二(导航栏侧滑渐变效果)

本文链接:https://www.haomeiwen.com/subject/aynkkxtx.html