iOS 13 和深色模式(Dark Mode)的適配

前言:隨著iPhone 11的發(fā)布,iOS 13已經(jīng)開始正式使用。每年發(fā)布新系統(tǒng),都會(huì)帶來(lái)一些新的變化,之前的一些方法的廢棄,又新增一些新的方法等等。因此作為應(yīng)用開發(fā)者,為了讓我們的應(yīng)用能在新的系統(tǒng)上流暢運(yùn)行,不出問(wèn)題,就需要對(duì)新系統(tǒng)進(jìn)行全方位的適配。本文將全面介紹即將到來(lái)的iOS 13系統(tǒng)上的一些注意點(diǎn)和新增的黑暗模式,我們的應(yīng)用怎么去適配。
一:iOS 13系統(tǒng)的適配以及變化

1.私有KVC

iOS 13上不允許使用:valueForKey、setValue: forKey獲取和設(shè)置私有屬性,需要使用其它方式修改,不然會(huì)奔潰
如:
[textField setValue:[UIColor red] forKeyPath:@"_placeholderLabel.textColor"];
//UITextField有個(gè)attributedPlaceholder的屬性,我們可以自定義這個(gè)富文本來(lái)達(dá)到我們需要的結(jié)果,替換為
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"輸入"attributes:@{NSForegroundColorAttributeName: [UIColor red]}];

iOS 13 通過(guò) KVC 方式修改私有屬性,有 Crash 風(fēng)險(xiǎn),謹(jǐn)慎使用!并不是所有KVC都會(huì)Crash,要嘗試!

2.通過(guò)計(jì)算TabBar上的圖片位置設(shè)置紅點(diǎn),紅點(diǎn)位置有偏移

如果之前有通過(guò)TabBar上圖片位置來(lái)設(shè)置紅點(diǎn)位置,在iOS13上會(huì)發(fā)現(xiàn)顯示位置都在最左邊去了。遍歷UITabBarButton的subViews發(fā)現(xiàn)只有在TabBar選中狀態(tài)下才能取到UITabBarSwappableImageView,解決辦法是修改為通過(guò)UITabBarButton的位置來(lái)設(shè)置紅點(diǎn)的frame。

3.模態(tài)彈出默認(rèn)交互改變

Defines the presentation style that will be used for this view controller when it is presented modally. Set this property on the view controller to be presented, not the presenter.
If this property has been set to UIModalPresentationAutomatic, reading it will always return a concrete presentation style. By default UIViewController resolves UIModalPresentationAutomatic to UIModalPresentationPageSheet, but other system-provided view controllers may resolve UIModalPresentationAutomatic to other concrete presentation styles.

iOS 13 的 presentViewController 默認(rèn)有視差效果,模態(tài)出來(lái)的界面現(xiàn)在默認(rèn)都下滑返回。 一些頁(yè)面必須要點(diǎn)確認(rèn)才能消失的,需要適配。如果項(xiàng)目中頁(yè)面高度全部是屏幕尺寸,那么多出來(lái)的導(dǎo)航高度會(huì)出現(xiàn)問(wèn)題。

控制器的 modalPresentationStyle 默認(rèn)值變了:
查閱了下 UIModalPresentationStyle枚舉定義,赫然發(fā)現(xiàn)iOS 13新加了一個(gè)枚舉值:
typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
UIModalPresentationFullScreen = 0,
UIModalPresentationPageSheet API_AVAILABLE(ios(3.2)) API_UNAVAILABLE(tvos),
UIModalPresentationFormSheet API_AVAILABLE(ios(3.2)) API_UNAVAILABLE(tvos),
UIModalPresentationCurrentContext API_AVAILABLE(ios(3.2)),
UIModalPresentationCustom API_AVAILABLE(ios(7.0)),
UIModalPresentationOverFullScreen API_AVAILABLE(ios(8.0)),
UIModalPresentationOverCurrentContext API_AVAILABLE(ios(8.0)),
UIModalPresentationPopover API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(tvos),
UIModalPresentationBlurOverFullScreen API_AVAILABLE(tvos(11.0)) API_UNAVAILABLE(ios) API_UNAVAILABLE(watchos),
UIModalPresentationNone API_AVAILABLE(ios(7.0)) = -1,
UIModalPresentationAutomatic API_AVAILABLE(ios(13.0)) = -2,
};

如果你完全接受蘋果的這個(gè)默認(rèn)效果,那就不需要去修改任何代碼。
如果,你原來(lái)就比較細(xì)心,已經(jīng)設(shè)置了modalPresentationStyle的值,那你也不會(huì)有這個(gè)影響。
對(duì)于想要找回原來(lái)默認(rèn)交互的同學(xué),直接設(shè)置如下即可:

self.modalPresentationStyle = UIModalPresentationOverFullScreen;

4.MPMoviePlayerController 在iOS 13已經(jīng)不能用了
'MPMoviePlayerController is no longer available. Use AVPlayerViewController in AVKit.'
既然不能用了,就只能使用其他播放器了

5.UISearchBar顯示問(wèn)題
升級(jí)到iOS13,UISearchController上的SearchBar顯示異常,查看后發(fā)現(xiàn)對(duì)應(yīng)的高度只有1px,目前沒找到具體導(dǎo)致的原因,解決辦法是使用KVO監(jiān)聽frame值變化后設(shè)置去應(yīng)該顯示的高度,另外,也可能出現(xiàn)一些其他的顯示問(wèn)題,這個(gè)需要自己去一個(gè)個(gè)查看,在去解決相應(yīng)的問(wèn)題。

黑線處理crash
之前為了處理搜索框的黑線問(wèn)題會(huì)遍歷后刪除UISearchBarBackground,在iOS13會(huì)導(dǎo)致UI渲染失敗crash;解決辦法是設(shè)置UISearchBarBackground的layer.contents為nil
public func clearBlackLine() {
for view in self.subviews.last!.subviews {
if view.isKind(of: NSClassFromString("UISearchBarBackground")!) {
view.backgroundColor = UIColor.white
view.layer.contents = nil
break
}
}
}

6.iOS 13 DeviceToken有變化
NSString *dt = [deviceToken description];
dt = [dt stringByReplacingOccurrencesOfString: @"<" withString: @""];
dt = [dt stringByReplacingOccurrencesOfString: @">" withString: @""];
dt = [dt stringByReplacingOccurrencesOfString: @" " withString: @""];
這段代碼運(yùn)行在 iOS 13 上已經(jīng)無(wú)法獲取到準(zhǔn)確的DeviceToken字符串了,
iOS 13 通過(guò)[deviceToken description]獲取到的內(nèi)容已經(jīng)變了。

解決方案:

  • (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
    if (![deviceToken isKindOfClass:[NSData class]]) return;
    const unsigned *tokenBytes = [deviceToken bytes];
    NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
    ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
    ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
    ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
    NSLog(@"deviceToken:%@",hexToken);
    }

7.提供第三方登錄的注意啦

Sign In with Apple will be available for beta testing this summer. It will be required as an option for users in apps that support third-party sign-in when it is commercially available later this year.

如果 APP 支持三方登陸(Facbook、Google、微信、QQ、支付寶等),就必須支持蘋果登陸,且要放前邊。關(guān)于蘋果登錄的,大家可以自行百度: ASAuthorizationAppleIDButton這個(gè)關(guān)鍵詞,進(jìn)行具體的查看

對(duì)于非iOS平臺(tái),Web、Android 可以使用 JS SDK,網(wǎng)頁(yè)版本登錄,邏輯類似、Facebook、QQ登錄。

建議支持使用Apple提供的按鈕樣式,已經(jīng)適配各類設(shè)備。個(gè)人理解蘋果的大概意思應(yīng)該是,使用第三方登錄的應(yīng)用,都要提供蘋果登錄這個(gè)模式,如果不提供,那么在審核的時(shí)候,審核應(yīng)該不給通過(guò)。

8.即將廢棄的 LaunchImage
從 iOS 8 的時(shí)候,蘋果就引入了 LaunchScreen,我們可以設(shè)置 LaunchScreen來(lái)作為啟動(dòng)頁(yè)。當(dāng)然,現(xiàn)在你還可以使用LaunchImage來(lái)設(shè)置啟動(dòng)圖。不過(guò)使用LaunchImage的話,要求我們必須提供各種屏幕尺寸的啟動(dòng)圖,來(lái)適配各種設(shè)備,隨著蘋果設(shè)備尺寸越來(lái)越多,這種方式顯然不夠 Flexible。而使用 LaunchScreen的話,情況會(huì)變的很簡(jiǎn)單, LaunchScreen是支持AutoLayout+SizeClass的,所以適配各種屏幕都不在話下。
注意啦??,從2020年4月開始,所有使? iOS13 SDK的 App將必須提供 LaunchScreen,LaunchImage即將退出歷史舞臺(tái)。

  1. Status Bar(狀態(tài)欄)更新

iOS13對(duì)Status BarAPI做了修改
之前Status Bar有兩種狀態(tài):
UIStatusBarStyleDefault 文字黑色
UIStatusBarStyleLightContent 文字白色

iOS13以前Status Bar樣式

iOS13以后有三種狀態(tài):
UIStatusBarStyleDefault自動(dòng)選擇黑色或白色
UIStatusBarStyleDarkContent文字黑色
UIStatusBarStyleLightContent文字白色

iOS13以后Status Bar有三種狀態(tài)

10.UIActivityIndicatorView加載視圖

iOS13對(duì)UIActivityIndicatorView的樣式也做了修改
之前有三種樣式:
UIActivityIndicatorViewStyleGray 灰色
UIActivityIndicatorViewStyleWhite 白色
UIActivityIndicatorViewStyleWhiteLarge 白色(大型)
iOS13廢棄了以上三種樣式,而用以下兩種樣式代替:
UIActivityIndicatorViewStyleLarge (大型)
UIActivityIndicatorViewStyleMedium (中型)
iOS13通過(guò)color屬性設(shè)置其顏色

示例:

  • (UIActivityIndicatorView *)loadingView {
    if (_loadingView == nil) {
    _loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
    [_loadingView setColor:[UIColor systemBackgroundColor]];
    [_loadingView setFrame:CGRectMake(0, 0, 200, 200)];
    [_loadingView setCenter:self.view.center];
    }
    return _loadingView;
    }

效果:

iOS13之前的三種樣式
iOS13以后的兩種樣式

現(xiàn)在,我們來(lái)看看,在iOS 13系統(tǒng)新增的最重大的功能,就是黑暗模式,蘋果自己叫深色模式:Dark Mode:

Apps on iOS 13 are expected to support dark mode Use system colors and materials Create your own dynamic colors and images Leverage flexible infrastructure
Apps on iOS 13 are expected to support dark mode Use system colors and materials

查看了網(wǎng)上有些人說(shuō),iOS 13系統(tǒng)上,必須要適配黑暗模式,不然審核是通不過(guò)的,但我看了蘋果自己的說(shuō)明,我個(gè)人的理解意思是,這個(gè)不是強(qiáng)制要求,開發(fā)者可以自己選。好了,繼續(xù)往下看這個(gè),下來(lái)會(huì)根據(jù)以下幾個(gè)方面講解黑暗模式:

一、適配Dark Mode:顏色適配,圖片適配
二、獲取當(dāng)前模式(Light or Dark)
三、其他內(nèi)容
四、總結(jié)

首先看看我們的效果圖:

image.png
一、適配Dark Mode

開發(fā)者主要從顏色和圖片兩個(gè)方面進(jìn)行適配,我們不需要關(guān)心切換模式時(shí)該如何操作,這些都由系統(tǒng)幫我們實(shí)現(xiàn)

顏色適配:
iOS13 之前 UIColor只能表示一種顏色,而從 iOS13 開始UIColor是一個(gè)動(dòng)態(tài)的顏色,在Light Mode和Dark Mode可以分別設(shè)置不同的顏色。
iOS13系統(tǒng)提供了一些動(dòng)態(tài)顏色

@property (class, nonatomic, readonly) UIColor *systemBrownColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *systemIndigoColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *systemGray2Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGray3Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGray4Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGray5Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGray6Color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *labelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *secondaryLabelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *tertiaryLabelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *quaternaryLabelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *linkColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *placeholderTextColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *separatorColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *opaqueSeparatorColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *systemBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *secondarySystemBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *tertiarySystemBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGroupedBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *secondarySystemGroupedBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *tertiarySystemGroupedBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *secondarySystemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *tertiarySystemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *quaternarySystemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

① 實(shí)例:

[self.view setBackgroundColor:[UIColor systemBackgroundColor]];
[self.titleLabel setTextColor:[UIColor labelColor]];
[self.detailLabel setTextColor:[UIColor placeholderTextColor]];

② 效果展示:

系統(tǒng)UIColor樣式

用法和iOS13之前的一樣,使用系統(tǒng)提供的這些動(dòng)態(tài)顏色,不需要其他的適配操作

③ 自定義動(dòng)態(tài)UIColor
在實(shí)際開發(fā)過(guò)程,系統(tǒng)提供的這些顏色還遠(yuǎn)遠(yuǎn)不夠,因此我們需要?jiǎng)?chuàng)建更多的動(dòng)態(tài)顏色
初始化動(dòng)態(tài)UIColor方法
iOS13 UIColor增加了兩個(gè)初始化方法,使用以下方法可以創(chuàng)建動(dòng)態(tài)UIColor
注:一個(gè)是類方法,一個(gè)是實(shí)例方法

  • (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
  • (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

這兩個(gè)方法要求傳一個(gè)block進(jìn)去
當(dāng)系統(tǒng)在LightMode和DarkMode之間相互切換時(shí)就會(huì)觸發(fā)此回調(diào)
這個(gè)block會(huì)返回一個(gè)UITraitCollection類
我們需要使用其屬性u(píng)serInterfaceStyle,它是一個(gè)枚舉類型,會(huì)告訴我們當(dāng)前是LightMode還是DarkMode
typedef NS_ENUM(NSInteger, UIUserInterfaceStyle) {
UIUserInterfaceStyleUnspecified,
UIUserInterfaceStyleLight,
UIUserInterfaceStyleDark,
} API_AVAILABLE(tvos(10.0)) API_AVAILABLE(ios(12.0)) API_UNAVAILABLE(watchos);

實(shí)例:
UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
return [UIColor redColor];
}
else {
return [UIColor greenColor];
}
}];

[self.bgView setBackgroundColor:dyColor];

效果展示:

自定義UIColor效果

接下來(lái)我們看看如何適配圖片:

2.圖片適配
打開Assets.xcassets
新建一個(gè)Image set
默認(rèn)顯示效果

打開右側(cè)工具欄,點(diǎn)擊最后一欄,找到Appearances,選擇Any,Dark

側(cè)邊欄

將兩種模式下不同的圖片資源都拖進(jìn)去

兩種不同模式

使用該圖片:
[_logoImage setImage:[UIImage imageNamed:@"icon_logo"]];
最終效果圖

大功告成,完成顏色和圖片的Dark Mode適配,是不是很easy呢!
獲取當(dāng)前模式(Light or Dark)

有時(shí)候我們需要知道當(dāng)前處于什么模式,并根據(jù)不同的模式執(zhí)行不同的操作
iOS13中CGColor依然只能表示單一的顏色
通過(guò)調(diào)用UITraitCollection.currentTraitCollection.userInterfaceStyle
獲取當(dāng)前模式
實(shí)例:
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
[self.titleLabel setText:@"DarkMode"];
}
else {
[self.titleLabel setText:@"LightMode"];
}
其他

1.監(jiān)聽模式切換
有時(shí)我們需要監(jiān)聽系統(tǒng)模式的變化,并作出響應(yīng)
那么我們就需要在需要監(jiān)聽的viewController中,重寫下列函數(shù)
/ 注意:參數(shù)為變化前的traitCollection

  • (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;

// 判斷兩個(gè)UITraitCollection對(duì)象是否不同

  • (BOOL)hasDifferentColorAppearanceComparedToTraitCollection:(UITraitCollection *)traitCollection;

示例:

  • (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];
    // trait發(fā)生了改變
    if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
    // 執(zhí)行操作
    }
    }

2.CGColor適配

我們知道iOS13后,UIColor能夠表示動(dòng)態(tài)顏色,但是CGColor依然只能表示一種顏色,那么對(duì)于CALayer等對(duì)象如何適配暗黑模式呢?當(dāng)然是利用前面提到的監(jiān)聽模式切換的方法啦。

方式一:resolvedColor
// 通過(guò)當(dāng)前traitCollection得到對(duì)應(yīng)UIColor
// 將UIColor轉(zhuǎn)換為CGColor

  • (UIColor *)resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection;

實(shí)例:

  • (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];

    UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
    if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
    return [UIColor redColor];
    }
    else {
    return [UIColor greenColor];
    }
    }];
    UIColor *resolvedColor = [dyColor resolvedColorWithTraitCollection:previousTraitCollection];
    layer.backgroundColor = resolvedColor.CGColor;

方式二:performAsCurrent
// 使用當(dāng)前trainCollection調(diào)用此方法

  • (void)performAsCurrentTraitCollection:(void (^)(void))actions;

示例:

  • (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];

    UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
    if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
    return [UIColor redColor];
    }
    else {
    return [UIColor greenColor];
    }
    }];
    [self.traitCollection performAsCurrentTraitCollection:^{
    layer.backgroundColor = dyColor.CGColor;
    }];

}

方式三:最簡(jiǎn)單的方法,直接設(shè)置為一個(gè)動(dòng)態(tài)UIColor的CGColor即可

  • (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];
    UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
    if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
    return [UIColor redColor];
    }
    else {
    return [UIColor greenColor];
    }
    }];
    layer.backgroundColor = dyColor.CGColor;
    }

??!!! 設(shè)置layer顏色都是在traitCollectionDidChange中,意味著如果沒有發(fā)生模式切換,layer將會(huì)沒有顏色,需要設(shè)置一個(gè)基本顏色

3.模式切換時(shí)打印log
模式切換時(shí)自動(dòng)打印log,就不需要我們一次又一次的執(zhí)行po命令了
在Xcode菜單欄Product->Scheme->Edit Scheme
選擇Run->Arguments->Arguments Passed On Launch
添加以下命令即可:
-UITraitCollectionChangeLoggingEnabled YES

模式切換打印log

4.強(qiáng)行設(shè)置App模式

當(dāng)系統(tǒng)設(shè)置為L(zhǎng)ight Mode時(shí),對(duì)某些App的個(gè)別頁(yè)面希望一直顯示Dark Mode下的樣式,這個(gè)時(shí)候就需要強(qiáng)行設(shè)置當(dāng)前ViewController的模式了
// 設(shè)置當(dāng)前view或viewCongtroller的模式
@property(nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle;

示例:
// 設(shè)置為Dark Mode即可
[self setOverrideUserInterfaceStyle:UIUserInterfaceStyleDark];

?? 注意!!!
當(dāng)我們強(qiáng)行設(shè)置當(dāng)前viewController為Dark Mode后,這個(gè)viewController下的view都是Dark Mode
由這個(gè)ViewController present出的ViewController不會(huì)受到影響,依然跟隨系統(tǒng)的模式,
不想適配暗黑模式可以關(guān)閉暗黑模式:
在Info.plist文件中添加Key:User Interface Style,值類型設(shè)置為String,值為L(zhǎng)ight,就可以不管在什么模式下,軟件只支持淺色模式,不支持黑暗模式,如果只想軟件支持黑暗模式,則可以把類型設(shè)置為:Dark

5.NSAttributedString優(yōu)化:
對(duì)于UILabel、UITextField、UITextView,在設(shè)置NSAttributedString時(shí)也要考慮適配Dark Mode,否則在切換模式時(shí)會(huì)與背景色融合,造成不好的體驗(yàn)
不建議的做法:
NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};
NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];

推薦的做法:
// 添加一個(gè)NSForegroundColorAttributeName屬性
NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor labelColor]};
NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];

6.iOS13 中設(shè)置 UITabBarItem的選中以及未選中字體顏色無(wú)效

iOS13 碰到設(shè)置tabbar字體為選中狀態(tài)顏色,正常切換沒有問(wèn)題,push后再返回,選中顏色變化系統(tǒng)藍(lán)色,以及其他未選中的字體顏色也變?yōu)橄到y(tǒng)的藍(lán)牙
目前碰到這種狀況有兩種方法:
這個(gè)是子視圖影響所以用tintColor試試
tintColor有尋找和傳遞
1、尋找也就是通過(guò)get方法獲取屬性的值。
2、傳遞也就是當(dāng)主動(dòng)改變tintColor時(shí)
解決辦法如下,在iOS 13上面可以用以下方法設(shè)置字體顏色,其他系統(tǒng)可以保持原來(lái)的設(shè)置方法不變:

if (@available(iOS 13, *)) {
self.tabBar.unselectedItemTintColor = [UIColor whiteColor]; //未選中字體的顏色
self.tabBar.tintColor = [UIColor whiteColor]; //選中字體的顏色
} else {
// Fallback on earlier versions
}

最后,關(guān)于iOS13 的介紹,大概就這些吧。想要看的更加詳細(xì)的介紹的話,大家可以自己去查閱更多的資料。

參考文章:iOS13 暗黑模式(Dark Mode)適配之OC版

作者:飛哥Andy
鏈接:http://www.itdecent.cn/p/0be7ce07cb4f
來(lái)源:簡(jiǎn)書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 因?yàn)橐Y(jié)局swift3.0中引用snapKit的問(wèn)題,看到一篇介紹Xcode8,swift3變化的文章,覺得很詳細(xì)...
    uniapp閱讀 4,852評(píng)論 0 12
  • 一、證書管理用Xcode8打開工程后,比較明顯的就是下圖了,這個(gè)是蘋果的新特性,可以幫助我們自動(dòng)管理證書。建議大家...
    seventhboy閱讀 1,667評(píng)論 0 2
  • #pragma mark someValueAboutTableView 1.tableView的樣式:UITab...
    瀟巖閱讀 1,049評(píng)論 0 0
  • 一、簡(jiǎn)介 <<繼承關(guān)系:UIScrollView --> UIView-->UIResponder-->NSObj...
    無(wú)邪8閱讀 2,067評(píng)論 0 0
  • 一、簡(jiǎn)介 <<繼承關(guān)系:UISearchBar-->UIView-->UIResponder-->NSObject...
    無(wú)邪8閱讀 1,386評(píng)論 0 1

友情鏈接更多精彩內(nèi)容