1. 项目中通过StatusBar来获取手机当前状态,但是在iOS 13中便获取不到了
解决:通过UIStatusBarManager获取statusBar
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].keyWindow.windowScene.statusBarManager;
id _statusBar = nil;
if ([statusBarManager respondsToSelector:@selector(createLocalStatusBar)]) {
UIView *_localStatusBar = [statusBarManager performSelector:@selector(createLocalStatusBar)];
if ([_localStatusBar respondsToSelector:@selector(statusBar)]) {
_statusBar = [_localStatusBar performSelector:@selector(statusBar)];
}
}
2. 在 iOS 13 中部分方法属性不允许使用 valueForKey、setValue:forKey: 来获取或者设置私有属性,具体表现为在运行时会直接崩溃
// 替代方案 1,使用 iOS 13 的新属性 searchTextField
searchBar.searchTextField.placeholder = @"search";
// 替代方案 2,遍历获取指定类型的属性
- (UIView *)findViewWithClassName:(NSString *)className inView:(UIView *)view{
Class specificView = NSClassFromString(className);
if ([view isKindOfClass:specificView]) {
return view;
}
if (view.subviews.count > 0) {
for (UIView *subView in view.subviews) {
UIView *targetView = [self findSpecificView:className inView:subView];
if (targetView != nil) {
return targetView;
}
}
}
return nil;
}
// 调用方法
UITextField *textField = [self findViewWithClassName:@"UITextField" inView:_searchBar];
// 崩溃 api。获取 _placeholderLabel 不会崩溃,但是获取 _placeholderLabel 里的属性就会
[textField setValue:[UIColor blueColor] forKeyPath:@"_placeholderLabel.textColor"];
[textField setValue:[UIFont systemFontOfSize:20] forKeyPath:@"_placeholderLabel.font"];
// 替代方案 1,去掉下划线,访问 placeholderLabel
[textField setValue:[UIColor blueColor] forKeyPath:@"placeholderLabel.textColor"];
[textField setValue:[UIFont systemFontOfSize:20] forKeyPath:@"placeholderLabel.font"];
// 替代方案 2
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"输入" attributes:@{
NSForegroundColorAttributeName: [UIColor blueColor],
NSFontAttributeName: [UIFont systemFontOfSize:20]
}];
3. 推送的 deviceToken 获取到的格式发生变化
在 iOS 13 中,这种方法已经失效,NSData类型的 deviceToken 转换成的字符串变成了:
{length = 32, bytes = 0xd7f9fe34 69be14d1 fa51be22 329ac80d ... 5ad13017 b8ad0736 }
解决方案:
#include <arpa/inet.h>
- (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);
}
UISearchBar 黑线处理导致崩溃
之前为了处理搜索框的黑线问题,通常会遍历 searchBar 的 subViews,找到并删除 UISearchBarBackground,在 iOS13 中这么做会导致 UI 渲染失败,然后直接崩溃,崩溃信息如下:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Missing or detached view for search bar layout'
解决方案:
设置 UISearchBarBackground 的 layer.contents 为 nil:
for (UIView *view in _searchBar.subviews.lastObject.subviews) {
if ([view isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
// [view removeFromSuperview];
view.layer.contents = nil;
break;
}
}
方法弃用
1. UIWebView 将被禁止提交审核
在 iOS 13 推出后,苹果在 UIWebView 的说明上将其支持的系统范围定格在了 iOS 2 ~ iOS 12。目前,如果开发者将包含 UIWebView api 的应用更新上传到 App Store 审核后,其将会收到包含 ITMS-90809 信息的回复邮件,提示你在下一次提交时将应用中 UIWebView 的 api 移除。
解决方案
用 WKWebView 替代 UIWebView,确保所有 UIWebView 的 api 都要移除,如果需要适配 iOS 7 的可以通过 openURL 的方式在 Safari 打开。
2. 使用 UISearchDisplayController 导致崩溃
在 iOS 8 之前,我们在 UITableView 上添加搜索框需要使用 UISearchBar + UISearchDisplayController 的组合方式,而在 iOS 8 之后,苹果就已经推出了 UISearchController 来代替这个组合方式。在 iOS 13 中,如果还继续使用 UISearchDisplayController 会直接导致崩溃,崩溃信息如下:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'UISearchDisplayController is no longer supported when linking against this version of iOS. Please migrate your application to UISearchController.'
解决方案:
使用 UISearchController 替换 UISearchBar + UISearchDisplayController 的组合方案。
3. MPMoviePlayerController 被弃用
在 iOS 9 之前播放视频可以使用 MediaPlayer.framework 中的MPMoviePlayerController类来完成,它支持本地视频和网络视频播放。但是在 iOS 9 开始被弃用,如果在 iOS 13 中继续使用的话会直接抛出异常:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'MPMoviePlayerController is no longer available. Use AVPlayerViewController in AVKit.'
解决方案
使用 AVFoundation 里的 AVPlayer 作为视频播放控件。
工程适配
1. 蓝牙权限字段更新导致崩溃以及提交审核失败
在 iOS 13 中,苹果将原来蓝牙申请权限用的 NSBluetoothPeripheralUsageDescription 字段,替换为 NSBluetoothAlwaysUsageDescription 字段。
For apps with a deployment target of iOS 13 and later, use NSBluetoothAlwaysUsageDescription instead.
如果在 iOS 13 中使用旧的权限字段获取蓝牙权限,会导致崩溃,崩溃信息如下:
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.
解决方案:
官网文档也有说明,就是在 Info.plist 中把两个字段都加上。
For deployment targets earlier than iOS 13, add both NSBluetoothAlwaysUsageDescription and NSBluetoothPeripheralUsageDescription to your app’s Information Property List file.
LaunchImage 被弃用
iOS 8 之前我们是在LaunchImage 来设置启动图,每当苹果推出新的屏幕尺寸的设备,我们需要 assets 里面放入对应的尺寸的启动图,这是非常繁琐的一个步骤。因此在 iOS 8 苹果引入了 LaunchScreen,可以直接在 Storyboard 上设置启动界面样式,可以很方便适配各种屏幕。
需要注意的是,苹果在 Modernizing Your UI for iOS 13 section 中提到
,从2020年4月开始,所有支持 iOS 13 的 App 必须提供 LaunchScreen.storyboard,否则将无法提交到 App Store 进行审批。
解决方案
使用 LaunchScreen.storyboard 设置启动页,弃用 LaunchImage
UISegmentedControl 默认样式改变
默认样式变为白底黑字,如果设置修改过颜色的话,页面需要修改。
UISegmentedControl原本设置选中颜色的 tintColor 已经失效,新增了 selectedSegmentTintColor 属性用以修改选中的颜色。
Xcode 11 创建的工程在低版本设备上运行黑屏
使用 Xcode 11 创建的工程,运行设备选择 iOS 13.0 以下的设备,运行应用时会出现黑屏。这是因为 Xcode 11 默认是会创建通过 UIScene 管理多个 UIWindow 的应用,工程中除了 AppDelegate 外会多一个 SceneDelegate:
这是为了 iPadOS 的多进程准备的,也就是说 UIWindow 不再是 UIApplication 中管理,但是旧版本根本没有 UIScene。
解决方案
在 AppDelegate 的头文件加上:
@property (strong, nonatomic) UIWindow *window;