POP Animation 和 layoutSubviews 的冲突

简介: 问题现象因为 Facebook 的 POP 框架用起来很舒服,于是一直慢慢来习惯了用 POP 做动画,最近做了一个很简单的让一个 Button 旋转的动画,程序却异常的崩溃了,崩溃的地方在 -layoutSubviews 这个地方,如下图目测,应该是...

问题现象

因为 Facebook 的 POP 框架用起来很舒服,于是一直慢慢来习惯了用 POP 做动画,最近做了一个很简单的让一个 Button 旋转的动画,程序却异常的崩溃了,崩溃的地方在 -layoutSubviews 这个地方,如下图目测,应该是因为动画的时候,触发了 -layoutSubviews 方法,于是崩溃,就像这样

img_476fdbf4437eed827cf5b41dfb3243ac.png
Crash

并且在终端输出了这样的信息

Jun 27 07:05:17  shenzhenren[5868] <Error>: CGAffineTransformInvert: singular matrix.
Jun 27 07:05:17  shenzhenren[5868] <Error>: CGAffineTransformInvert: singular matrix.

原因分析

我们先来看下关键的几处代码是怎么写的:

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.isShowAll = NO;
        [self loadShowAllButton];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.showAllButton.top = 8.0f;
    self.showAllButton.right = self.width;
}

- (void)loadShowAllButton {
    self.showAllButton = [[UIButton alloc] init];
    [self addSubview:self.showAllButton];
    self.showAllButton.width = 36.0f;
    self.showAllButton.height = 36.0f;
    self.showAllButton.left = 0.0f;
    self.showAllButton.top = 0.0f;
    self.showAllButton.contentMode = UIViewContentModeCenter;
    [self.showAllButton setImage:[UIImage imageNamed:@"default-show-more-arrow"] forState:UIControlStateNormal];
    [self.showAllButton addTarget:self action:@selector(showAllButtonPushed:) forControlEvents:UIControlEventTouchUpInside];
}

- (void)showAllButtonPushed:(UIButton *)sender {
    self.isShowAll = !self.isShowAll;
    POPBasicAnimation *animation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerRotation];
    if (self.isShowAll) {
        animation.toValue = @M_PI;
    } else {
        animation.toValue = @0;
    }
    [sender pop_removeAllAnimations];
    [sender pop_addAnimation:animation forKey:nil];
}

上面的代码并不复杂,我想要实现的东西就是在点击按钮的时候,让他旋转180度,再次点击的时候让他转回来

那么根据上面代码,崩溃的过程其实是这样的:

  1. 点击按钮,触发 POP 动画
  2. POP 的动画为 UIView 添加对应的 Transform,同时产生动画
  3. 因为 UIView 添加了 Transform,所以 UIView 触发了需要重新计算 layout 的过程
  4. 调用 - (void)layoutSubviews 重新计算布局
  5. 因为 UIView 有 Transform 了,再通过 -setFrame:(GRect)frame 设置 UIView 的 frame 出现错误,参考官方文档

如何解决

既然找到了原因,那么我们就根据原因来跳坑吧,既然是触发了 View 重新计算布局,那么我们不要让他在这个父 View 触发即可,那么最简单粗暴的解决方法就是,创建一个新的 View 包裹住我们需要动画的 View 于是,这样修改

- (void)layoutSubviews {
    [super layoutSubviews];
    self.showAllButtonContainerView.top = 8.0f;
    self.showAllButtonContainerView.right = self.width;
}

- (void)loadShowAllButton {
    self.showAllButtonContainerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 36.0f, 36.0f)];
    [self addSubview:self.showAllButtonContainerView];
    self.showAllButton = [[UIButton alloc] init];
    [self.showAllButtonContainerView addSubview:self.showAllButton];
    self.showAllButton.width = 36.0f;
    self.showAllButton.height = 36.0f;
    self.showAllButton.left = 0.0f;
    self.showAllButton.top = 0.0f;
    self.showAllButton.contentMode = UIViewContentModeCenter;
    [self.showAllButton setImage:[UIImage imageNamed:@"default-show-more-arrow"] forState:UIControlStateNormal];
    [self.showAllButton addTarget:self action:@selector(showAllButtonPushed:) forControlEvents:UIControlEventTouchUpInside];
}

其实问题的原因还是没有好好阅读官方文档

目录
相关文章
UE4 使用Animation Data Modifiers修改动画片段
UE4 使用Animation Data Modifiers修改动画片段
133 0
UE4 使用Animation Data Modifiers修改动画片段
Core Animation - 摇动+循环动态画圆
Core Animation - 摇动+循环动态画圆
89 0
Core Animation - 摇动+循环动态画圆
|
存储 前端开发 JavaScript
JavaScript总结:关于堆栈、队列中push()、pop()、shift()、unshift()使用方法的理解
JavaScript总结:关于堆栈、队列中push()、pop()、shift()、unshift()使用方法的理解
260 0
JavaScript总结:关于堆栈、队列中push()、pop()、shift()、unshift()使用方法的理解
|
JavaScript 开发者
动画-使用 transition-group 元素实现列表动画|学习笔记
快速学习动画-使用 transition-group 元素实现列表动画
151 0
动画-使用 transition-group 元素实现列表动画|学习笔记
|
JavaScript 开发者
动画-使用transition-group元素实现列表动画|学习笔记
快速学习动画-使用transition-group元素实现列表动画
130 0
动画-使用transition-group元素实现列表动画|学习笔记
|
JavaScript 开发者
动画-transition-group 中 appear 和 tag 属性的作用|学习笔记
快速学习动画- transition-group 中 appear 和 tag 属性的作用
280 0
动画-transition-group 中 appear 和 tag 属性的作用|学习笔记
|
JavaScript
transition-group列表过渡
《Vue实战》笔记
106 0
ScrollView push之后再pop回来,contentOffset变成了0
ScrollView push之后再pop回来,contentOffset变成了0
191 0
|
算法 网络协议 测试技术