文章目录
  1. 1. 一、swizzle
    1. 1.1. 1. 给系统类增加成员变量
    2. 1.2. 2. 调换IMP
    3. 1.3. 3. 动态增加方法
    4. 1.4. 4. 获取某个类的成员变量或属性
    5. 1.5. 5. 获取成员函数
  2. 2. 二、基础换主题思路
    1. 2.1. 1. 颜色的替换
    2. 2.2. 2. 图片替换
  3. 3. 三、优化技巧
    1. 3.1. 第一步. 在UIView中
    2. 3.2. 第二步. 由于其他子View如UILabel都继承自UIView,这里只举一个例子,UILabel的写法:
    3. 3.3. 第三步. 使用方法:
  4. 4. 小结:
  5. 5. demo下载链接:
  6. 6. 参考链接:

一、swizzle

简单来说,swizzle有如下几个常用的方法

1. 给系统类增加成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@interface UILabel (Associate)
- (nonatomic, strong) UIColor *FlashColor;
@end

#import "UILabel+Associate.h"
@implementation UILabel (Associate)

static char flashColorKey;
- (void) setFlashColor:(UIColor *) flashColor{
objc_setAssociatedObject(self, &flashColorKey, flashColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIColor *) getFlashColor{
return objc_getAssociatedObject(self, &flashColorKey);
}
@end

2. 调换IMP

1
2
3
4
5
6
7
+ (void)swizzleInstanceMethod:(Class)class originSelector:	(SEL)originSelector otherSelector:(SEL)otherSelector
{
Method otherMehtod = class_getInstanceMethod(class, otherSelector);
Method originMehtod = class_getInstanceMethod(class, originSelector);
// 交换2个方法的实现
method_exchangeImplementations(otherMehtod, originMehtod);
}

3. 动态增加方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif

@interface EmptyClass:NSObject
@end

@implementation EmptyClass
@end

void sayHello(id self, SEL _cmd) {
NSLog(@"Hello");
}

- (void)addMethod {
class_addMethod([EmptyClass class], @selector(sayHello2), (IMP)sayHello, "v@:");

// Test Method
EmptyClass *instance = [[EmptyClass alloc] init];
[instance sayHello2];
}

其中types参数为”i@:@“,按顺序分别表示:

1
2
3
4
i:返回值类型int,若是v则表示void  
@:参数id(self)
::SEL(_cmd)
@:id(str)

这些表示方法都是定义好的(Type Encodings),关于Type Encodings的其他类型定义请参考官方文档

4. 获取某个类的成员变量或属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned int numIvars; //成员变量个数
Ivar *vars = class_copyIvarList(NSClassFromString(@"UIView"), &numIvars);
//Ivar *vars = class_copyIvarList([UIView class], &numIvars);

NSString *key=nil;
for(int i = 0; i < numIvars; i++) {

Ivar thisIvar = vars[i];
key = [NSString stringWithUTF8String:ivar_getName(thisIvar)]; //获取成员变量的名字
NSLog(@"variable name :%@", key);
key = [NSString stringWithUTF8String:ivar_getTypeEncoding(thisIvar)]; //获取成员变量的数据类型
NSLog(@"variable type :%@", key);
}
free(vars);

5. 获取成员函数

1
2
3
4
5
6
7
8
9
10
11
12
Method *meth = class_copyMethodList(NSClassFromString(@"UIView"), &numIvars);
//Method *meth = class_copyMethodList([UIView class], &numIvars);

for(int i = 0; i < numIvars; i++) {
Method thisIvar = meth[i];

SEL sel = method_getName(thisIvar);
const char *name = sel_getName(sel);

NSLog(@"zp method :%s", name);
}
free(meth);

二、基础换主题思路

主题我们可以将其划分为颜色图片两大块.

1. 颜色的替换

我们可以设置不同的key,将key对应的颜色值作为value,放在苹果的plist表中。当切换新主题的时候,我们去加载不同的plist即可

2. 图片替换

图片的话只要设置好对应目录即可。给出相同的名字,然后从网上下载好资源,本地建立一样名称的文件夹,并设置好即可。切换主题的时候,从不同目录去加载。

三、优化技巧

按照上诉方法大家在进行编码的时候是不是会发现代码很凌乱,通知或者委托到处在。这里提供一个简单的技巧。
我们利用swizzle在对系统控件加入一个reloadAppearance方法,用来设将我们给系统设置好的属性给其赋值。

第一步. 在UIView中

1
2
3
4
5
6
7
8
9
10
11
12
13
#import <objc/runtime.h>
@implementation UIView (Custom)
#pragma mark - ISkinProtocol

- (void)reloadAppearance
{
// 遍历所有view reloadAppearance
for (UIView* subView in self.subviews) {
[subView reloadAppearance];
}

[self setNeedsDisplay];
}

第二步. 由于其他子View如UILabel都继承自UIView,这里只举一个例子,UILabel的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@interface UILabel (Custom)
- (nonatomic, assgin) int skinNormalTextColor;
@end

#import "UILabel+Custom.h"
@implementation UILabel (Custom)

- (void)reloadAppearance
{
[super reloadAppearance];//遍历子view
self.skinTextColorNormal = self.skinTextColorNormal;//核心点,会设置上textColor的颜色
}
static NSString *skinNormalTextColorKey = @"skinNormalTextColorKey";
- (int)skinTextColorNormal
{
NSNumber* skinTextColorNormal = objc_getAssociatedObject(self, skinNormalTextColorKey);
return [skinTextColorNormal intValue];
}

- (void)setSkinTextColorNormal:(int)skinTextColorNormal
{
objc_setAssociatedObject(self, skinNormalTextColorKey, [NSNumber numberWithInt:skinTextColorNormal], OBJC_ASSOCIATION_RETAIN);

if (skinTextColorNormal != kColorInvalid) {
[self setTextColor:__QQGLOBAL_COLOR_USEDEFAULT(skinTextColorNormal, self.skinIsSetDefault)];
}
}
@end

第三步. 使用方法:

Appdelegate中提供一个reloadpublic方法即可:

[self.navigationcontroller.view reloadAppearance];

这样就会从View递归自动遍历到UILabel,UIImageView那些控件了。

小结:

之前在网上看到的思路在代码编写的时候,用了发送通知或者delegate的方法去通知界面刷新,但是只处理了图片,在颜色上没有很好的处理。而且代码显得十分臃肿。有效的利用swizzle我们使我们的代码的结构和可读性大大增强,减少代码量。

demo下载链接:

变色demo

参考链接:

1.IOS使用 swizzle 解决一些错误
2.Objective-C Runtime 运行时之四:Method Swizzling
3.Objective-C的hook方案(一): Method Swizzling
4.Objective-C Runtime Reference
5.iOS运行时获取对象的成员变量和成员方法

文章目录
  1. 1. 一、swizzle
    1. 1.1. 1. 给系统类增加成员变量
    2. 1.2. 2. 调换IMP
    3. 1.3. 3. 动态增加方法
    4. 1.4. 4. 获取某个类的成员变量或属性
    5. 1.5. 5. 获取成员函数
  2. 2. 二、基础换主题思路
    1. 2.1. 1. 颜色的替换
    2. 2.2. 2. 图片替换
  3. 3. 三、优化技巧
    1. 3.1. 第一步. 在UIView中
    2. 3.2. 第二步. 由于其他子View如UILabel都继承自UIView,这里只举一个例子,UILabel的写法:
    3. 3.3. 第三步. 使用方法:
  4. 4. 小结:
  5. 5. demo下载链接:
  6. 6. 参考链接: