2017.09.23ios
不断完善中。。。git
2017.10.02 新增 iPhone X 适配官方中文文档github
更新iOS11后,发现有些地方须要作适配,整理后按照优先级分为如下三类:api
######1. 升级后,发现某个拥有tableView的界面错乱,组间距和contentInset错乱,由于iOS11中 UIViewController
的 automaticallyAdjustsScrollViewInsets
属性被废弃了,所以当tableView超出安全区域时,系统自动会调整SafeAreaInsets
值,进而影响adjustedContentInset
值缓存
// 有些界面如下使用代理方法来设置,发现并无生效
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
// 这样的原理是由于以前只是实现了高度的代理方法,却没有实现View的代理方法,iOS10及之前这么写是没问题的,iOS11开启了行高估算机制引发的bug,所以有如下几种解决方法:
// 解决方法一:添加实现View的代理方法,只有实现下面两个方法,方法 (CGFloat)tableView: heightForFooterInSection: 才会生效
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return nil;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return nil;
}
// 解决方法二:直接使用tableView属性进行设置,修复该UI错乱
self.tableView.sectionHeaderHeight = 0;
self.tableView.sectionFooterHeight = 5;
[_optionTableView setContentInset:UIEdgeInsetsMake(-35, 0, 0, 0)];
// 解决方法三:添加如下代码关闭估算行高
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
复制代码
if ([UIDevice currentDevice].systemVersion.floatValue >= 11.0) {
make.edges.equalTo(self.view.safeAreaInsets);
} else {
make.edges.equalTo(self.view);
}
复制代码
.heic
格式图片,同一张live格式的图片,iOS10发送就没问题(转成了jpg),iOS11就不行// 0.83能保证压缩先后图片大小是一致的
// 形成不一致的缘由是图片的bitmap一个是8位的,一个是16位的
imageData = UIImageJPEGRepresentation([UIImage imageWithData:imageData], 0.83);
复制代码
// 这是由于 UIScrollView 的 contentInsetAdjustmentBehavior 属性默认为 automatic,经过如下代码能够修复
if (@available(iOS 11.0, *)) {
self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
// 固然,若是是使用 Storyboard,能够依次 Size Inspector -> Content Insets -> Set 'Never' 搞定
复制代码
进行修改以后,没有 SearchViewController 的页面是没有问题的,可是拥有searchViewController 的页面,进行搜索文本的输入会形成UI错乱,所以使用如下解决方法安全
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (@available(iOS 11.0, *)) {
self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (@available(iOS 11.0, *)) {
self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
}
}
复制代码
前往文件 "UITableView+FDTemplateLayoutCell.h" 70行
if (isSystemVersionEqualOrGreaterThen10_2) {
// 将这里的 UILayoutPriorityRequired 更改成 UILayoutPriorityDefaultHigh 便可解决问题
widthFenceConstraint.priority = UILayoutPriorityDefaultHigh - 1;
}
复制代码
UITableViewRowActionStyleNormal
,改成UITableViewRowActionStyleDestructive
便可deleteRowsAtIndexPaths
方法保持UI上的同步UITableViewRowAction *deleteRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
[self.dataSource removeObjectAtIndex:indexPath.row];
// 刷新tableview
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}
复制代码
fastSocket
第三方报错,具体缘由是缺乏C99的头文件,引入#include <sys/time.h>
便可searchController
赋值给了
NavigationItem
,经过属性
hidesSearchBarWhenScrolling
能够控制搜索栏是否在滑动的时候进行隐藏和显示
// A view controller that will be shown inside of a navigation controller can assign a UISearchController to this property to display the search controller’s search bar in its containing navigation controller’s navigation bar.
@property (nonatomic, retain, nullable) UISearchController *searchController API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);
// If this property is true (the default), the searchController’s search bar will hide as the user scrolls in the top view controller’s scroll view. If false, the search bar will remain visible and pinned underneath the navigation bar.
@property (nonatomic) BOOL hidesSearchBarWhenScrolling API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);
复制代码
另外,UINavigationBar
新增属性 BOOL值 prefersLargeTitles
来实现下面的效果,并能够经过 largeTitleTextAttributes
来设置大标题的文本样式。设置大标题以后,导航栏的高度就会由以前的64pt变成 96pt,若是项目中有直接写死的高度或者隐藏导航栏之类的操做,就须要适配一下 微信
有个界面使用到了导航栏按钮相关的frame,也发生了UI错乱,查看UI层级关系后发现,iOS11之前是直接把按钮加到了UINavigationBar
上面,而iOS11则是先将按钮加到了_UITAMICAdaptorView
,再加到_UIButtonBarStackView
、_UINavigationBarContentView
,接着才是UINavigationBar
。所以若是须要获取导航栏按钮 frame
或者 superView
,这里须要专门作下适配网络
下载完Xcode9以后,第一件事天然是在 iPhone X(模拟器)上过把瘾,而后编译后就发现报错了 因为iPhone X的状态栏是和其余版本手机差别比较大的,所以api 变化也比较大 前后作了如下适配app
打印的 Log 报出如下错误: Trapped uncaught exception 'NSUnknownKeyException', reason: '[<UIStatusBar_Modern 0x7fcdb0805770> valueForUndefinedKey:]: this class is not key value coding-compliant for the key foregroundView.'iphone
使用 runtime 打印其全部属性,发现如下差别
// 测试代码
#import <objc/runtime.h>
NSMutableString *resultStr = [NSMutableString string];
//获取指定类的Ivar列表及Ivar个数
unsigned int count = 0;
Ivar *member = class_copyIvarList([[application valueForKeyPath:@"_statusBar"] class], &count);
for(int i = 0; i < count; i++){
Ivar var = member[i];
//获取Ivar的名称
const char *memberAddress = ivar_getName(var);
//获取Ivar的类型
const char *memberType = ivar_getTypeEncoding(var);
NSString *str = [NSString stringWithFormat:@"key = %s type = %s \n",memberAddress,memberType];
[resultStr appendString:str];
}
NSLog(@"%@", resultStr);
复制代码
// 其余版本的手机
key = _inProcessProvider type = @"<UIStatusBarStateProvider>"
key = _showsForeground type = B
key = _backgroundView type = @"UIStatusBarBackgroundView"
key = _doubleHeightLabel type = @"UILabel"
key = _doubleHeightLabelContainer type = @"UIView"
key = _currentDoubleHeightText type = @"NSString"
key = _currentRawData type = {超长。。}
key = _interruptedAnimationCompositeViews type = @"NSMutableArray"
key = _newStyleBackgroundView type = @"UIStatusBarBackgroundView"
key = _newStyleForegroundView type = @"UIStatusBarForegroundView"
key = _slidingStatusBar type = @"UIStatusBar"
key = _styleAttributes type = @"UIStatusBarStyleAttributes"
key = _waitingOnCallbackAfterChangingStyleOverridesLocally type = B
key = _suppressGlow type = B
key = _translucentBackgroundAlpha type = d
key = _showOnlyCenterItems type = B
key = _foregroundViewShouldIgnoreStatusBarDataDuringAnimation type = B
key = _tintColor type = @"UIColor"
key = _lastUsedBackgroundColor type = @"UIColor"
key = _nextTintTransition type = @"UIStatusBarStyleAnimationParameters"
key = _overrideHeight type = @"NSNumber"
key = _disableRasterizationReasons type = @"NSMutableSet"
key = _timeHidden type = B
key = _statusBarWindow type = @"UIStatusBarWindow"
// iPhone X
key = _statusBar ; type = @"_UIStatusBar"
// 所以可见iPhone X的状态栏是多嵌套了一层,多取一次便可,最终适配代码为:
NSArray *children;
// 不能用 [[self deviceVersion] isEqualToString:@"iPhone X"] 来判断,由于iPhone X 的模拟器不会返回 iPhone X
if ([[application valueForKeyPath:@"_statusBar"] isKindOfClass:NSClassFromString(@"UIStatusBar_Modern")]) {
children = [[[[application valueForKeyPath:@"_statusBar"] valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews];
} else {
children = [[[application valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews];
}
复制代码
警告以上处理,代码看起来是不报错了,然而!!具体看了下代码发现并不生效!由于从iPhone X取出来以后只有view层级的信息,所以采用如下方法肯定2G/3G/4G,从API上目测是有效的
NSArray *typeStrings2G = @[CTRadioAccessTechnologyEdge,
CTRadioAccessTechnologyGPRS,
CTRadioAccessTechnologyCDMA1x];
NSArray *typeStrings3G = @[CTRadioAccessTechnologyHSDPA,
CTRadioAccessTechnologyWCDMA,
CTRadioAccessTechnologyHSUPA,
CTRadioAccessTechnologyCDMAEVDORev0,
CTRadioAccessTechnologyCDMAEVDORevA,
CTRadioAccessTechnologyCDMAEVDORevB,
CTRadioAccessTechnologyeHRPD];
NSArray *typeStrings4G = @[CTRadioAccessTechnologyLTE];
// 该 API 在 iOS7 以上系统才有效
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
CTTelephonyNetworkInfo *teleInfo= [[CTTelephonyNetworkInfo alloc] init];
NSString *accessString = teleInfo.currentRadioAccessTechnology;
if ([typeStrings4G containsObject:accessString]) {
NSLog(@"4G网络");
} else if ([typeStrings3G containsObject:accessString]) {
NSLog(@"3G网络");
} else if ([typeStrings2G containsObject:accessString]) {
NSLog(@"2G网络");
} else {
NSLog(@"未知网络");
}
} else {
NSLog(@"未知网络");
}
复制代码
常常从 Github 上下载项目把玩的老司机们都知道,有些老项目在模拟器上跑起来以后也会只有 iPhone 4(320480)的布局空间,形成这个的缘由是启动图使用 Launch Images Source 设置的时候没有勾选并设置对应的图片(11252436),解决方法以下
可是即便按照上面的操做进行以后,会发现底部 UITabBar 依旧是高出一些高度,查看层级关系后发现,一样是因为安全区的缘由,UITabBar 高度由49pt变成了83pt,所以这里也要对iPhone X 及其模拟器进行适配
CGRectGetHeight([UIApplication sharedApplication].statusBarFrame)
复制代码
首先看下屏幕尺寸
在设计方面,苹果官方文档human-interface-guidelines有明确要求,下面结合图例进行说明:
关于 safe area,使用 safeAreaLayoutGuide 和 safeAreaInset就能解决大部分问题,可是横屏下还可能会产生一些问题,须要额外适配
[headerView.contentView setBackgroundColor:[UIColor headerFooterColor]]
,这个写法看起来没错,可是只有在 iPhone X上有问题[headerView.backgroundView setBackgroundColor:[UIColor headerFooterColor]]
if ([deviceString isEqualToString:@"iPhone10,1"]) return @"国行(A1863)、日行(A1906)iPhone 8";
if ([deviceString isEqualToString:@"iPhone10,4"]) return @"美版(Global/A1905)iPhone 8";
if ([deviceString isEqualToString:@"iPhone10,2"]) return @"国行(A1864)、日行(A1898)iPhone 8 Plus";
if ([deviceString isEqualToString:@"iPhone10,5"]) return @"美版(Global/A1897)iPhone 8 Plus";
if ([deviceString isEqualToString:@"iPhone10,3"]) return @"国行(A1865)、日行(A1902)iPhone X";
if ([deviceString isEqualToString:@"iPhone10,6"]) return @"美版(Global/A1901)iPhone X";
复制代码
更多新设备信息详见**Github-iOS-getClientInfo**
// 在VC里面重写下面这个方法便可
- (BOOL)prefersHomeIndicatorAutoHidden{
return YES;
}
复制代码