# 問題現象
因為 Facebook 的 POP 框架用起來很舒服,于是一直慢慢來習慣了用 POP 做動畫,最近做了一個很簡單的讓一個 Button 旋轉的動畫,程序卻異常的崩潰了,崩潰的地方在 `-layoutSubviews` 這個地方,如下圖目測,應該是因為動畫的時候,觸發了 `-layoutSubviews` 方法,于是崩潰,就像這樣

并且在終端輸出了這樣的信息
```
Jun 27 07:05:17 shenzhenren[5868] <Error>: CGAffineTransformInvert: singular matrix.
Jun 27 07:05:17 shenzhenren[5868] <Error>: CGAffineTransformInvert: singular matrix.
```
<!--more-->
# 原因分析
我們先來看下關鍵的幾處代碼是怎么寫的:
```
- (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 出現錯誤,參考[官方文檔](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/occ/instp/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];
}
```
其實問題的原因還是沒有好好閱讀官方文檔