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
不再单纯表示黑色,而是会根据情况设置成lightContent
或darkContent
,也就是说,浅色模式设置default
状态栏会显示黑色,而暗黑模式设置default
状态栏会显示白色。lightContent
和darkContent
分别代表设置状态栏颜色为白色和黑色。项目中可能有多个页面会修改状态栏颜色,那么以前方法就要修改下。
注意⚠️:项目中以后修改状态栏颜色一律使用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到二级页面,二级页面有个操作,把二级页面最小化到一个窗口上,实现方法就是首先获取keyWindow
:UIWindow *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;
就可以了。
未完待续。。。