POP Animation 和 layoutSubviews 的衝突

noark9發表於2016-07-09

問題現象

因為 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]; } ```

其實問題的原因還是沒有好好閱讀官方文件

相關文章