iOS 13 适配

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

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:点我下载

Sign in with Apple 适配时间苹果已经确认,具体更新到了公众号同名文章,可以在文末扫码关注公众号进行查看。


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


9月24更新:

关于Dark Mode,这里补充几点。

因为苹果目前还没有强制必须适配这个,相信大家的很多项目也没有开始是配这个模式。所以,前期可以强制Light模式。不然,你可能会遇到一些问题,比如UITableViewCell的背景色,如果你没有设置过背景色的话,它在Dark模式下就是黑色的,再比如UIDatePicker文字颜色等等。

那么怎么强制模式呢?

iOS 13UIViewUIViewController都添加了一个属性:

@property (nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

给这个属性设置成某一种模式,即可强制显示模式。
如果你想修改一处,应用所有地方,那么你只需要设置widow的显示模式即可,这会影响widow下面的所有视图显示模式,这也可以看出显示模式是向下传递的。

if (@available(iOS 13, *)) {
   [self.window setOverrideUserInterfaceStyle:UIUserInterfaceStyleLight];
}

8.iOS 13 UITabBar顶部分割线隐藏

这个问题源自网友的提问,我的项目中并没有这样的需求,所以之前没有处理。
看到网上的一些解决办法,如下:

[UITabBar appearance].layer.borderWidth = 0.0f;
[UITabBar appearance].clipsToBounds = YES;

看了下,实际起作用的代码是这条代码:

[UITabBar appearance].clipsToBounds = YES;

也就是裁剪掉了多余的部分(多余的部分正好就是分割线,后面会提到为什么是分割线),达到隐藏分割线的目的。但是这样设置,TabBar会裁剪子视图,这样我们有大按钮的TabBar,按钮就会被裁剪,造成显示不完整。

那么在iOS 13上有没有别的办法来隐藏分割线呢?

肯定是有的,其实解决这样的问题,只要我们能找到这个视图,就可以解决问题。从这个角度出发,我们来看下iOS 13上面TabBar子视图都有哪些。直接打印下subviews即可。

(
    "<_UIBarBackground: 0x7fbb34007920; frame = (0 0; 414 83);",
    "<UITabBarButton: 0x7fbb2ed032d0; frame = (2 1; 410 48);"
)

我们可以看到内部有个_UIBarBackground私有的东西,分割线肯定跟他有关(总不可能跟UITabBarButton有关吧😝)。然后,我们在看下这个控件的子视图。

(
    "<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;
         }
     }];
 }];

运行后,发现分割线隐藏了,也就证明了我们的猜想。当然,上面这个代码不严谨哈,这里主要是提供一种思路,仅作为演示,具体怎么做相信大家都可以的。

总结下怎么解决:找到它》隐藏它。千万别通过KVC的方式去处理,理由看第一点。

9.UIWebView被废弃

"No longer supported; please adopt WKWebView.", ios(2.0, 12.0)

这个大家也尽快适配吧,看到有提到审核被拒的问题。

10.iOS 13 需增加蓝牙权限描述

简友 @funkep 提到最新一次提审被拒情况,被拒原因之一就是需要添加蓝牙权限获取描述。如果你的应用需要使用蓝牙权限,需要在Info.plist里面加上NSBluetoothAlwaysUsageDescription这个key,对应的描述value根据权限的用途来描述即可。

感谢 @funkep 的分享,大家遇到的被拒理由可以反馈到留言区,我会更新到文章里面,让更多人看到。


如果这篇文章对你有帮助,不妨随手点个赞!谢谢❤️

⚠️禁止未授权转载,只接收链接转载,不接受内容拷贝转载

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260