iOS 15 适配踩坑

苹果前两天推出了iOS 15的beta-3。这个消息还是从客户那儿知道的😂,没想到他们比我还前卫,秋天都等不及~~

测试背景

Xcode 13 beta版,iOS 15 beta 3的系统。
除了客户提出的问题,自己还发现了两处UI异常,不过说不定苹果能良心发现,在正式版中给修复一下。

一、企业签名的 App 无法使用

客户反馈说 App 不能正常打开,并且提示下面的这种信息:

“xxx”Needs to Be Updated : The developer of this app needs to update it to work with this version of iOS.

“xxx”需要更新 :App开发者需要更新此App以在此iOS版本上正常工作。

猜测是需要重签名或者使用最新的 Xcode 打包。

我看苹果论坛上有人说必须使用新的 Xcode 以适配 iOS 15,但我用旧版的 Xcode 重新打了一个包,也可以解决这个问题。所以,不方便更新 Xcode 的话,可以尝试重签名试试。

睡了一觉之究极补充: 签名问题与 Xcode 版本无关,而是 Mac 系统导致的,将 macOS 升级到 11.* Big Sur 以上再进行重新签名,可以解决 App 无法使用的问题。

二、 NavigationBar 颜色及背景失效

1. 问题描述

项目中往往会自定义一个导航控制器,方便全局指定导航条的背景色、标题颜色等等。以设置背景色和标题颜色为例:

1
2
3
4
5
//背景色
self.navigationBar.barTintColor = RGB(42, 109, 240);
//Title 颜色
NSDictionary *titleTextAttributes = @{NSFontAttributeName:[UIFont fontWithName:@"" size:18], NSForegroundColorAttributeName:RGB(255, 255, 255)};
[self.navigationBar setTitleTextAttributes:titleTextAttributes];

但在 iOS 15上发现,指定的背景色失效了,但滚动控制器的视图时,导航条的背景又出现了。看了一眼 UINavigationBar 的 API,15中并没有新增的。倒是有几个 iOS 13新增的 API 我没用过……哈哈哈,写到这里觉得自己以前的功课落下太多了,13的更新还没学习呢😂😂😂😂😂

2. iOS 13新增 API

  • standardAppearance : 描述导航栏以标准高度显示时要使用的外观属性。
1
@property (nonatomic, readwrite, copy) UINavigationBarAppearance *standardAppearance;
  • compactAppearance : 描述导航栏在紧凑高度时使用的外观属性。如果未设置,则将使用标准外观。
1
@property (nonatomic, readwrite, copy, nullable) UINavigationBarAppearance *compactAppearance;
  • scrollEdgeAppearance : 描述当关联的 UIScrollView 向上滚动时要使用的导航栏的外观属性。如果未设置,将改用修改后的standardAppearance。
1
@property (nonatomic, readwrite, copy, nullable) UINavigationBarAppearance *scrollEdgeAppearance;
  • compactScrollEdgeAppearance : 描述当导航栏以紧凑的高度显示时,以及关联的 UIScrollView 往上滚动时,要使用的导航栏的外观属性。如果未设置,则首先尝试 scrollEdgeAppearance,如果为nil,则尝试 compactAppearance,然后尝试修改 standardAppearance。
1
@property(nonatomic,readwrite, copy, nullable) UINavigationBarAppearance *compactScrollEdgeAppearance;

3. 解决办法

根据我们的问题现象,猜测是 standardAppearancescrollEdgeAppearance 需要调整,如果正常状态和滚动状态颜色一样,可以修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
NSDictionary *titleTextAttributes = @{NSFontAttributeName:[UIFont fontWithName:MAIN_FONT_FAMILY size:18], NSForegroundColorAttributeName:RGB(255, 255, 255)};
if (@available(iOS 13.0, *)) {
UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
appearance.backgroundColor = RGB(42, 109, 240);
appearance.titleTextAttributes = titleTextAttributes;
self.navigationBar.standardAppearance = appearance;
self.navigationBar.scrollEdgeAppearance = appearance;
} else {
// Fallback on earlier versions
self.navigationBar.barTintColor = RGB(42, 109, 240);
[self.navigationBar setTitleTextAttributes:titleTextAttributes];
}

Bingo!颜色显示正常啦

所以这是什么意思?强买强卖吗?必须设置 Appearance 才可以?

4. 遗留问题

在 Xcode 13-beta 中,必须同时指定 standardAppearancescrollEdgeAppearance 才可以。但根据苹果的注释,如果 scrollEdgeAppearance 为nil,会默认使用 standardAppearance 啊。燃鹅并不行。不知道是苹果的 bug 还是怎么的……朋友昨天叫我一起转行了,因为他觉得苹果的系统做的一年不如一年~ 😂

5. 导航栏底部的黑线

shadowColor 阴影属性设置为透明。

1
2
3
4
5
6
7
8
9
10
11
12
//去掉导航栏底部的黑线
if (@available(iOS 13.0, *)) {
...
appearance.shadowColor = [UIColor clearColor];
...
} else {
// Fallback on earlier versions
...
[self.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.navigationBar.shadowImage = [[UIImage alloc] init];
...
}

三、UITabBar 背景图失效

这个问题有点类似上一个,UITabBar 之前设置的背景图片,老版本可以,iOS 15上表现为空白。参考问题二的思路,找到了下面的 API,做个兼容就可以了。当然,遗留问题同上,必须同时指定 standardAppearancescrollEdgeAppearance 才可以……🙄……而且,如果在初始化以后,某个时机单独修改了 standardAppearance,也必须要同步指定一下 scrollEdgeAppearance ……🙄

  • API
1
2
@property (nonatomic, readwrite, copy) UITabBarAppearance *standardAppearance;//ios 13.0.
@property (nonatomic, readwrite, copy, nullable) UITabBarAppearance *scrollEdgeAppearance;//ios 15.0.
  • 老方式
1
[self.tabBar setBackgroundImage:[img imageWithRenderingMode:(UIImageRenderingModeAlwaysOriginal)]];
  • 兼容新的API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UIImage *img = [UIImage imageNamed:@"ahaaaaa"];
if (@available(iOS 13.0, *)) {
UITabBarAppearance *appearance = [[UITabBarAppearance alloc] init];
appearance.backgroundImage = img;
appearance.backgroundImageContentMode = UIViewContentModeScaleToFill;
self.tabBar.standardAppearance = appearance;
if (@available(iOS 15.0, *)) {
self.tabBar.scrollEdgeAppearance = appearance;
} else {
// Fallback on earlier versions
}
} else {
// Fallback on earlier versions
[self.tabBar setBackgroundImage:[img imageWithRenderingMode:(UIImageRenderingModeAlwaysOriginal)]];
}

四、UITabBarItem 文字颜色失效

……还是同上,新版本中 UITabBarItem 文字颜色的修改不起作用。同样是在 iOS 13 中新增的 UITabBarItemAppearance 来修改 Item 的不同状态下的不同表现。遗留问题同上。

  • 相关的类型如下,其他的API就不贴了:

UITabBarItemAppearanceUITabBarItemStateAppearance

1
2
3
4
5
6
7
8
9
10
11
/// The appearance when the tab bar item is in the normal state
@property (nonatomic, readonly, strong) UITabBarItemStateAppearance *normal;

/// The appearance when the tab bar item is in the selected state
@property (nonatomic, readonly, strong) UITabBarItemStateAppearance *selected;

/// The appearance when the tab bar item is in the disabled state
@property (nonatomic, readonly, strong) UITabBarItemStateAppearance *disabled;

/// The appearance when the tab bar item is in the focused state
@property (nonatomic, readonly, strong) UITabBarItemStateAppearance *focused;
  • 兼容新的API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//Set tabBar style.
UIColor *normalTitleColor = RGBA(80, 80, 81, 1);
UIColor *selectedTitleColor = RGBA(42, 109, 240, 1);
if (@available(iOS 13.0, *)) {
UITabBarItemAppearance *itemAppearance = [[UITabBarItemAppearance alloc] init];
itemAppearance.normal.titleTextAttributes = @{NSForegroundColorAttributeName : normalTitleColor};
itemAppearance.selected.titleTextAttributes = @{NSForegroundColorAttributeName : selectedTitleColor};
UITabBarAppearance *appearance = [[UITabBarAppearance alloc] init];
appearance.stackedLayoutAppearance = itemAppearance;
self.tabBar.standardAppearance = appearance;
if (@available(iOS 15.0, *)) {
self.tabBar.scrollEdgeAppearance = appearance;
} else {
// Fallback on earlier versions
}
}else if (@available(iOS 10.0, *)) {
self.tabBar.tintColor = normalTitleColor;
self.tabBar.unselectedItemTintColor = selectedTitleColor;
}else {
// Fallback on earlier versions
}

五、 UITableView Header 高度失效

通常 TableView 第一个分组如果不需要 Header 的话,我们会给个0.01的高度,看上去就是顶部没有空白的效果。

1
2
3
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 0.01;
}

但在 iOS 15上视图的顶部默认会下沉几个像素,我以为磨人的 automaticallyAdjustsScrollIndicatorInsets 这类小妖精又出现了,尝试了一下好像不是这类问题。😂

看了一眼官网的 API 变动,发现了一个小秘密:

The amount of padding above each section header.

每个分组Header上方的填充量。

1
@property(nonatomic) CGFloat sectionHeaderTopPadding;

所以我就这样:

1
2
3
if (@available(iOS 15.0, *)) {
self.tableView.sectionHeaderTopPadding = 0;
}

试着改了一下,毕竟试试又不会怀孕。好了……有那么一瞬间我仿佛能理解苹果为什么加这个属性,貌似真的有场景会用到这个间距。冷静了一下,我发现我还是太菜了,理解不了~~~

六、UITextField 的 clearButton 向右偏移

_UITextFieldClearButton 向右偏移了一点儿点儿…有点压到边框,倒是不影响使用,然后我也不知道怎么改,谁知道告诉我一下。阿里嘎多~

2021-08-12:关于问题六,iOS 15 beta-4 版本中貌似已经好了~~


目前就发现了这几个问题。希望秋天到来的时候,开发者们不用花太多时间在 UI 适配上。