本來想寫個UIApearance的教程改應用模式切換的绞蹦,結果搞出來一個非常贊的方法來實現(xiàn)任意模式的切換。
不了解UIApearance的可以點這里看看了解下榜旦。
說下思路:
本來我是想通過extension和子類化幽七,自定義UIApearance可以調用的方法來實現(xiàn)模式的切換,具體方式就不寫了溅呢,說真的澡屡,有點麻煩,也不容易封裝咐旧,本來是想推薦給大家在找不到合適的方式而且不想使用通知的時候嘗試下驶鹉,后來突然想到如果我直接用block把對象本身傳回來,不是可以任意修改樣式了嗎铣墨,然后就有了這個室埋。
期間做了許多嘗試:
1.本來想在自定義UIApearance方法時把block作為參數(shù),結果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的自定義方法看峻,通過傳遞模式類型來選擇執(zhí)行哪個block萨西。這個方法很有效摹恰,但是也很麻煩昙篙,因為你用到的所有需要改變模式的UI空間都需要子類化一遍惯殊。
3.之后我直接刪掉了block的屬性骤肛,專門定制化在某個模式見切換的UI控件纳本,比如一個View的子類專門黑白切換,另一個淺灰深灰切換腋颠,然后在自定義的方法里直接根據(jù)傳入的模式修改顏色繁成,在使用時只要把對應的控件繼承對應的子類就好了。這樣的好處是不用每個對象都寫兩套修改方案淑玫,節(jié)省代碼巾腕。壞處是如果不同類型的控件很多,就需要定義很多的子類絮蒿,而且對與那些已經(jīng)完成的項目添加模式切換也會比較坑爹尊搬。
4.再后來突然想到第2次嘗試的方法可以優(yōu)化下,直接把block通過extension+runtime的方式直接給系統(tǒng)控件添加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方法通過了袭祟,然后去用,發(fā)現(xiàn)get方法每次取到得都是nil捞附,然后又想辦法巾乳,到最后終于解決掉這個在extension里創(chuàng)建block類型的存儲屬性的方法。過程沒辦法說了鸟召,其實就是把自定義block類型與AnyObject相互轉換胆绊。
unsafeBitCast(anyObjectValue, MyBlock.self)
unsafeBitCast(myBlockValue, AnyObject.self)
6.解決之后再去嘗試模式切換,結果給了我一個很大的驚喜欧募。通過(extension+runtime自定義block存儲屬性)+ 自定義UIApearance方法压状,我發(fā)現(xiàn)只要給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 )
}
}
}
}
根據(jù)這種模式直接復制粘貼可以添加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