iOS 13 如期而至,適配工作可以開展起來啦。在適配 iOS 13 過程中,遇到了如下一些問題。
1. UITextField 的私有屬性 _placeholderLabel 被禁止訪問了
遇到的第一個崩潰是修改UITextField
的placeholder
的顏色,歷史遺留代碼如下:
[_textField setValue:self.placeholderColor forKeyPath:@"_placeholderLabel.textColor"];
收到的錯誤信息??
'Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug'
那么這個問題如何處理呢?
其實(shí),UITextField
有個attributedPlaceholder
的屬性,我們可以自定義這個富文本來達(dá)到我們需要的結(jié)果。
修改如下:
NSMutableAttributedString *placeholderString = [[NSMutableAttributedString alloc] initWithString:placeholder attributes:@{NSForegroundColorAttributeName : self.placeholderColor}];
_textField.attributedPlaceholder = placeholderString;
注意??,iOS 13 通過 KVC 方式修改私有屬性,有 Crush 風(fēng)險,謹(jǐn)慎使用!
2. 控制器的 modalPresentationStyle 默認(rèn)值變了
對于這個變化,有點(diǎn)措手不及,直接修改了模態(tài)窗口的交互。
查閱了下 UIModalPresentationStyle
枚舉定義,赫然發(fā)現(xiàn)iOS 13
新加了一個枚舉值:
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,
};
是的,就是UIModalPresentationAutomatic
,蘋果居然直接將modalPresentationStyle
默認(rèn)值改成這個,有點(diǎn)不解,難道是怕我們不知道新加了這個交互?這個也完全違反了開閉原則吧??。
如何修改:
如果你完全接受蘋果的這個默認(rèn)效果,那就不需要去修改任何代碼。
如果,你原來就比較細(xì)心,已經(jīng)設(shè)置了modalPresentationStyle
的值,那你也不會有這個影響。
對于想要找回原來默認(rèn)交互的同學(xué),直接設(shè)置如下即可:
self.modalPresentationStyle = UIModalPresentationFullScreen;
值得注意的是,當(dāng) modalPresentationStyle
是 UIModalPresentationAutomatic
時,presentationController
是不會消失的。所以,關(guān)閉模態(tài)窗口的時候,presentationController
的生命周期方法 viewWillAppear:
和 viewDidAppear:
都不會觸發(fā)。
3. MPMoviePlayerController 在iOS 13已經(jīng)不能用了
在使用到MPMoviePlayerController
的地方,直接拋了異常:
'MPMoviePlayerController is no longer available. Use AVPlayerViewController in AVKit.'
如何修改:
這個沒啥好說的,既然不能再用了,那只能換掉了。替代方案就是AVKit
里面的那套播放器。
4. iOS 13 DeviceToken有變化??
這個很重要??
可能大多數(shù)使用第三方推送的童鞋都不會注意到這個問題,一般現(xiàn)在的第三方推送都是將DeviceToken
原始數(shù)據(jù)丟進(jìn)去,具體的解析都是第三方內(nèi)部處理,所以,這些第三方解析DeviceToken
的方式正確的話,那就毫無問題。如果你們是通過這種方式來獲取DeviceToken
,那你需要注意了。(這個坑也是多年前埋下的,很多文章介紹的也是下面這個方法,不規(guī)范的做法遲早要還的??),如下:
NSString *dt = [deviceToken description];
dt = [dt stringByReplacingOccurrencesOfString: @"<" withString: @""];
dt = [dt stringByReplacingOccurrencesOfString: @">" withString: @""];
dt = [dt stringByReplacingOccurrencesOfString: @" " withString: @""];
這段代碼運(yùn)行在 iOS 13 上已經(jīng)無法獲取到準(zhǔn)確的DeviceToken
字符串了,iOS 13 通過[deviceToken description]
獲取到的內(nèi)容已經(jīng)變了。
{length = 32, bytes = 0x778a7995 29f32fb6 74ba8167 b6bddb4e ... b4d6b95f 65ac4587 }
可以看到,跟原來我們認(rèn)識的那個已經(jīng)完全不一樣了。其實(shí),造成這樣的問題,主要還是沒有使用正確的方式來操作,下面是解決辦法:
NSMutableString *deviceTokenString = [NSMutableString string];
const char *bytes = deviceToken.bytes;
NSInteger count = deviceToken.length;
for (int i = 0; i < count; i++) {
[deviceTokenString appendFormat:@"%02x", bytes[i]&0x000000FF];
}
或者你也可以使用友盟提供的方法(2019年7月24日更新)
- (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);
}
5.Sign in with Apple (提供第三方登錄的注意啦??)
如果你的應(yīng)用使用了第三方登錄,那么你可能也需要加下 「Sign in with Apple」??
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.
關(guān)于如何集成,可以參考這篇文章:《Sign in with Apple》。
附上官方Demo:點(diǎn)我下載
Sign in with Apple 適配時間蘋果已經(jīng)確認(rèn),具體更新到了公眾號同名文章,可以在文末掃碼關(guān)注公眾號進(jìn)行查看。
6.即將廢棄的 LaunchImage
從 iOS 8 的時候,蘋果就引入了 LaunchScreen
,我們可以設(shè)置 LaunchScreen
來作為啟動頁。當(dāng)然,現(xiàn)在你還可以使用LaunchImage
來設(shè)置啟動圖。不過使用LaunchImage
的話,要求我們必須提供各種屏幕尺寸的啟動圖,來適配各種設(shè)備,隨著蘋果設(shè)備尺寸越來越多,這種方式顯然不夠 Flexible
。而使用 LaunchScreen
的話,情況會變的很簡單, LaunchScreen
是支持AutoLayout
+SizeClass
的,所以適配各種屏幕都不在話下。
注意??: 從2020年4月開始,所有使? iOS13 SDK
的 App
將必須提供 LaunchScreen
,LaunchImage
即將退出歷史舞臺。
再補(bǔ)充一點(diǎn),在使用 LaunchScreen
的時候,里面用到的圖片資源,最好別放在 xcassets
里面,不然在你修改圖片后,你會發(fā)現(xiàn)真機(jī)上并不會生效。
7. 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
9月24更新:
關(guān)于Dark Mode
,這里補(bǔ)充幾點(diǎn)。
因?yàn)樘O果目前還沒有強(qiáng)制必須適配這個,相信大家的很多項(xiàng)目也沒有開始是配這個模式。所以,前期可以強(qiáng)制Light
模式。不然,你可能會遇到一些問題,比如UITableViewCell
的背景色,如果你沒有設(shè)置過背景色的話,它在Dark
模式下就是黑色的,再比如UIDatePicker
文字顏色等等。
那么怎么強(qiáng)制模式呢?
iOS 13
給UIView
和UIViewController
都添加了一個屬性:
@property (nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
給這個屬性設(shè)置成某一種模式,即可強(qiáng)制顯示模式。
如果你想修改一處,應(yīng)用所有地方,那么你只需要設(shè)置widow
的顯示模式即可,這會影響widow下面的所有視圖顯示模式,這也可以看出顯示模式是向下傳遞的。
if (@available(iOS 13, *)) {
[self.window setOverrideUserInterfaceStyle:UIUserInterfaceStyleLight];
}
8.iOS 13 UITabBar頂部分割線隱藏
這個問題源自網(wǎng)友的提問,我的項(xiàng)目中并沒有這樣的需求,所以之前沒有處理。
看到網(wǎng)上的一些解決辦法,如下:
[UITabBar appearance].layer.borderWidth = 0.0f;
[UITabBar appearance].clipsToBounds = YES;
看了下,實(shí)際起作用的代碼是這條代碼:
[UITabBar appearance].clipsToBounds = YES;
也就是裁剪掉了多余的部分(多余的部分正好就是分割線,后面會提到為什么是分割線),達(dá)到隱藏分割線的目的。但是這樣設(shè)置,TabBar
會裁剪子視圖,這樣我們有大按鈕的TabBar
,按鈕就會被裁剪,造成顯示不完整。
那么在iOS 13
上有沒有別的辦法來隱藏分割線呢?
肯定是有的,其實(shí)解決這樣的問題,只要我們能找到這個視圖,就可以解決問題。從這個角度出發(fā),我們來看下iOS 13
上面TabBar
子視圖都有哪些。直接打印下subviews
即可。
(
"<_UIBarBackground: 0x7fbb34007920; frame = (0 0; 414 83);",
"<UITabBarButton: 0x7fbb2ed032d0; frame = (2 1; 410 48);"
)
我們可以看到內(nèi)部有個_UIBarBackground
私有的東西,分割線肯定跟他有關(guān)(總不可能跟UITabBarButton
有關(guān)吧??)。然后,我們在看下這個控件的子視圖。
(
"<UIImageView: 0x7fbb2ec09b90; frame = (0 -0.333333; 414 0.333333);",
"<UIVisualEffectView: 0x7fbb2ec05a20; frame = (0 0; 414 83); "
)
emmmm??,我們看到了一個越界的UIImageView
,這個UIImageView
就是分割線視圖。怎么證明呢?我們加下這個代碼:
[self.tabBar.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj1, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj1 isKindOfClass:[UIImageView class]]) {
obj1.hidden = YES;
}
}];
}];
運(yùn)行后,發(fā)現(xiàn)分割線隱藏了,也就證明了我們的猜想。當(dāng)然,上面這個代碼不嚴(yán)謹(jǐn)哈,這里主要是提供一種思路,僅作為演示,具體怎么做相信大家都可以的。
總結(jié)下怎么解決:找到它》隱藏它。千萬別通過KVC的方式去處理,理由看第一點(diǎn)。
9.UIWebView被廢棄
"No longer supported; please adopt WKWebView.", ios(2.0, 12.0)
這個大家也盡快適配吧,看到有提到審核被拒的問題。
10.iOS 13 需增加藍(lán)牙權(quán)限描述
簡友 @funkep 提到最新一次提審被拒情況,被拒原因之一就是需要添加藍(lán)牙權(quán)限獲取描述。如果你的應(yīng)用需要使用藍(lán)牙權(quán)限,需要在Info.plist
里面加上NSBluetoothAlwaysUsageDescription
這個key
,對應(yīng)的描述value
根據(jù)權(quán)限的用途來描述即可。
感謝 @funkep 的分享,大家遇到的被拒理由可以反饋到留言區(qū),我會更新到文章里面,讓更多人看到。
如果這篇文章對你有幫助,不妨隨手點(diǎn)個贊!謝謝??
??禁止未授權(quán)轉(zhuǎn)載,只接收鏈接轉(zhuǎn)載,不接受內(nèi)容拷貝轉(zhuǎn)載