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

上一篇已经完成了导航栏效果的渐变。但是侧滑返回的时候,导航栏从不透明界面跳转到透明界面时,总是会突变,感觉很膈应。这里将用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链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容