iOS 适配遇坑集锦

1、崩溃:iOS系统搜索数据组件 UISearchBar 闪退

原因:iOS 13后 UISearchBar引入了一个新的类叫UISearchTextField,并且在iOS13中引入了名为searchTextField的属性。iOS 13前后获取UISearchBar中的UITextField方法不同,没有加判断引起

解决:iOS 13之前通过 [self.searchBar valueForKey:@"_searchField"]; iOS 13之后通过遍历UISearchBar找到或者通过 self.searchBar.searchTextField获取。

2、iOS15 设置了section高度的列表会出现head高度增加的情况

原因:iOS15对于tableview,新增了sectionHeaderTopPadding作为列表每个部分标题上方的填充,它的默认值是UITableViewAutomaticDimension,所以我们要将他设置为0,否则当我们的列表设置了section高度的列表会出现head高度增加22px的情况

解决:

  • 全局设置(推荐)可以在 AppDelegate中直接全局设置生效,目前未发现不良影响
if (@available(iOS 15.0, *)) {

   [UITableView appearance].sectionHeaderTopPadding = 0;

}
复制代码
  • 部分页面设置 针对单独某个UITableView设置
if (@available(iOS 15.0, *)) {

    self.tableView.sectionHeaderTopPadding = 0;

}
复制代码

3、iOS15 导航栏显示异常,设置背景颜色或设置某种颜色或透明或颜色变化无效果

原因:iOS15 navigationBar的相关属性设置要通过实例UINavigationBarAppearance来实现,UINavigationBarAppearance是iOS13更新的API,应该有人已经在用,我们的应用兼容iOS10以上,对于导航栏的设置还没有使用UINavigationBarAppearance,如今在iOS15上失效;

解决1:创建UINavigationController分类,增加如下方法:

/// 设置导航栏颜色

- (void)setNavigationBackgroundColor:(UIColor *)color {
   NSDictionary *dic = @{
       NSForegroundColorAttributeName : [UIColor whiteColor],
       NSFontAttributeName : [UIFont systemFontOfSize:18]
   };

   if (@available(iOS 15.0, *)) {
   // 滚动状态
      UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
   // 设置为不透明 a
      ppearance.backgroundEffect = nil;
      appearance.backgroundImage = [UIImage imageWithColor:color];
      appearance.shadowColor = color;
      appearance.backgroundColor = color;
   // 静止状态
      UINavigationBarAppearance *appearance2 = [[UINavigationBarAppearance alloc] init];
   // 设置为不透明
      appearance2.backgroundEffect = nil;
      appearance2.backgroundImage = [UIImage imageWithColor:color];
      appearance2.shadowColor = color;
      appearance2.backgroundColor = color;
      self.navigationBar.scrollEdgeAppearance = appearance;
      self.navigationBar.standardAppearance = appearance2;
    } else {
      self.navigationBar.titleTextAttributes = dic;
      [self.navigationBar setShadowImage:[[UIImage alloc] init]];
      [self.navigationBar setBackgroundImage:[UIImage imageWithColor:color] forBarMetrics:UIBarMetricsDefault];
    }
}
复制代码

将原来iOS15之前的如下代码:

UIImage *image = [UIImage imageWithColor:color]; [self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault]; [self.navigationController.navigationBar setShadowImage:image];

修改为:

[self.navigationController setNavigationBackgroundColor:color];

其中color为当前导航栏的颜色,可根据项目实际情况设置颜色

4、iOS15 导航的毛玻璃效果

原因:在iOS15中,UINavigationBar默认是透明的,有滑动时会逐渐变为模糊效果,可以通过改变scrollEdgeAppearance属性直接变为模糊效果

解决:

if (@available(iOS 15.0, *)){
    UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
    appearance.backgroundEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular];
    navBar.scrollEdgeAppearance = appearance;
}
复制代码

5、iOS15 UIImageWriteToSavedPhotosAlbum存储图片回调中处理图片Crash

原因:iOS15中,UIImageWriteToSavedPhotosAlbum存储图片之后的回调不再返回图片了,会返回nil,如果在回调方法里面操作image有可能会直接Crash;

解决:目前的解决办法声明一个全局image去记录;

6、iOS14 Cell点击无效

原因:iOS14更改Cell视图布局.将contentView放在最上层,如果将视图加载在cell上,将会出现contentView遮罩,导致事件无法响应.是在此前关于 contentView 的声明注释中,官方已经明确建议开发者将 customView 放在 contentView 上,使 contentView 作为 UITableViewCell 默认的 fatherView。

解决:

  1. 可以将cell子视图加载在contentView上(提倡)
  2. 将contentView设置到最底层 self.sendSubviewToBack(self.contentView)

7、iOS14 相机权限设置 每次触发相册功能时都进行弹窗询问

原因:iOS14 新增了“Limited Photo Library Access” 模式,在授权弹窗中增加了 Select Photo 选项。用户可以在 App 请求调用相册时选择部分照片让 App 读取。从 App 的视⻆来看,你的相册里就只有这几张照片,App 无法得知其它照片的存在。

用户选择“PHAuthorizationStatusLimited” 时,如果未进行适配,有可能会在每次触发相册功能时都进行弹窗询问用户是否需要修改照片权限。

解决:对于这种情况可通过在 Info.plist 中设置

“PHPhotoLibraryPreventAutomaticLimitedAccessAlert”的值为 YES 来阻止该弹窗反复弹出,并且可通过下面这个 API 来主动控制何时弹出PHPickerViewController 进行照片选择

[[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];

官方推荐使用 PHPicker 来替代原 API 进行图片选择。PHPicker 为独立进程,会在视图最顶层进行展示,应用内无法对其进行截图也无法直接访问到其内的数据。

UIImagePickerController -> PHPickerViewController, UIImagePickerViewController 功能受限,每次只能选择一张图片,将逐渐被废弃。

PHPicker 支持多选,支持搜索,支持按 image,video,livePhotos 等进行选择。

8、iOS13.4 UIDatePicker 更新

原因: iOS13及以前,UIDatePicker样式只有轮播样式。 iOS13.4之后,UIDatePicker修改了默认的显示样式如下图

解决:如果还需要原来的滚动样式可以设置:

if (@available(iOS 13.4, *)) {
    datePicker.preferredDatePickerStyle = UIDatePickerStyleWheels; // 改变一下 preferredDatePickerStyle 属性
}
复制代码

iOS14中UIDatePicker样式有四种,可根据需求设置,默认是UIDatePickerStyleAutomatic,会自动选择当前平台和模式可用的最佳样式。 UIDatePickerStyle 需要使用轮播样式,设置为UIDatePickerStyleWheels,并且最好在设置完style属性以后,再设置frame和背景颜色。

9、iOS13 暗黑模式适配

原因:iOS13系统设置暗黑模式,应用app 出现大量背景暗黑色UI分布突兀;

解决:禁用暗黑模式 info中Appearance 设置Light,开发中UI视图设置背景色;

10、iOS13 私有方法 KVC 不允许使用

原因:在 iOS 13 中不再允许使用 valueForKey、setValue:forKey: 等方法获取或设置私有属性,虽然编译可以通过,但是在运行时会直接崩溃,并提示一下崩溃信息:

使用的私有方法 [_textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];

// 崩溃提示信息 *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Access to UITextField's _placeholderLabel ivar is prohibited. This is an application bug'

解决:

使用其他方法:

// 替换的方案
self.textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"输入"attributes:@{NSForegroundColorAttributeName: [UIColor redColor]}];
复制代码

11、iOS13 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.layer.contents = nil;
        break;
    }
}
复制代码

12、iOS13 使用 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.'

另外在 iOS 13 中终于可以获取直接获取搜索的文本框:

_searchBar.searchTextField.text = @“search";

解决: 替换UISearchDisplayController,使用新的UISearchController;

13、iOS13 模态弹出默认交互改变

原因:在 iOS 13 中此枚举值直接成为了模态弹出的默认值,因此 presentViewController 方式打开视图不是全屏的并且默认是下滑返回。

解决:

如果需要做成全屏显示的界面,需要手动设置弹出样式:

- (UIModalPresentationStyle)modalPresentationStyle {
   return UIModalPresentationFullScreen;
}

//或者在present之前:
ctr.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:ctr animated:animated completion:completion];
复制代码

14、iOS13 UISegmentedControl 默认样式改变

原因:iOS13 默认样式变为白底黑字,如果设置修改过颜色的话,页面需要修改。原本设置选中颜色的 tintColor 已经失效,新增了 selectedSegmentTintColor 属性用以修改选中的颜色。

解决: 通过 selectedSegmentTintColor 属性修改颜色;

15、推送的 deviceToken 获取到的格式发生变化

原因:原本可以直接将 NSData 类型的 deviceToken 转换成 NSString 字符串,然后替换掉多余的符号即可:

  • (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSString *token = [deviceToken description]; for (NSString *symbol in @[@" ", @"<", @">", @"-"]) { token = [token stringByReplacingOccurrencesOfString:symbol withString:@""]; } NSLog(@"deviceToken:%@", token); }

在 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
      );
}
复制代码

16、iOS12.5.5 弹框层理显示问题

原因: iOS12.5.5出现当前ViewController弹框之后长按再次触发系统弹框,出现新弹框被覆盖的情况,代码如下图:

解决1: 以Window 层级弹出系统提示框,放在最上层

解决2:自定义AlertView,Window 弹出提示框放在最上层

17、iOS12 tabbar 从二级页面返回时,出现跳跃

原因:iOS 12.1 这个问题是 iOS 12.1 Beta 2 引入的问题,在 push viewController 的时候设置hidesBottomBarWhenPushed = YES, 手势返回的时候就会出现问题,主要原因是 tabBar 内的按钮 UITabBarButton 的frame.size 变为 (0, 0) 导致的。

解决:[UITabBar appearance].translucent = NO;

18、iOS11 安全区域适配

原因:iOS 11中ViewController的automaticallyAdjustsScrollViewInsets属性被废弃,顶部多了一定的inset;

解决:

if (@available(iOS 11.0, *)) {
    self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
    self.automaticallyAdjustsScrollViewInsets = NO;
}
复制代码

19、iOS11 tableView的界面错乱,组间距和contentInset错乱

原因:因为iOS11中 UIViewController 的 automaticallyAdjustsScrollViewInsets 属性被废弃了,因此当tableView超出安全区域时,系统自动会调整SafeAreaInsets值,进而影响adjustedContentInset值

解决1:顶部间距问题

if (@available(iOS 11.0, *)) {

self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;

} else {

self.automaticallyAdjustsScrollViewInsets = NO;

}

解决2:添加实现View的代理方法,只有实现下面两个方法,方法 (CGFloat)tableView: heightForFooterInSection: 才会生效

  • (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return nil; }

  • (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { return nil; }

解决2:直接使用tableView属性进行设置,修复该UI错乱

self.tableView.sectionHeaderHeight = 0;

self.tableView.sectionFooterHeight = 5;

[_optionTableView setContentInset:UIEdgeInsetsMake(-35, 0, 0, 0)];

解决3:添加以下代码关闭估算行高

self.tableView.estimatedRowHeight = 0;

self.tableView.estimatedSectionHeaderHeight = 0;

self.tableView.estimatedSectionFooterHeight = 0;

20、iPhone 11 手机,iOS 14.0.1系统计算是否是iphonex系列问题

1、原来代码根据

[[UIApplication sharedApplication] statusBarFrame].size.height == 44判断是否是iphonex系列手机,但是11手机可能会多几个像素。因此需要改变判断方式,可改为:

#define XPX_IS_IPhoneX ({BOOL isPhoneX = NO;
if (@available(iOS 11.0, *)) {
    isPhoneX = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom > 0.0;
} (isPhoneX);})
复制代码

21、OSS上传图片失败问题

直接原因:使用过期的OSSToken去上传图片

错误信息:Message=The security token you provided has expired., Code=SecurityTokenExpired, RequestId=61C57F9AB9FD8B3839B74546}

原因1:iOS 网络请求库 AFNetworking 请求数据是有缓存策略的,默认使用的是NSURLRequestUseProtocolCachePolicy = 0 这项策略, 如果缓存不存在,直接从服务端获取。如果缓存存在,会根据response中的Cache-Control字段判断下一步操作,如: Cache-Control字段为must-revalidata, 则询问服务端该数据是否有更新,无更新的话直接返回给用户缓存数据,若已更新,则请求服务端。

原因2:OSSClient链接对象不使用单例形式创建(虽然官方推荐)

解决1:在网络请求库里面加入去除缓存策略的代码

解决2:修改OSSClient 创建方式,官网推荐以类似单例的这种形式创建链接对象

Guess you like

Origin juejin.im/post/7049648953255526430
ios