黑魔法到底有多强大?(一)各种第三方SDK的导航条

版权声明:欢迎大家积极分享!交流。关注我~ https://blog.csdn.net/qinqi376990311/article/details/81001071

黑魔法到底有多强大?

第一篇 各种第三方SDK私有控制器的导航条

TIPS:我们可能都看过无数的文章,讲解在OC中如何实现Method Swizzling 交换方法,但是在实际应用中,好多人可能想不到这一点。所以我准备写一系列利用黑魔法才能实现的需求的文章。

我们在集成一些第三方SDK时,尤其是带有UI界面的第三方。它的导航条风格跟我们App中的不一样,我们当然希望以我们App本身的导航条风格为主了。甚至包括一些系统的界面,比如 UIImagePickerController 等。

现在我就以 融云SDK 和 UIImagePickerController 作为实例来实现这个需求。

说到这里,不得不提一下 UIAppearance 协议,这个协议的作用是设置一些全局属性,在App生命周期内,所有当前类的对象都会被设置某属性,举例说明:

[UIButton appearance].exclusiveTouch = YES;
[UITextField appearance].tintColor = kMainColor();

这表示,App内所有的按钮都不能同时点击,所有的文本输入框,光标的渲染颜色为kMainColor()
那么可能就有人说了,直接利用 [UINavigationBar appearance] 来设置不就好了?这样并不完美,这样的话所有的导航条都会变成你所设置的风格。那如果App内有多种导航条风格的话,这个方式就不合适了。

那么我们将通过黑魔法来实现这个需求,其实非常简单。
1、创建 UIViewController 的 Category ,这里我命名为 UIViewController+Swizzling
2、在 .m 中,重写 +load 方法,来 hook -viewWillAppear: 方法,为什么不hook viewDidLoad呢?有些控制器会在 appear 的时候对导航条进行修改,所以我们最终也在其本身的 -viewWillAppear: 执行完毕之后,才操作我们的导航条。上代码~

#import "UIViewController+Swizzling.h"

@implementation UIViewController (Swizzling)

+ (void)load {
    [super load];
    //原本的willAppear方法
    Method willAppearOriginal = class_getInstanceMethod([self class], @selector(viewWillAppear:));
    //我们的willAppear方法
    Method willAppearNew = class_getInstanceMethod([self class], @selector(swizzlingViewWillAppear:));
    //交换
    if (!class_addMethod([self class], @selector(viewWillAppear:), method_getImplementation(willAppearNew), method_getTypeEncoding(willAppearNew))) {
        method_exchangeImplementations(willAppearOriginal, willAppearNew);
    }
}

- (void)swizzlingViewWillAppear:(BOOL)animated {
    [self swizzlingViewWillAppear:animated];
    if ([self isMemberOfClass:NSClassFromString(@"RCPhotosPickerController")]) {
        [self configureRongCloudNavigation];
        UICollectionView *collectionView = self.view.subviews.firstObject;
        collectionView.contentInset = UIEdgeInsetsMake(kNavHeight(), 0, 0, 0);
    }
    if ([self isMemberOfClass:NSClassFromString(@"RCAlumListTableViewController")] || [self isMemberOfClass:NSClassFromString(@"RCLocationPickerViewController")] || [self isMemberOfClass:NSClassFromString(@"RCLocationViewController")] || [self isMemberOfClass:NSClassFromString(@"PUPhotoPickerHostViewController")]) {
        [self configureRongCloudNavigation];
    }
}

- (void)configureRongCloudNavigation {
    self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init];
    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:kNavImageName(@"nav_bg")] forBarMetrics:UIBarMetricsDefault];
    self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
    [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont boldSystemFontOfSize:kFontSize(18)]}];
}

这里我的项目中集成了融云SDK,但是我需要将它里面的一些界面的导航条风格跟我App本身的保持一致。

PS:那些控制器的类型如何获取?可以再hook -viewDidAppear: 方法,来NSLog类名就ok了。
这里使用 NSClassFromString() 来显示类名,因为融云本身并没有这些私有类的头文件。
里面获取到 UICollectionView 的原理跟获取类型的方式是一样的,打印所有子视图。

到这里,我们的需求就完成了,是不是非常简单呢~

猜你喜欢

转载自blog.csdn.net/qinqi376990311/article/details/81001071