iOS13 适配(持续更新中。。。)

iOS13适配

1.UITextField的私有属性 _placeholderLabel 被禁止访问了
遇到的崩溃代码如下:

[_textField setValue:[UIColor colorWithHex:0xb0b0b0] forKeyPath:@"_placeholderLabel.textColor"];
[self.txtField setValue:[UIFont systemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];

收到的错误信息:
Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug
修改方法:使用attributedPlaceholder属性

NSMutableAttributedString *placeholderString = [[NSMutableAttributedString alloc] initWithString:placeholder attributes:@{NSForegroundColorAttributeName : [UIColor colorWithHex:0xb0b0b0], NSFontAttributeName : [UIFont systemFontOfSize:16]}];
self.textField.attributedPlaceholder = placeholderString;

设置完颜色再使用textField.placeholder = @"asdasdasd";更改placeholder时,新的placeholder也会显示之前设置的的颜色和大小,不需要重新写AttributedString
注意⚠️,设置字体颜色大小时placeholderString长度不能为0,也就是不能是@“”,否则设置无效!默认placeholder颜色70%灰色。

同样UISearchBar的私有属性 _searchField 也被禁止访问了,但是增加了属性 searchTextField 可以直接进行修改里面的内容。

UITextField * searchField;
if (@available (iOS 13.0, *)) {
    searchField = _searchBar.searchTextField;
} else {
    searchField = [_searchBar valueForKey:@"_searchField"];
}

上面方法用到iOS 13新的API,如果多人协作开发,别人没有升级xcode就会报错,可以使用下面方法:

UITextField * searchField;
searchField = [_searchBar valueForKey:@"searchField"];

直接去掉下划线即可,亲测可行!

注意⚠️,iOS13通过KVC方式修改私有属性,有Crash风险,谨慎使用! �

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

并且直接修改了 modalPresentationStyle 属性的默认值:

/*
 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 system-provided subclasses may resolve UIModalPresentationAutomatic to other concrete presentation styles. Participation in the resolution of UIModalPresentationAutomatic is reserved for system-provided view controllers.
 Defaults to UIModalPresentationAutomatic on iOS starting in iOS 13.0, and UIModalPresentationFullScreen on previous versions. Defaults to UIModalPresentationFullScreen on all other platforms.
 */
@property(nonatomic,assign) UIModalPresentationStyle modalPresentationStyle API_AVAILABLE(ios(3.2));

如果你觉得这个效果OK,那大可不必修改,如果你原来设置过 modalPresentationStyle 的值,那对你也不会有影响。如果你想恢复以前交互效果,可执行以下代码:

self.modalPresentationStyle = UIModalPresentationOverFullScreen;

如果项目中有很多地方,那么一个一个改会很麻烦,可以使用方法交换,判断一下modalPresentationStyle 属性,并且进行修改,代码如下:

#import "UIViewController+JWFixIOS13.h"

@implementation UIViewController (JWFixIOS13)

+ (void)load
{
    Method carshMethod = class_getInstanceMethod([self class], @selector(presentViewController: animated: completion:));
    Method newMethod = class_getInstanceMethod([self class], @selector(jw_presentViewController: animated: completion:));
    method_exchangeImplementations(carshMethod, newMethod);
}

- (void)jw_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
{
    if (@available(iOS 13.0, *)) {
        // iOS13以后style默认UIModalPresentationAutomatic,以前版本xcode没有这个枚举,所以只能写-2
        if (viewControllerToPresent.modalPresentationStyle == -2 || viewControllerToPresent.modalPresentationStyle == UIModalPresentationPageSheet) {
            viewControllerToPresent.modalPresentationStyle = UIModalPresentationOverFullScreen;
        }
    }
    [self jw_presentViewController:viewControllerToPresent animated:flag completion:completion];
}

@end

注意: UIModalPresentationOverFullScreen 最低支持 iOS8, 如果你还要支持更低版本,你可以用 UIModalPresentationFullScreen ,与 UIModalPresentationFullScreen 的唯一区别在于,UIWindow下除了presented VC,还有其他正常的VC层级关系。也就是说该模式下,UIKit以 rootViewController 为 presentation context,但 presentation 完成之后不会将rootViewController移出当前的UI栈。使用UIModalPresentationOverFullScreen前一个页面不会走viewWillDisAppear方法,而UIModalPresentationFullScreen会走,如果在viewWillDisAppera方法里面有逻辑处理,这个也是需要注意的地方。

3.首页frame莫名下移20像素问题
ContainerViewController的xib size为Inferred view的y会莫名变成20 把 size 改成 FreeForm 就好了 暂时不知道原因原理

4.iOS13 DeviceToken有变化!
可能大多数使用第三方推送的童鞋都不会注意到这个问题,一般现在的第三方推送都是将deviceToken原始数据丢进去,具体的解析都是第三方内部处理,所以,如果这些第三方解析deviceToken的方式正确的话,那就没有问题。如果你们用以下方式来处理deviceToken,那就需要注意修改了!

_pushToken = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];

原因是iOS 13以后,无法通过description直接获取字符串了,iOS13以后,[deviceToken description]获取内容如下:
{length=32,bytes=0xa4e1bbf547ad55ffac9b8267801c6901...ace88f4c12e681a5}
而iOS 13以前,[deviceToken description]获取内容如下:
<db9c6e58 237049ae c588e594 1b4d8c8c 7f8c5cbf c843d801 57f7b328 7912b5ab>
可以看到,现在[deviceToken description]获取内容已经完全不一样了,下面是解决办法:

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

或者你也可以用友盟提供的方法

#include <arpa/inet.h>

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    if (![deviceToken isKindOfClass:[NSData class]]) return;
    const unsigned *tokenBytes = (const unsigned *)[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. MPMoviePlayerController 在iOS 13已经不能用了
在使用到MPMoviePlayerController的地方,直接抛了异常:

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

如何修改:
这个没啥好说的,既然不能再用了,那只能换掉了。替代方案就是AVKit里面的那套播放器。

6.Sign in with Apple
如果你的应用有第三方登录,那你需要加上 sign in with apple
关于如何集成,可以参考下面这个文章:Sing in with apple

7.Dark Mode
iOS 13新出了暗黑模式,如果项目不进行适配,开启暗黑模式运行app就会让人感到异常酸爽。苹果官方强烈建议适配暗黑模式,以后不适配的新项目很可能会被拒,但是毕竟现在还只是beta版本,而且适配起来感觉很麻烦,目前我们项目的方法是全局关闭暗黑模式
1.在info.plist 文件中,添加UIUserInterfaceStyle key名字为User Interface Style类型为String
2.将UIUserInterfaceStyle key的值设置为Light
这样即使开启暗黑模式,项目中也不会有颜色问题,但是!!!!遇到一个新的问题,写在第8项。

8.UIStatusBarStyle
iOS 13中,状态栏新增了一个样式:

typedef NS_ENUM(NSInteger, UIStatusBarStyle) {
    UIStatusBarStyleDefault                                  = 0, // Automatically chooses light or dark content based on the user interface style
    UIStatusBarStyleLightContent     API_AVAILABLE(ios(7.0)) = 1, // Light content, for use on dark backgrounds
    UIStatusBarStyleDarkContent     API_AVAILABLE(ios(13.0)) = 3, // Dark content, for use on light backgrounds
    
    UIStatusBarStyleBlackTranslucent NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 1,
    UIStatusBarStyleBlackOpaque      NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 2,
} API_UNAVAILABLE(tvos);

之前statusBar有两种状态,黑色:default,白色:lightContent。iOS13以后,新增了一种状态:darkContent,并且以前状态代表的颜色也有所变化。default不再单纯表示黑色,而是会根据情况设置成lightContentdarkContent,也就是说,浅色模式设置default状态栏会显示黑色,而暗黑模式设置default状态栏会显示白色。lightContentdarkContent分别代表设置状态栏颜色为白色和黑色。项目中可能有多个页面会修改状态栏颜色,那么以前方法就要修改下。
注意⚠️:项目中以后修改状态栏颜色一律使用JWTools!!!!!!
代码如下:

typedef NS_ENUM(NSUInteger, JWStatusBarTextColor)
{
    JWStatusBarTextWhiteColor,
    JWStatusBarTextBlackColor,
};

+ (void)statusBarTextColor:(JWStatusBarTextColor)statusBarTextColor
{
    [self statusBarTextColor:statusBarTextColor animated:NO];
}

+ (void)statusBarTextColor:(JWStatusBarTextColor)statusBarTextColor animated:(BOOL)animated
{
    switch (statusBarTextColor) {
        case JWStatusBarTextBlackColor: {
            if (@available(iOS 13.0, *)) {
                // iOS13 以后 default会根据状态自动设置黑色还是白色, 需要用 UIStatusBarStyleDarkContent 设置黑色 低版本xcode没有这个枚举 只能用3
                [[UIApplication sharedApplication] setStatusBarStyle:3];
            } else {
                [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
            }
            break;
        }
        case JWStatusBarTextWhiteColor: {
            [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:animated];
            break;
        }
    }
}

这样写的好处的万一以后有增加红色、绿色之类的,只需要增加一个枚举值,外面只需要设置好我想要的颜色,不需要关心style类型以及对应的颜色,只需要在这个方法里做对应的修改即可。

9.UISearchBar背景颜色
直接贴代码吧。。。

//设置背景色
    if (@available (iOS 13.0, *)) {
        [self.searchController.searchBar setBackgroundImage:[self imageWithColor:[UIColor whiteColor]] forBarPosition:(UIBarPositionAny) barMetrics:(UIBarMetricsDefault)];
    } else {
        [self.searchController.searchBar setBackgroundColor:[UIColor clearColor]];
    }
- (UIImage *)imageWithColor: (UIColor *)color {
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);
    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return theImage;
}

10.UISegmentedControl
iOS 13中UISegmentedControl样式变了,如果设置TintColor会发现没有作用,如果你觉得iOS 13默认样式OK,那大可不必修改,如果你想把样式变成和原来一样,那么就需要做出修改,代码如下:

UISegmentedControl *segmentControl = [[UISegmentedControl alloc] initWithItems:@[@"asdasd", @"奥术大师多", @"123456"]];
segmentControl.frame = CGRectMake(30, 200, 300, 50);
[self.view addSubview:segmentControl];

UIColor *color = [UIColor redColor];
if (@available(iOS 13.0, *)) {
    // 修改选中标题颜色
    [segmentControl setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]} forState:UIControlStateSelected];
    // 修改未选中标题颜色
    [segmentControl setTitleTextAttributes:@{NSForegroundColorAttributeName : color} forState:UIControlStateNormal];
    // 修改未选中背景颜色 color转image方法上面代码里有
    [segmentControl setBackgroundImage:[self imageWithColor:[UIColor whiteColor]] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    // 修改选中背景颜色
    [segmentControl setBackgroundImage:[self imageWithColor:color] forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
    // 修改分割线颜色
    [segmentControl setDividerImage:[self imageWithColor:color] forLeftSegmentState:UIControlStateSelected rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    // 设置描边
    segmentControl.layer.borderWidth = 1;
    segmentControl.layer.borderColor = color.CGColor;
    // 设置圆角,区分iOS版本号时可以不写圆角
    segmentControl.layer.cornerRadius = 5;
    segmentControl.layer.masksToBounds = YES;
} else {
    [segmentControl setTintColor:color];
}

如果你想区分iOS版本且不影响原来逻辑只是适配iOS 13的话,那可以不写圆角代码,如果不判断的话,需要加上圆角代码,这样以前版本也会显示你想要的样式,就不用判断iOS 13啦!


2019.9.29
11.UITabBarController
问题场景如下:页面push到二级页面,二级页面有个操作,把二级页面最小化到一个窗口上,实现方法就是首先获取keyWindowUIWindow *window = [UIApplication sharedApplication].keyWindow;,然后在keyWindow 添加一个view

[window addSubview:self.floatingView];
[window bringSubviewToFront:self.floatingView];

之后tabBar的选中文字颜色就变成系统默认的蓝色。
设置选中字体颜色代码:[vc.tabBarItem setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys: selectedColor, NSForegroundColorAttributeName, nil] forState:UIControlStateSelected];修改方法,加上tintColor设置即可self.tabBar.tintColor = selectedColor;。原因未知,有了解的可以交流下哈!

12.UILabel的text与attributedText

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 300, 40)];
label.backgroundColor = [UIColor redColor];
label.textColor = [UIColor greenColor];
    
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:@"attText:按实际的规划建安公司的环境" attributes:@{NSForegroundColorAttributeName : [UIColor purpleColor]}];
    
label.attributedText = str;
label.text = @"text:啊手机客户端规划局安徽省给大家看";

如图

测试代码截图

<font color = pink>iOS 13</font>以前,label显示文字颜色会是绿色,如下面样式,但是<font color = pink>iOS 13</font>以后,label显示文字就会变成紫色,如上面样式。如果还想显示绿色,那么需要设置label.attributedText = nil;就可以了。

未完待续。。。

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

推荐阅读更多精彩内容