天天看點

iOS 使用UI控件的外觀協定UIAppearance進行設定預設UI控件樣式

在iOS開發中,經常會對UINavigationBar的樣式進行全局樣式。采用的設定方式有兩種:

第一種,采用方式如下:

[UINavigationBar appearance]      

這種是對一類對象的預設全局外觀樣式設定,它對設定時機有要求。

通常需要在UIWindow的viewlayout之前。錯過了時機後,設定是沒有效果的。

可以選擇在下面方法内設定:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    return YES;
}      

第二種,采用方式如下:

[self.navigationController.navigationBar setBarTintColor:[UIColor redColor]];      

這種是即時性的,設定後就會有效果。

第一種方式通過appearance使得UINavigationBar産生了全局預設樣式,那麼appearance是什麼東西呢?

Appearance(外觀協定)講解

iOS 使用UI控件的外觀協定UIAppearance進行設定預設UI控件樣式

UIAppearance檔案包含于UIKit架構中,該檔案很短,包括注釋加起來總共56行

點選去看看。

裡面有一個宏定義和兩個協定聲明,分别如下:

1.宏定義:

#define UI_APPEARANCE_SELECTOR __attribute__((annotate("ui_appearance_selector")))      

由注釋可知:凡是被這個宏标記的屬性方法,都可以當用作全局樣式的調用方法。

如:UIView的backgroundColor屬性

@property(nullable, nonatomic,copy)            UIColor          *backgroundColor UI_APPEARANCE_SELECTOR; //      

2.外觀容器協定:

@protocol UIAppearanceContainer <NSObject> @end      

這是個空協定,裡面啥都沒有

3.外觀協定:

@protocol UIAppearance <NSObject>
//傳回實作了此外觀協定的UI控件執行個體,用這個傳回的對象設定的屬性是全局性。
+ (instancetype)appearance;
//傳回實作了此外觀協定的UI控件執行個體,用這個傳回的對象設定的屬性隻對ContainerClass内部包裹的對象有效。
+ (instancetype)appearanceWhenContainedIn:(nullable Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION NS_DEPRECATED_IOS(5_0, 9_0, "Use +appearanceWhenContainedInInstancesOfClasses: instead") __TVOS_PROHIBITED;
//是iOS9之後的替代方法,作用和上面的一樣。
+ (instancetype)appearanceWhenContainedInInstancesOfClasses:(NSArray<Class <UIAppearanceContainer>> *)containerTypes NS_AVAILABLE_IOS(9_0);
//是iOS8新增的方法,是對不同的布局方案(緊湊型,普通型)采用不同的外觀樣式
+ (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait NS_AVAILABLE_IOS(8_0);
//與上面的方法相似,多了一個參數條件,隻對ContainerClass内部包裹的對象有效。
+ (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedIn:(nullable Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION NS_DEPRECATED_IOS(8_0, 9_0, "Use +appearanceForTraitCollection:whenContainedInInstancesOfClasses: instead") __TVOS_PROHIBITED;
//iOS9後的新方法,是對上面方法的替換。
+ (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedInInstancesOfClasses:(NSArray<Class <UIAppearanceContainer>> *)containerTypes  NS_AVAILABLE_IOS(9_0);
@end      

在上面代碼中對裡面定義的5個方法進行了說明。

遵守了這兩個協定的類,可以進行同一預設外觀設定,那麼哪些類遵守了這兩個協定了呢?

看一下UI控件的父類UIView

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, UIFocusItemContainer, CALayerDelegate>      

通過上面UIView的定義可以發現,UIView遵守了外觀協定和外觀容器協定。

也就是說所有的UI控件,隻有其屬性方法被UI_APPEARANCE_SELECTOR宏标示了,那麼就可以進行全局外觀

預設設定。

這裡列一下UINavigationBar通常用的的全局設定。

//⚠️: 1與2互斥,且1的優先級高

//1.設定導航背景

UIImage *bg = [UIImage pureImageWithColor:[UIColor brownColor]];
[[UINavigationBar appearance] setBackgroundImage:bg forBarMetrics:UIBarMetricsDefault];      

//2.設定導航顔色

[[UINavigationBar appearance] setBarTintColor:[UIColor redColor]];      

//3.設定導航文字顔色

[[UINavigationBar appearance] setTintColor:[UIColor blueColor]];      

//4.設定導航字型

NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8];
shadow.shadowOffset = CGSizeMake(0, 1);
[[UINavigationBar appearance] setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys: [UIColor colorWithRed:245.0/255.0 green:245.0/255.0 blue:245.0/255.0 alpha:1.0], NSForegroundColorAttributeName, shadow, NSShadowAttributeName, [UIFont fontWithName:@"HelveticaNeue-CondensedBlack" size:21.0], NSFontAttributeName, nil]];      

//5.特定環境或某種場合下外觀樣式設定(水準緊湊型,UINavigationBar預設外觀為粉色。)

[[UINavigationBar appearanceForTraitCollection:[UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact]] setBarTintColor:[UIColor purpleColor]];      

另外對UIButton的全局預設設定如下:

//讓一類控件同時表現出某種屬性

[[UIButton appearance] setBackgroundColor:[UIColor yellowColor]];
[[UIButton appearance] setTitle:@"同一設定" forState:UIControlStateNormal];      

//讓一類控件在某種環境下表現出某種外觀樣式

[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class]]] setTitle:@"比較特别" forState:UIControlStateNormal];
[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class]]] setBackgroundColor:[UIColor redColor]];      

UI控件的預設全局設定方式符合預期。

那麼iOS開發中,所有的UI控件都可以通過上面兩種方法進行全局樣式預設設定和自定義設定了。

iOS