首页 iOS 13 之后 Masonry 使用上面的一些注意事项 (动画效果未达预期)
文章
取消

iOS 13 之后 Masonry 使用上面的一些注意事项 (动画效果未达预期)

昨天用户反馈在 iOS 13 的系统下, 我的一个 App 的部分功能不能够正常工作了:

  1. 页面返回按钮不见了 (自定义的导航栏动画执行结果出错).
  2. 底部按钮被挤压了 (自定义的 Bar 动画执行结果出错).

自定义导航栏底部按钮的 Bar 就像平时的 电子书/漫画 App 一样, 通过点击屏幕终于进行 显示/隐藏.

首先, 自定义导航栏 和 底部按钮的Bar 约束如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 自定义导航栏
// 其实就是个 UIView, 通过动画来显示和隐藏
[_navBar mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.right.mas_equalTo(0.0f);
    make.top.mas_equalTo(-height);
    make.height.mas_equalTo(height);
}];

// 像 TabBar 一样的 UIView 自定义, 也是通过动画来显示和隐藏
[_actionBar mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.right.mas_equalTo(0.0f);
    make.top.equalTo(self.view.mas_bottom);
    make.height.mas_equalTo(49.0f);
}];

首先, 显示的动画是有问题的, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)showBars {
    // 做一些判断, 方式用户连续点击造成动画出错.
    if (_barAnimating == YES || _barShown) {
        return;
    }
    _barShown = YES;
    _navBar.hidden = NO;
    _actionBar.hidden = NO;
    _barAnimating = YES;
    // nav frame
    CGRect navFrame = _navBar.frame;
    navFrame.origin.y = 0.0f;
    // action bar frame
    CGRect actionFrame = _actionBar.frame;
    actionFrame.size.height = 49.0f + self.view.safeAreaInsets.bottom;
    actionFrame.origin.y = CGRectGetHeight(self.view.bounds) - CGRectGetHeight(actionFrame);
    [UIView animateWithDuration:0.3f animations:^{
        self.navBar.frame = navFrame;
        self.actionBar.frame = actionFrame;
    } completion:^(BOOL finished) {
        self.barAnimating = NO;
        self.barShown     = YES;
    }];
}

基本就是通过 UIView 动画让 navBaractionBar 滑动出现/消失.

iOS 11iOS 12 上面一切工作正常, 但是在 iOS 13 上面却出现了问题.

通过调试之后发现问题如下:

  1. navBar 根本没出现 (frame 没有发生变化, 动画结果与预期不符).
  2. actionBar 只有一部分出现, 还有一部分处于屏幕之外, 因此看起来像是被挤压了一样.

此时猜测是不是 约束 造成的问题. (因为以前写 自定义键盘扩展 时候遇到过类似的情况, 因为约束的原因, 无论如何修改 frame 结果始终与预期不符).

然后我就尝试的使用约束来做动画, 把动画部分的代码修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[UIView animateWithDuration:0.3f animations:^{
    // 修改 navBar 顶部约束
    [self.navBar mas_updateConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(0);
    }];
    // 修改 actionBar 底部约束
    [self.actionBar mas_updateConstraints:^(MASConstraintMaker *make) {
        make.bottom.mas_equalTo(0);
    }];
    [self.view layoutIfNeeded];
} completion:^(BOOL finished) {
    self.barAnimating = NO;
    self.barShown     = YES;
}];

actionBar 约束改为对 bottom 进行约束, 因为一开始是对 top 进行的约束:

1
2
3
4
5
6
[_actionBar mas_makeConstraints:^(MASConstraintMaker *make) {
    // 其他约束
    // ...
    // 对 bottom 进行约束
    make.bottom.mas_equalTo(CGRectGetHeight(_actionBar.bounds));
}];

代码执行的结果是符合预期了, 但是页面内容会 闪烁. 显然还是不行.

最后尝试对 navBaractionBar 使用 frame 来进行定位:

1
2
3
4
5
CGFloat height = CGRectGetMaxY(self.navigationController.navigationBar.frame);
// navBar
_navBar.frame = CGRectMake(0, -height, CGRectGetWidth(self.view.bounds), height);
// actionBar
_actionBar.frame = CGRectMake(0, CGRectGetHeight(self.view.bounds), CGRectGetWidth(self.view.bounds), 49);

UIView 动画部分还原成最早修改 frame 的方法.

执行结果达到预期, 页面也不闪烁了.

至此问题就算是解决了.

造成这个问题的原因应该是 iOS 13 更新之 对自动布局 应该有一些变化吧(暂时未深究其原因), 后面再做项目得注意这一点.

本文由作者按照 CC BY 4.0 进行授权

iOS 开发关于暗黑模式 (Dark Model) 的问题

Unity 中如何关闭 UI 的用户交互开关