iOS13 適配 附:獲取DeviceToken的Swift版本

 

iOS 13 如期而至,雖然正式版還沒出來,但是適配工作可以開展起來啦。在適配 iOS 13 過程中,遇到了如下一些問題。

1. UITextField 的私有屬性 _placeholderLabel 被禁止訪問了

遇到的第一個崩潰是修改UITextFieldplaceholder的顏色,歷史遺留代碼如下:

[_textField setValue:self.placeholderColor forKeyPath:@"_placeholderLabel.textColor"];

收到的錯誤信息⚠️

'Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug' 

那麼這個問題如何處理呢?

其實,UITextField有個attributedPlaceholder的屬性,我們可以自定義這個富文本來達到我們需要的結果。

修改如下:

NSMutableAttributedString *placeholderString = [[NSMutableAttributedString alloc] initWithString:placeholder attributes:@{NSForegroundColorAttributeName : self.placeholderColor}];
_textField.attributedPlaceholder = placeholderString;

注意⚠️,iOS 13 通過 KVC 方式修改私有屬性,有 Crush 風險,謹慎使用!

2. 控制器的 modalPresentationStyle 默認值變了

對於這個變化,有點措手不及,直接修改了模態窗口的交互。
查閱了下 UIModalPresentationStyle枚舉定義,赫然發現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默認值改成這個,有點不解,難道是怕我們不知道新加了這個交互?這個也完全違反了開閉原則吧?。

如何修改:
如果你完全接受蘋果的這個默認效果,那就不需要去修改任何代碼。
如果,你原來就比較細心,已經設置了modalPresentationStyle的值,那你也不會有這個影響。
對於想要找回原來默認交互的同學,直接設置如下即可:

self.modalPresentationStyle = UIModalPresentationFullScreen;

值得注意的是,當 modalPresentationStyleUIModalPresentationAutomatic時,presentationController 是不會消失的。所以,關閉模態窗口的時候,presentationController 的生命週期方法 viewWillAppear:viewDidAppear: 都不會觸發。

3. MPMoviePlayerController 在iOS 13已經不能用了

在使用到MPMoviePlayerController的地方,直接拋了異常:

'MPMoviePlayerController is no longer available. Use AVPlayerViewController in AVKit.' 

如何修改:
這個沒啥好說的,既然不能再用了,那隻能換掉了。替代方案就是AVKit裏面的那套播放器。

4. iOS 13 DeviceToken有變化‼️

這個很重要⚠️
可能大多數使用第三方推送的童鞋都不會注意到這個問題,一般現在的第三方推送都是將DeviceToken原始數據丟進去,具體的解析都是第三方內部處理,所以,這些第三方解析DeviceToken的方式正確的話,那就毫無問題。如果你們是通過這種方式來獲取DeviceToken,那你需要注意了。(這個坑也是多年前埋下的,很多文章介紹的也是下面這個方法,不規範的做法遲早要還的?),如下:

NSString *dt = [deviceToken description];
dt = [dt stringByReplacingOccurrencesOfString: @"<" withString: @""];
dt = [dt stringByReplacingOccurrencesOfString: @">" withString: @""];
dt = [dt stringByReplacingOccurrencesOfString: @" " withString: @""];

這段代碼運行在 iOS 13 上已經無法獲取到準確的DeviceToken字符串了,iOS 13 通過[deviceToken description]獲取到的內容已經變了。

{length = 32, bytes = 0x778a7995 29f32fb6 74ba8167 b6bddb4e ... b4d6b95f 65ac4587 }

可以看到,跟原來我們認識的那個已經完全不一樣了。其實,造成這樣的問題,主要還是沒有使用正確的方式來操作,下面是解決辦法:

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);
}

 Swift版本貼上?

 var deviceTokenString = String()
        let bytes = [UInt8](deviceToken)
        for item in bytes {
            deviceTokenString += String(format:"%02x", item&0x000000FF)
        }
       printLog("deviceToken:\(deviceTokenString)")

5.Sign in with Apple (提供第三方登錄的注意啦⚠️)

如果你的應用使用了第三方登錄,那麼你可能也需要加下 「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.

關於如何集成,可以參考這篇文章:《Sign in with Apple》
附上官方Demo:點我下載

6.即將廢棄的 LaunchImage

從 iOS 8 的時候,蘋果就引入了 LaunchScreen,我們可以設置 LaunchScreen來作爲啓動頁。當然,現在你還可以使用LaunchImage來設置啓動圖。不過使用LaunchImage的話,要求我們必須提供各種屏幕尺寸的啓動圖,來適配各種設備,隨着蘋果設備尺寸越來越多,這種方式顯然不夠 Flexible。而使用 LaunchScreen的話,情況會變的很簡單, LaunchScreen是支持AutoLayout+SizeClass的,所以適配各種屏幕都不在話下。

注意⚠️: 從2020年4月開始,所有使⽤ iOS13 SDKApp 將必須提供 LaunchScreenLaunchImage即將退出歷史舞臺。

再補充一點,在使用 LaunchScreen的時候,裏面用到的圖片資源,最好別放在 xcassets 裏面,不然在你修改圖片後,你會發現真機上並不會生效。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章