上一篇已經(jīng)完成了導(dǎo)航欄效果的漸變垄惧。但是側(cè)滑返回的時(shí)候遏片,導(dǎo)航欄從不透明界面跳轉(zhuǎn)到透明界面時(shí)薯嗤,總是會(huì)突變顽爹,感覺(jué)很膈應(yīng)。這里將用runtime截獲系統(tǒng)的方法來(lái)完成對(duì)導(dǎo)航欄動(dòng)畫的逆襲
由于監(jiān)測(cè)側(cè)滑手勢(shì)的方法是系統(tǒng)管理的骆姐,并沒(méi)有暴露給我們镜粤。所以我們先要做一些準(zhǔn)備工作:
利用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, 參數(shù)個(gè)數(shù):%d, 參數(shù)類型: %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導(dǎo)航欄的alpha值:
這里有兩種方法:
方法一:所有控制都繼承一個(gè)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中的方法的時(shí)候點(diǎn)到UIViewControllerAnimatedTransitioning這個(gè)里面翻閱了一下發(fā)現(xiàn)有兩個(gè)協(xié)議里面都有下面幾個(gè)關(guān)于 過(guò)度交互 的方法,于是互在打印的方法數(shù)組里也查下發(fā)現(xiàn)也有于是就嘗試了一下晤揣,發(fā)現(xiàn)果然是這幾個(gè)方法:
- (void)updateInteractiveTransition:(CGFloat)percentComplete;
- (void)cancelInteractiveTransition;
- (void)finishInteractiveTransition;
這里參數(shù)類型 “v24@0:8d16” 這里v表示返回值類型為void桥爽,后面的d16表示參數(shù)類型為double類型,與上面方法一致昧识。
然后我們對(duì)上面幾個(gè)方法進(jìn)行方法交換钠四,系統(tǒng)相應(yīng)該方法的時(shí)候能完成自己想做的事:
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);
}
/*
*此方法在手指在屏幕上,側(cè)滑返回過(guò)程中調(diào)用跪楞。
**/
- (void)yhh_updateInteractiveTransition:(CGFloat)percentComplete {
// percentComplete是當(dāng)前移動(dòng)距離與屏幕的比例
NSLog(@"%f", percentComplete);
UIViewController *topvc = self.topViewController;
id <UIViewControllerTransitionCoordinator> tran = topvc.transitionCoordinator;
// fromvc 從哪個(gè)控制來(lái)缀去, tovc去哪個(gè)控制器
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];
}
/*
*側(cè)滑返回手勢(shì)松手時(shí)不能pop回上一界面時(shí)調(diào)用(滑動(dòng)小于一半屏幕)。
*此處context由于不知道是什么類型甸祭,里面有什么屬性缕碎,成員變量所以用了runtime查看context及context的superclass.
*在context.superclass里面發(fā)現(xiàn)了_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];
}
/*
*側(cè)滑返回手勢(shì)松手時(shí)能夠pop回上一界面時(shí)調(diào)用(滑動(dòng)大于一半屏幕)
**/
- (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];
}
設(shè)置導(dǎo)航欄alpha值
/*
*這里由于直接修改UINavigationBar.subviews[0]的alpha值會(huì)導(dǎo)致漸變過(guò)程中沒(méi)有毛玻璃效果。
*#所以利用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;
}
}
最終效果:
附上gitub鏈接