前言
今天寫一個界面的時候,在Cell中添加了一個子view,該view里有幾個星星按鈕,但是發現點擊星星的時候,并沒有響應。為啥??
番外
不應該啊,所以這時回想下事件傳遞機制。
我們知道能接受事件的都是UIResponder
的子類,UIView
,UIViewController
都是UIResponder
的子類,所以默認的能接受各種事件(touch事件,press事件,加速計事件,各種手勢)。
但是默認的
UIImageView
,UILabel
的isUserInteractionEnabled
屬性是NO,所以如果想要在他兩上增加手勢,或者ImageView中有其他的控件需要接受事件響應,這時我們需要手動的設置isUserInteractionEnabled
屬性為YES。
UIApplication
我們很熟悉,一個全局的單例對象,App內所有的事件都是交給它進行分發和處理的。UIApplication
除了能夠接收上述的事件,還會接收UIControl
的action事件,除此之外還有接受一些系統的事件(內存警告,屏幕旋轉等)。
用戶事件(Touch,Press)
當App接收到一個事件的時候,UIKit
會自動的找到那個接收這個事件的UIResponder
,所謂的first responder
。
也可以手動調用
becomeFirstResponder
方法成為first responder
,textView調用此方法就會自動彈出inputView
,這里的inputView
也就是我們的鍵盤,同時也可以顯示自定義的inputView
,比如常見的表情鍵盤。
touch, press事件通過hitTest(_:with:)
方法找到first responder
。找到first responder
后,first responder
有三種選擇,下面以touch事件為例說明,點擊藍色的View,此時first responder
為藍色的View:
藍色的View實現了
touchesBegan:withEvent:
,同時調用super
,讓事件進行傳遞,讓下一個響應者做一些事情;藍色的View實現了
touchesBegan:withEvent:
,不調用super
,此時事件傳遞中斷;藍色的View沒有實現
touchesBegan:withEvent:
方法,此時會根據Responder Chain
進行尋找下一個響應者,如圖所示:
UIControl Action
當用戶點擊一個綁定target/action的按鈕后,一個點擊事件會被發送給UIApplication
,然后UIApplication
會通知到按鈕去執行這個action。
當addTarget,我們傳了nil,也就是沒有target,
UIApplication
就會通知first responder去執行action;
如果first responder
沒有實現這個action,那么此時就會按照事件那樣根據Responder Chain
進行尋找下一個響應者。
所以我們可以通過如下方式隱藏鍵盤
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
系統事件
系統事件會直接發送給給UIApplication
,然后會分發到AppDelegate
進行處理。
開始
回歸正題,找到我們按鈕添加事件的代碼:
starOne.addTarget(self, action: #selector(clickStar(_:)), for: .touchDown)
starTwo.addTarget(self, action: #selector(clickStar(_:)), for: .touchDown)
扯了那么多,Cell中的按鈕不能響應點擊事件,說明當我們點擊星星的時候,UIApplication
并沒有找到按鈕添加的target,也就是self
(EvaluateView),說明cell中的evaluateView
并沒有正確的顯示,才導致按鈕的點擊沒有正常的響應。
于是找到Cell中添加evaluateView
的地方,發現問題了,設置約束不完整,少了一個底部的??,添加以后evaluateView
,正常顯示,按鈕的點擊事件也就正常了。