皮膚切換

提供資訊、信息類的App一般都有白天和黑夜兩種閱讀模式

  • 現有項目中皮膚切換思路
    1、資源文件(圖片、plist色板值、接口讀取數據拼接html模版所使用到的樣式表CSS等),都需準備兩套。
    2、對用到的控件進行父類繼承,擴展屬性用字符串設置圖片、文本顏色名稱,如UIButton包括:未選中圖片、高亮中圖片、選中圖片、禁用圖片;未選中文字顏色、高亮中文字顏色、選中文字顏色、禁用文字顏色;未選中背景圖片、高亮背景圖片、選中背景圖片、禁用背景圖片等。
    3、皮膚切換,保存當前模式到本地:通過發送通知的方式,控件接收通知,通過工具類方法刷新重新讀取該皮膚模式下對應的顏色或圖片;web頁面讀取該皮膚模式下的樣式表,通過JS替換CSS的href。
  • DKNightVersion皮膚切換學習
    1、目錄結構:
tmp40972269.png

Core:核心類(DKColor顏色設置,DKImage圖片設置,DKColorTable處理皮膚配置文件,DKNightVersionManager皮膚管理類,NSObject+Night擴展一個DKNightVersionManager)
DeallocBlockExecutor:內存回收(移除通知)相關的回調
CoreAnimation:動畫Layer的擴展
Resources:皮膚配置文件
UIKit:皮膚控件的擴展
2、思路
2.1、擴展NSObject通過DKNightVersionManager單例來管理皮膚的切換,設置themeVersion后保存到本地,并通知其它視圖更新顏色。

- (void)setThemeVersion:(DKThemeVersion *)themeVersion {
    if ([_themeVersion isEqualToString:themeVersion]) {
        // if type does not change, don't execute code below to enhance performance.
        return;
    }
    _themeVersion = themeVersion;

    // Save current theme version to user default
    [[NSUserDefaults standardUserDefaults] setValue:themeVersion forKey:DKNightVersionCurrentThemeVersionKey];
    [[NSNotificationCenter defaultCenter] postNotificationName:DKNightVersionThemeChangingNotificaiton
                                                        object:nil];

    if (self.shouldChangeStatusBar) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        if ([themeVersion isEqualToString:DKThemeVersionNight]) {
            [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
        } else {
            [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
        }
#pragma clang diagnostic pop
    }
}

2.2、顏色設置:如TableViewCell的背景顏色通過一個屬性dk_cellTintColorPicker進行,實質是一個 block,它接收參數 DKThemeVersion *themeVersion,但是會返回一個 UIColor *
UIKit擴展中.m文件中的屬性pickersNSObject+Nightpickers是一個東西。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    // 項目中這里是寫的一個宏自動生成,效果跟寫一個UITableViewCell+Night類別是一樣的
    cell.dk_cellTintColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa);

    return cell;
}

2.3、然后通過屬性關聯設置UITableView的 tintColor;同時,每一個對象還持有一個pickers 數組,來存儲自己的全部 DKColorPicker:

@interface UITableViewCell ()

@property (nonatomic, strong) NSMutableDictionary<NSString *, DKColorPicker> *pickers;

@end

@implementation UITableViewCell (Night)

- (DKColorPicker)dk_cellTintColorPicker {
    return objc_getAssociatedObject(self, @selector(dk_cellTintColorPicker));
}

- (void)dk_setCellTintColorPicker:(DKColorPicker)picker {
    objc_setAssociatedObject(self, @selector(dk_cellTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC);
    self.tintColor = picker(self.dk_manager.themeVersion);
    [self.pickers setValue:[picker copy] forKey:@"setTintColor:"];
}

@end

2.4、在第一次使用這個屬性時,當前對象注冊為 DKNightVersionThemeChangingNotificaiton 通知的觀察者。 pickers屬性只有在對象的某個 DKColorPicker/DKImagePicker首次被賦值時才會被創建。
在每次收到通知時,都會調用 night_update 方法,將當前主題傳入 DKColorPicker,并再次執行,并將結果傳入對應的屬性 [self performSelector:sel withObject:result]

- (NSMutableDictionary<NSString *, DKColorPicker> *)pickers {
    // 第一次進來pickers為空進入if
    NSMutableDictionary<NSString *, DKColorPicker> *pickers = objc_getAssociatedObject(self, @selector(pickers));
    if (!pickers) {
        
        @autoreleasepool {
            // Need to removeObserver in dealloc
            if (objc_getAssociatedObject(self, &DKViewDeallocHelperKey) == nil) {
                __unsafe_unretained typeof(self) weakSelf = self; // NOTE: need to be __unsafe_unretained because __weak var will be reset to nil in dealloc
                id deallocHelper = [self addDeallocBlock:^{
                    [[NSNotificationCenter defaultCenter] removeObserver:weakSelf];
                }];
                objc_setAssociatedObject(self, &DKViewDeallocHelperKey, deallocHelper, OBJC_ASSOCIATION_ASSIGN);
            }
        }

        pickers = [[NSMutableDictionary alloc] init];
        // 將局部變量pickers和當前對象的pickers關聯
        objc_setAssociatedObject(self, @selector(pickers), pickers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        [[NSNotificationCenter defaultCenter] removeObserver:self name:DKNightVersionThemeChangingNotificaiton object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(night_updateColor) name:DKNightVersionThemeChangingNotificaiton object:nil];
    }
    return pickers;
}

- (void)night_updateColor {
    [self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker  _Nonnull picker, BOOL * _Nonnull stop) {
        SEL sel = NSSelectorFromString(selector);
        id result = picker(self.dk_manager.themeVersion);
        [UIView animateWithDuration:DKNightVersionAnimationDuration
                         animations:^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                             [self performSelector:sel withObject:result];
#pragma clang diagnostic pop
                         }];
    }];
}
  • objc_AssociationPolicy幾種類型區別:Objective-C中的Associated Objects - 曾靜的技術博客
    1、 OBJC_ASSOCIATION_ASSIGN,給關聯對象指定弱引用,相當于 @property(assign)@property(unsafe_unretained)
    2、 OBJC_ASSOCIATION_RETAIN_NONATOMIC,給關聯對象指定非原子的強引用,相當于 @property(nonatomic,strong)@property(nonatomic,retain)
    3、 OBJC_ASSOCIATION_COPY_NONATOMIC, 給關聯對象指定非原子的copy特性,相當于 @property(nonatomic,copy)
    4、 OBJC_ASSOCIATION_RETAIN,給關聯對象指定原子強引用,相當于 @property(atomic,strong)@property(atomic,retain)
    5、 OBJC_ASSOCIATION_COPY,給關聯對象指定原子copy特性,相當于 @property(atomic,copy)

objc_setAssociatedObject:用來把一個對象與另一個對象進行關聯。一共需要四個參數,分別是:源對象,關鍵字,關聯的對象和一個關聯策略。源對象和關聯對象就是需要進行關聯的兩個對象; 關鍵字是一個void類型的指針, 每一個關聯的關鍵字必須是唯一的,通常都是會采用靜態變量來作為關鍵字,一般情況下也可以取@selector(function_name)即取得一個function的id作為關鍵字;關聯策略是一個枚舉,用來表示兩個對象的關聯程度。
objc_getAssociatedObject:和objc_setAssociatedObject配套使用,它是獲取相關聯的對象時使用的,objc_getAssociatedObject:兩個參數,源對象、關鍵字(注意關鍵字唯一且一致)。

  • run Time在項目中的運用
    實質:是一個運行時庫(Runtime Library),它是一個主要使用 C 和匯編寫的庫,為 C 添加了面相對象的能力并創造了 Objective-C。這就是說它在類信息(Class information) 中被加載,完成所有的方法分發,方法轉發,等等。Objective-C runtime 創建了所有需要的結構體。Objective-C為什么有面相對象的能力?就是因為有runtime這個鬼東西!參考:http://www.lxweimin.com/p/bba1ac264873

1、動態添加屬性;
2、方法切換
云信消息處理:message的時間與處理(13位處理成10位)

+(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 要特別注意你替換的方法到底是個性質的方法
        // When swizzling a Instance method, use the following:
        // Class class = [self class];

        // When swizzling a class method, use the following:
        Class class = object_getClass((id)self);

        SEL originalSelector = @selector(systemMethod_PrintLog);
        SEL swizzledSelector = @selector(ll_imageName);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

3、獲取一個類的所有成員變量
獲得某個類的所有成員變量 Ivar *class_copyIvarList(Class cls , unsigned int *outCount):(哪個類,放一個接收值的地址,用來存放屬性的個數),返回值:存放所有獲取到的屬性
獲得成員變量的名字 const char *ivar_getName(Ivar v)
獲得成員變量的類型 const char *ivar_getTypeEndcoding(Ivar v)

unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([Person class], &outCount);
// 遍歷所有成員變量
for (int i = 0; i < outCount; i++) { 
    // 取出i位置對應的成員變量 
    Ivar ivar = ivars[i]; 
    const char *name = ivar_getName(ivar); 
    const char *type = ivar_getTypeEncoding(ivar); 
    NSLog(@"成員變量名:%s 成員變量類型:%s",name,type);
}
// 注意釋放內存!
free(ivars);
  • Objective C類方法load和initialize的區別
    1、load是只要類所在文件被引用就會被調用,而initialize是在類或者其子類的第一個方法被調用前調用。所以如果類沒有被引用進項目,就不會有load調用;但即使類文件被引用進來,但是沒有使用,那么initialize也不會被調用。
    2、相同點在于:方法只會被調用一次。

  • runtime 完整總結
    來自南峰子博客,就是對“objc/runtime.h”的解讀

  • 查看Demo請點擊
    使用plist文件進行色值配置

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容

  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,751評論 0 9
  • 對于從事 iOS 開發人員來說,所有的人都會答出【runtime 是運行時】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,730評論 7 64
  • 吳蜀難降魏, 文武需兩全。 東風成諸葛, 梟雄敗赤壁。 望古今多少成敗, 昨日風吹云散。 明爭暗奪, 名利難收。 ...
    風中瀟灑小青年閱讀 71評論 0 1
  • 似夢 天藍藍 水盈盈 山青青 草油油 天上人間 非夢 微風輕撫湖面 花香悠遠 歡快的鳥兒 吟唱心曲 走進一個夢 祈...
    舒潔湲閱讀 289評論 15 4