POP Animation 和 layoutSubviews 的

作者: noark9 | 来源:发表于2016-06-27 07:42 被阅读363次

问题现象

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

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];
}

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

相关文章

网友评论

  • plantseeds:问一下button.width、top 这些属性是哪来的呀?
    noark9:自己加的 UIView 的 Category
  • plantseeds:你这个链接里面还有一句:Changes to this property can be animated. However, if the transform property contains a non-identity transform, the value of the frame property is undefined and should not be modified. In that case, you can reposition the view using the center property and adjust the size using the bounds property instead. 好像是说不能修改frame,但是可以通过修改center和bounds来代替frame达到同样的效果?
    noark9:@lg_pursuing 是的在修改了 transform 后只能依赖 center 和,bound 属性

本文标题:POP Animation 和 layoutSubviews 的

本文链接:https://www.haomeiwen.com/subject/qjjzdttx.html