本来想写个UIApearance的教程改应用模式切换的,结果搞出来一个非常赞的方法来实现任意模式的切换。
不了解UIApearance的可以点这里看看了解下。
说下思路:
本来我是想通过extension和子类化,自定义UIApearance可以调用的方法来实现模式的切换,具体方式就不写了,说真的,有点麻烦,也不容易封装,本来是想推荐给大家在找不到合适的方式而且不想使用通知的时候尝试下,后来突然想到如果我直接用block把对象本身传回来,不是可以任意修改样式了吗,然后就有了这个。
期间做了许多尝试:
1.本来想在自定义UIApearance方法时把block作为参数,结果UIApearance的自定义方法有严格的限制,然后失败了。
// Swift
func propertyForAxis1(axis1: IntegerType, axis2: IntegerType, axisN: IntegerType) -> PropertyType
func setProperty(property: PropertyType, forAxis1 axis1: IntegerType, axis2: IntegerType)
// OBJECTIVE-C
- (PropertyType)propertyForAxis1:(IntegerType)axis1 axis2:(IntegerType)axis2 … axisN:(IntegerType)axisN;
- (void)setProperty:(PropertyType)property forAxis1:(IntegerType)axis1 axis2:(IntegerType)axis2 … axisN:(IntegerType)axisN;
2.然后我想到了使用子类化,增加block的属性进行操作,然后结合UIApearance的自定义方法,通过传递模式类型来选择执行哪个block。这个方法很有效,但是也很麻烦,因为你用到的所有需要改变模式的UI空间都需要子类化一遍。
3.之后我直接删掉了block的属性,专门定制化在某个模式见切换的UI控件,比如一个View的子类专门黑白切换,另一个浅灰深灰切换,然后在自定义的方法里直接根据传入的模式修改颜色,在使用时只要把对应的控件继承对应的子类就好了。这样的好处是不用每个对象都写两套修改方案,节省代码。坏处是如果不同类型的控件很多,就需要定义很多的子类,而且对与那些已经完成的项目添加模式切换也会比较坑爹。
4.再后来突然想到第2次尝试的方法可以优化下,直接把block通过extension+runtime的方式直接给系统控件添加block作为存储属性。结果失败了,因为set方法编译无法通过。下面贴一下通过extension+runtime添加存储属性的方法,大家可以自己尝试下。
private struct AssociatedKeys {
static var aStringKey = "aStringKey"
}
var aString: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys. aStringKey) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject( self, &AssociatedKeys. aStringKey, newValue as NSString?, .OBJC_ASSOCIATION_COPY_NONATOMIC )
}
}
}
5.然后我就以学习的目的想想办法看能不能解决,做了许多尝试,中间demo崩了多少次都不想说了,之后终于set方法通过了,然后去用,发现get方法每次取到得都是nil,然后又想办法,到最后终于解决掉这个在extension里创建block类型的存储属性的方法。过程没办法说了,其实就是把自定义block类型与AnyObject相互转换。
unsafeBitCast(anyObjectValue, MyBlock.self)
unsafeBitCast(myBlockValue, AnyObject.self)
6.解决之后再去尝试模式切换,结果给了我一个很大的惊喜。通过(extension+runtime自定义block存储属性)+ 自定义UIApearance方法,我发现只要给UIView加上这段代码,所有问题都解决了,这是我做之前都没想到的,效果好的让我想哭......
代码
代码并不多,下面看代码:
import UIKit
typealias Block = @convention(block) (UIView) -> Void
extension UIView{
private struct AssociatedKeys {
static var blockName1 = "blockName1"
static var blockName2 = "blockName2"
}
//自定义的UIApearance方法,调用方法为 UIView.appearance().setType(1) UIApearance的具体特性可以自己去尝试和查资料
//通过type切换模式
dynamic func setType(type: Int){
switch type {
case 1:
if block1 != nil {
block1!(self)
}
default:
if block2 != nil {
block2!(self)
}
}
}
var block1: Block?{
get {
let value = objc_getAssociatedObject(self, &AssociatedKeys.blockName1)
return unsafeBitCast(value, Block.self)
}
set {
if let newValue = newValue {
let value:AnyObject = unsafeBitCast(newValue, AnyObject.self)
objc_setAssociatedObject( self, &AssociatedKeys.blockName1, value, .OBJC_ASSOCIATION_COPY )
}
}
}
var block2: Block?{
get {
let value = objc_getAssociatedObject(self, &AssociatedKeys.blockName2)
return unsafeBitCast(value, Block.self)
}
set {
if let newValue = newValue {
let value:AnyObject = unsafeBitCast(newValue, AnyObject.self)
objc_setAssociatedObject( self, &AssociatedKeys.blockName2, value, .OBJC_ASSOCIATION_COPY )
}
}
}
}
根据这种模式直接复制粘贴可以添加block3,block4...
然后是使用示例:
//aView是个UIView 不同的UIVIew子类互不影响
//模式1时做的内容
aView.block1 = { view in
view.backgroundColor = UIColor.redColor()
}
//模式2时做的内容
aView.block2 = {view in
view.backgroundColor = UIColor.blueColor()
}
//aLabel是一个UILabel
aLabel.block1 = { view in
let lab = view as! UILabel
lab.backgroundColor = UIColor.redColor()
lab.textColor = UIColor.blueColor()
}
aLabel.block2 = {view in
let lab = view as! UILabel
lab.backgroundColor = UIColor.blueColor()
lab.textColor = UIColor.redColor()
}
UIView.appearance().setType(1)
UIView.appearance().setType(0)
你可以在block里写任何你想改变的效果。
把UIView的extension写进项目里,然后给所有切换模式改变颜色的UI控件设置block1和block2,另外建议那些大量的相同类型的UI控件同时继承同一个子类直接定制。
最后
如果应用要添加模式设置,尤其是已完成的项目,用这种方式修改起来会比较简单,可以尝试下。
补充
OC版代码
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (Extension)
//扩展属性 block
@property (nonatomic, copy) void (^doApearanceBlock)(UIView *view);
@property (nonatomic, strong) NSString *languageText UI_APPEARANCE_SELECTOR;
//-(void)setLanguage:(NSString *)language UI_APPEARANCE_SELECTOR;
@end
NS_ASSUME_NONNULL_END
#import "UIView+Extension.h"
#import <objc/runtime.h>
static const void *doApearanceBlockKey = &doApearanceBlockKey;
static const void *languageTextKey = &languageTextKey;
@implementation UIView (Extension)
@dynamic doApearanceBlock;
- (void (^)(UIView * _Nonnull))doApearanceBlock{
return objc_getAssociatedObject(self,doApearanceBlockKey);
}
- (void)setDoApearanceBlock:(void (^)(UIView * _Nonnull))doApearanceBlock{
objc_setAssociatedObject(self, doApearanceBlockKey, doApearanceBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)languageText{
return objc_getAssociatedObject(self,languageTextKey);
}
- (void)setLanguageText:(NSString *)languageText{
objc_setAssociatedObject(self, languageTextKey, languageText, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end