1. KVC访问私有属性
2. 模态d窗VIEwController 默认样式改变
3. 黑暗模式的适配
4. LaunchImage即将废弃
5. 新增一直使用蓝牙的权限申请
6. Sign With Apple
7. 推送Device Token适配
8. UIKit 控件变化
9. Statusbar新增样式
1. KVC访问私有属性 这次iOS 13系统升级,影响范围最广的应属KVC访问修改私有属性了,直接禁止开发者获取或直接设置私有属性。而KVC的初衷是允许开发者通过Key名直接访问修改对象的属性值,为其中最典型的 UITextFIEld
的 _placeholderLabel
、UISearchbar
的 _searchFIEld
。 造成影响:在iOS 13下App闪退 错误代码:
// placeholderLabel私有属性访问
[textFIEld setValue:[UIcolor redcolor] forKeyPath:@"_placeholderLabel.textcolor"];
[textFIEld setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.Font"];
// searchFIEld私有属性访问
UISearchbar *searchbar = [[UISearchbar alloc] init];
UITextFIEld *searchTextFIEld = [searchbar valueForKey:@"_searchFIEld"];
解决方案: 使用 NSMutableAttributedString
富文本来替代KVC访问 UITextFIEld
的 _placeholderLabel
textFIEld.attributedplaceholder = [[NSAttributedString alloc] initWithString:@"placeholder" attributes:@{NSForegroundcolorAttributename: [UIcolor darkGraycolor], NSFontAttributename: [UIFont systemFontOfSize:13]}];
因此,可以为UITextFeild
创建category
,专门用于处理修改placeHolder
属性提供方法
#import "UITextFIEld+ChangePlaceholder.h"
@implementation UITextFIEld (Change)
- (voID)setPlaceholderFont:(UIFont *)Font {
[self setPlaceholdercolor:nil Font:Font];
}
- (voID)setPlaceholdercolor:(UIcolor *)color {
[self setPlaceholdercolor:color Font:nil];
}
- (voID)setPlaceholdercolor:(nullable UIcolor *)color Font:(nullable UIFont *)Font {
if ([self checkPlaceholderEmpty]) {
return;
}
NSMutableAttributedString *placeholderAttriString = [[NSMutableAttributedString alloc] initWithString:self.placeholder];
if (color) {
[placeholderAttriString addAttribute:NSForegroundcolorAttributename value:color range:NSMakeRange(0, self.placeholder.length)];
}
if (Font) {
[placeholderAttriString addAttribute:NSFontAttributename value:Font range:NSMakeRange(0, self.placeholder.length)];
}
[self setAttributedplaceholder:placeholderAttriString];
}
- (BOol)checkPlaceholderEmpty {
return (self.placeholder == nil) || ([[self.placeholder stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0);
}
关于 UISearchbar,可遍历其所有子视图,找到指定的 UITextFIEld 类型的子视图,再根据上述 UITextFIEld 的通过富文本方法修改属性。
#import "UISearchbar+ChangePrivateTextFIEldSubvIEw.h"
@implementation UISearchbar (ChangePrivateTextFIEldSubvIEw)
/// 修改Searchbar系统自带的TextFIEld
- (voID)changeSearchTextFIElDWithCompletionBlock:(voID(^)(UITextFIEld *textFIEld))completionBlock {
if (!completionBlock) {
return;
}
UITextFIEld *textFIEld = [self findTextFIElDWithVIEw:self];
if (textFIEld) {
completionBlock(textFIEld);
}
}
/// 递归遍历UISearchbar的子视图,找到UITextFIEld
- (UITextFIEld *)findTextFIElDWithVIEw:(UIVIEw *)vIEw {
for (UIVIEw *subvIEw in vIEw.subvIEws) {
if ([subvIEw isKindOfClass:[UITextFIEld class]]) {
return (UITextFIEld *)subvIEw;
}else if (subvIEw.subvIEws.count > 0) {
return [self findTextFIElDWithVIEw:subvIEw];
}
}
return nil;
}
@end
PS:关于如何查找自己的App项目是否使用了私有API,可以参考 iOS查找私有API 文章
2. 模态d窗 VIEwController 默认样式改变 模态d窗属性 UIModalPresentationStyle
在 iOS 13 下默认被设置为 UIModalPresentationautomatic
新特性,展示样式更为炫酷,同时可用下拉手势关闭模态d窗。 若原有模态d出 VIEwController 时都已指定模态d窗属性,则可以无视该改动。 若想在 iOS 13 中继续保持原有默认模态d窗效果。可以通过 runtime 的 Method Swizzling
方法交换来实现。
#import "UIVIEwController+ChangeDefaultPresentStyle.h"
@implementation UIVIEwController (ChangeDefaultPresentStyle)
+ (voID)load {
static dispatch_once_t oncetoken;
dispatch_once(&oncetoken, ^{
Class class = [self class];
//替换方法
SEL originalSelector = @selector(presentVIEwController:animated:completion:);
SEL newSelector = @selector(new_presentVIEwController:animated:completion:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method newMethod = class_getInstanceMethod(class, newSelector);;
BOol dIDAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(newMethod),
method_getTypeEnCoding(newMethod));
if (dIDAddMethod) {
class_replaceMethod(class,
newSelector,
method_getImplementation(originalMethod),
method_getTypeEnCoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
});
}
- (voID)new_presentVIEwController:(UIVIEwController *)vIEwControllertopresent animated:(BOol)flag completion:(voID (^)(voID))completion {
vIEwControllertopresent.modalPresentationStyle = UIModalPresentationFullScreen;
[self new_presentVIEwController:vIEwControllertopresent animated:flag completion:completion];
}
@end
#import "UIVIEwController+ChangeDefaultPresentStyle.h"
@implementation UIVIEwController (ChangeDefaultPresentStyle)
+ (voID)load {
static dispatch_once_t oncetoken;
dispatch_once(&oncetoken, newMethod);
}
});
}
- (voID)new_presentVIEwController:(UIVIEwController *)vIEwControllertopresent animated:(BOol)flag completion:(voID (^)(voID))completion {
vIEwControllertopresent.modalPresentationStyle = UIModalPresentationFullScreen;
[self new_presentVIEwController:vIEwControllertopresent animated:flag completion:completion];
}
@end
3. 黑暗模式的适配针对黑暗模式的推出,Apple官方推荐所有三方App尽快适配。目前并没有强制App进行黑暗模式适配。因此黑暗模式适配范围现在可采用以下三种策略:
全局关闭黑暗模式
指定页面关闭黑暗模式
全局适配黑暗模式
3.1. 全局关闭黑暗模式方案一:在项目 Info.pList
文件中,添加一条内容,Key为 User Interface Style
,值类型设置为String并设置为 light
即可。
方案二:代码强制关闭黑暗模式,将当前 window 设置为 light 状态。
if(@available(iOS 13.0,*)){
self.window.overrIDeUserInterfaceStyle = UIUserInterfaceStylelight;
}
3.2 指定页面关闭黑暗模式
从Xcode 11、iOS 13开始,UIVIEwController与VIEw新增属性 overrIDeUserInterfaceStyle
,若设置VIEw对象该属性为指定模式,则强制该对象以及子对象以指定模式展示,不会跟随系统模式改变。
设置 VIEwController 该属性, 将会影响视图控制器的视图以及子视图控制器都采用该模式
设置 VIEw 该属性, 将会影响视图及其所有子视图采用该模式
设置 Window 该属性, 将会影响窗口中的所有内容都采用该样式,包括根视图控制器和在该窗口中显示内容的所有控制器
3.3 全局适配黑暗模式适配黑暗模式,主要从两方面入手:图片资源适配与颜色适配
图片资源适配 打开图片资源管理库 Assets.xcassets
,选中需要适配的图片素材item,打开最右侧的 Inspectors 工具栏,找到 Appearances 选项,并设置为 Any,Dark
模式,此时会在item下增加Dark Appearance,将黑暗模式下的素材拖入即可。关于黑暗模式图片资源的加载,与正常加载图片方法一致。
颜色适配
iOS 13开始UIcolor变为动态颜色,在light Mode与Dark Mode可以分别设置不同颜色。 若UIcolor色值管理,与图片资源一样存储于 Assets.xcassets
中,同样参照上述方法适配。 若UIcolor色值并没有存储于 Assets.xcassets
情况下,自定义动态UIcolor时,在iOS 13下初始化方法增加了两个方法
+ (UIcolor *)colorWithDynamicProvIDer:(UIcolor * (^)(uitraitcollection *))dynamicProvIDer API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
- (UIcolor *)initWithDynamicProvIDer:(UIcolor * (^)(uitraitcollection *))dynamicProvIDer API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
这两个方法要求传一个block,block会返回一个 uitraitcollection 类
当系统在黑暗模式与正常模式切换时,会触发block回调 示例代码:
UIcolor *dynamiccolor = [UIcolor colorWithDynamicProvIDer:^UIcolor * _Nonnull(uitraitcollection * _Nonnull trainCollection) {
if ([trainCollection userInterfaceStyle] == UIUserInterfaceStylelight) {
return [UIcolor whitecolor];
} else {
return [UIcolor blackcolor];
}
}];
[self.vIEw setBackgroundcolor:dynamiccolor];
当然了,iOS 13系统也默认提供了一套基本的黑暗模式UIcolor动态颜色,具体声明如下:
@property (class, nonatomic, Readonly) UIcolor *systembrowncolor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, Readonly) UIcolor *systemIndigocolor API_AVAILABLE(ios(13.0), Readonly) UIcolor *systemGray2color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, Readonly) UIcolor *systemGray3color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *systemGray4color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *systemGray5color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *systemGray6color API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *labelcolor API_AVAILABLE(ios(13.0), Readonly) UIcolor *secondaryLabelcolor API_AVAILABLE(ios(13.0), Readonly) UIcolor *tertiaryLabelcolor API_AVAILABLE(ios(13.0), Readonly) UIcolor *quaternaryLabelcolor API_AVAILABLE(ios(13.0), Readonly) UIcolor *@R_403_6862@color API_AVAILABLE(ios(13.0), Readonly) UIcolor *placeholderTextcolor API_AVAILABLE(ios(13.0), Readonly) UIcolor *separatorcolor API_AVAILABLE(ios(13.0), Readonly) UIcolor *opaqueSeparatorcolor API_AVAILABLE(ios(13.0), Readonly) UIcolor *systemBackgroundcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *secondarySystemBackgroundcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *tertiarySystemBackgroundcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *systemGroupedBackgroundcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *secondarySystemGroupedBackgroundcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *tertiarySystemGroupedBackgroundcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *systemFillcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *secondarySystemFillcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *tertiarySystemFillcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Readonly) UIcolor *quaternarySystemFillcolor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
监听模式的切换
当需要监听系统模式发生变化并作出响应时,需要用到 VIEwController 以下函数
// 注意:参数为变化前的traitCollection,改函数需要重写
- (voID)traitCollectionDIDChange:(uitraitcollection *)prevIoUsTraitCollection;
// 判断两个uitraitcollection对象是否不同
- (BOol)hasDifferentcolorAppearanceComparedToTraitCollection:(uitraitcollection *)traitCollection;
示例代码:
- (voID)traitCollectionDIDChange:(uitraitcollection *)prevIoUsTraitCollection {
[super traitCollectionDIDChange:prevIoUsTraitCollection];
// trait has Changed?
if ([self.traitCollection hasDifferentcolorAppearanceComparedToTraitCollection:prevIoUsTraitCollection]) {
// do something...
}
}
系统模式变更,自定义重绘视图当系统模式变更时,系统会通知所有的 VIEw以及 VIEwController 需要更新样式,会触发以下方法执行(参考Apple官方适配链接):
NSVIEw
- (voID)updateLayer;
- (voID)drawRect:(NSRect)dirtyRect;
- (voID)layout;
- (voID)updateConstraints;
UIVIEw
- (voID)traitCollectionDIDChange:(uitraitcollection *)prevIoUsTraitCollection;
- (voID)layoutSubvIEws;
- (voID)drawRect:(NSRect)dirtyRect;
- (voID)updateConstraints;
- (voID)tintcolorDIDChange;
UIVIEwController
- (voID)traitCollectionDIDChange:(uitraitcollection *)prevIoUsTraitCollection;
- (voID)updateVIEwConstraints;
- (voID)vIEwWillLayoutSubvIEws;
- (voID)vIEwDIDLayoutSubvIEws;
UIPresentationController
- (voID)traitCollectionDIDChange:(uitraitcollection *)prevIoUsTraitCollection;
- (voID)containerVIEwWillLayoutSubvIEws;
- (voID)containerVIEwDIDLayoutSubvIEws;
4. LaunchImage即将废弃使用 LaunchImage 设置启动图,需要提供各类屏幕尺寸的启动图适配,这种方式随着各类设备尺寸的增加,增加了额外不必要的工作量。为了解决 LaunchImage 带来的弊端,iOS 8引入了 LaunchScreen 技术,因为支持 autoLayout + SizeClass,所以通过 LaunchScreen 就可以简单解决适配当下以及未来各种屏幕尺寸。 Apple官方已经发出公告,2020年4月开始,所有使用iOS 13 SDK 的App都必须提供 LaunchScreen。 创建一个 LaunchScreen 也非常简单 (1)New files创建一个 LaunchScreen,在创建的 VIEwController 下 VIEw 中新建一个 Image,并配置 Image 的图片 (2)调整 Image 的 frame 为占满屏幕,并修改 Image 的 autoresizing 如下图,完成
5. 新增一直使用蓝牙的权限申请在iOS13之前,无需权限提示窗即可直接使用蓝牙,但在iOS 13下,新增了使用蓝牙的权限申请。最近一段时间上传IPA包至App Store会收到以下提示。
解决方案:只需要在 Info.pList
里增加以下条目:
NSBluetoothAlwaysUsageDescription 这里输入使用蓝牙来做什么
6. Sign With Apple在iOS 13系统中,Apple要求提供第三方登录的App也要支持「Sign With Apple」,具体实践参考 iOS Sign With Apple实践
7. 推送Device Token适配在iOS 13之前,获取Device Token 是将系统返回的 NSData
类型数据通过 -(voID)description;
方法直接转换成 Nsstring
字符串。 iOS 13之前获取结果:
iOS 13之后获取结果:
适配方案: 目的是要将系统返回 NSData
类型数据转换成字符串,再传给推送服务方。-(voID)description;
本身是用于为类调试提供相关的打印信息,严格来说,不应直接从该方法获取数据并应用于正式环境中。将 NSData
转换成 HexString
,即可满足适配需求。
- (Nsstring *)getHexStringForData:(NSData *)data {
NSUInteger length = [data length];
char *chars = (char *)[data bytes];
NSMutableString *hexString = [[NSMutableString alloc] init];
for (NSUInteger i = 0; i < length; i++) {
[hexString appendString:[Nsstring stringWithFormat:@"%0.2hhx", chars[i]]];
}
return hexString;
}
主要还是参照了Apple官方的 UIKit 修改文档声明。iOS 13 Release Notes
8.1. UItableVIEwiOS 13下设置 cell.contentVIEw.backgroundcolor
会直接影响 cell 本身 selected 与 highlighted 效果。 建议不要对 contentVIEw.backgroundcolor
修改,而对 cell
本身进行设置。
iOS 13之后,Badge 字体默认由13号变为17号。 建议在初始化 TabbarController 时,显示 Badge 的 VIEwController 调用 setBadgeTextAttributes:forState:
方法
if (@available(iOS 13, *)) {
[vIEwController.tabbarItem setBadgeTextAttributes:@{NSFontAttributename: [UIFont systemFontOfSize:13]} forState:UIControlStatenormal];
[vIEwController.tabbarItem setBadgeTextAttributes:@{NSFontAttributename: [UIFont systemFontOfSize:13]} forState:UIControlStateSelected];
}
8.2. UITabbarItem 加载gif需设置 scale 比例NSData *data = [NSData dataWithContentsOffile:path];
CGImageSourceRef gifSource = CGImageSourceCreateWithData(CFBrIDgingRetain(data), nil);
size_t gifCount = CGImageSourceGetCount(gifSource);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(gifSource, i,NulL);
// iOS 13之前
UIImage *image = [UIImage imageWithCGImage:imageRef]
// iOS 13之后添加scale比例(该imageVIEw将展示该动图效果)
UIImage *image = [UIImage imageWithCGImage:imageRef scale:image.size.wIDth / CGRectGetWIDth(imageVIEw.frame) orIEntation:UIImageOrIEntationUp];
CGImageRelease(imageRef);
无文字时图片位置调整iOS 13下不需要调整 imageInsets
,图片会自动居中显示,因此只需要针对iOS 13之前的做适配即可。
if (IOS_VERSION < 13.0) {
vIEwController.tabbarItem.imageInsets = UIEdgeInsetsMake(5, 0, -5, 0);
}
TabbarItem选中颜色异常在 iOS 13下设置 tabbarItem 字体选中状态的颜色,在push到其它 VIEwController 再返回时,选中状态的 tabbarItem 颜色会变成默认的蓝色。
设置 tabbar 的 tintcolor 属性为原本选中状态的颜色即可。
self.tabbar.tintcolor = [UIcolor redcolor];
8.3. 新增 Diffable DataSource在 iOS 13下,对 UItableVIEw 与 UICollectionVIEw 新增了一套 Diffable DataSource API。为了更高效地更新数据源@R_84_6419@,避免了原有粗暴的刷新方法 - (voID)reloadData
,以及手动调用控制列表刷新范围的API,很容易出现计算不准确造成 NSInternalinconsistencyException 而引发App crash。 api 官方链接
Statusbar 新增一种样式,默认的 default 由之前的黑色字体,变为根据系统模式自动选择展示 lightContent 或者 darkContent
总结以上是内存溢出为你收集整理的iOS 13原生端适配攻略全部内容,希望文章能够帮你解决iOS 13原生端适配攻略所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)