POP Animation 和 layoutSubviews 的衝突
問題現象
因為 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.
原因分析
我們先來看下關鍵的幾處程式碼是怎麼寫的:
``` - (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度,再次點選的時候讓他轉回來
那麼根據上面程式碼,崩潰的過程其實是這樣的:
- 點選按鈕,觸發 POP 動畫
- POP 的動畫為 UIView 新增對應的 Transform,同時產生動畫
- 因為 UIView 新增了 Transform,所以 UIView 觸發了需要重新計算 layout 的過程
- 呼叫
- (void)layoutSubviews
重新計算佈局 - 因為 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]; } ```
其實問題的原因還是沒有好好閱讀官方文件
相關文章
- Activemq和Rabbitmq埠衝突MQ
- Elasticsearch和亞馬遜之間的衝突 - protocolElasticsearch亞馬遜Protocol
- 解決jquery和其他庫的衝突jQuery
- 科研和工作並不衝突
- Laravel 6.0 的 predis 和 phpredis 衝突的原因LaravelRedisPHP
- svn檔案衝突,樹衝突詳解
- jQuery的$命名衝突jQuery
- jquery和dwr的util.js方法衝突jQueryJS
- fcitx5 和 ibus的衝突
- wsl docker 和 ubuntu 衝突問題DockerUbuntu
- layoutSubviewsView
- 解衝突用到的命令
- 為什麼docker 網段衝突會和宿主機衝突?原因分析和解決方案Docker
- commit 衝突MIT
- 雜湊衝突
- SVN衝突解決和注意事項
- PCL(9)PLC庫和OpenCV庫中的FLANN衝突OpenCV
- 理解事件分發和衝突的實戰技巧事件
- exp工具的direct和query衝突以及程式補充
- STL Container和ATL智慧包裹類的衝突 (轉)AI
- Git 衝突了怎麼辦,如何高效快速的解決程式碼衝突?Git
- cad快捷鍵和win10衝突怎麼辦_cad快捷鍵和win10衝突的解決方法Win10
- 如何避免javascript中的衝突JavaScript
- VMware無法啟動/VMware和wsl衝突問題/VMware與Hyper-V衝突問題
- Flutter和iOS手勢衝突解決思路FlutteriOS
- 安裝vcenter server埠 80 和 IIS 衝突Server
- android NestedScrollView和ListView衝突問題AndroidView
- Git 解決衝突Git
- git 解決衝突Git
- 查詢maven衝突Maven
- jQuery多庫衝突jQuery
- caffe框架和theano框架的所需cudnn的版本衝突問題框架DNN
- git pull衝突的解決方案Git
- 埠衝突,可愛的8080
- JAR衝突問題的解決JAR
- PHP中的trait方法衝突PHPAI
- 關於hash衝突的解決
- ScrollView中用ListView的事件衝突View事件