1.問題:之前在面試時被問到這樣一個問題:如何擴大一個給定視圖的點選區域,或者說如何讓一個視圖在目前視圖外的子視圖也能響應點選事件。
2.場景:可能大家不太了解這種問題是什麼意思,現在給定場景。我們在開發中經常會遇到這樣問題,上傳圖檔的時候,我們選中圖檔後要删除選中的圖檔,會在右上角添加一個删除按鈕。這時候删除按鈕就會有一半,或者全部都出現在目前圖檔視圖的bounds外,導緻不好點或者不能點的情況出現,那就需要用到我們今天的命題,擴大視圖的點選區域。
3.前提:在解決這個問題的時候,我們先要清楚兩個東西
1.觸摸時間的touch的産生順序。一個APP中touch時間是如何産生的,當我們手指接觸到螢幕時,通過硬體産生一個touch
然後以 touch -> UIApplication -> UIWindow -> UIViewController.View -> subviews -> ... ->最終找到最合适的響應者 or 丢棄
2.響應鍊:當我們找到最合适的響應者的時候,我的view就開始進行touch時間的響應。順序恰恰和産生相反。
以 view -> supView ->... -> UIViewController -> UIWindow -> UIApplication or(丢棄)。
4.方法:hitTest,這個方法就負責進行touch事件的傳遞,當事件通過觸摸點傳過來之後,由這個類的對象通過hitTest方法判斷傳遞給哪些subView,或者丢棄。一個事件會丢棄有四種情況
①:view.userinterfaceEnable == NO
②:view.hidden == YES
③:view.alpha < 0.05
④:點選區域 超出 view.bounds 之外。
是以我們不難推斷hitTest的實作。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
if (self.hidden == NO || self.alpha < 0.05 || self.userInteractionEnabled == NO) {
//1.當滿足這幾個條件時,直接丢棄touch事件,不再向下分發。
return nil;
}else{
if (![self pointInside:point withEvent:event]) {
//2.如果點選point在視圖之外,丢棄
return nil;
}else{
//3.分發給子視圖
if (self.subviews.count > 0) {
for (UIView *subView in self.subviews) {
UIView *hitTestSubView = [subView hitTest:point withEvent:event];
return hitTestSubView;
}
}else{
return self;
}
}
}
}
5.解決,是以我們隻需要在改變分發政策,讓他在視圖外同樣分發給子視圖就行了。
class HitTestView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if isHidden == true || alpha < 0.05 || isUserInteractionEnabled == false {
return super.hitTest(point, with: event)
}else{
if self.point(inside: point, with: event) {
return super.hitTest(point, with: event)
}else{
//1.有subView時交給subView 去響應
for subview in self.subviews{
let coverPoint = self.convert(point, to: subview)
return subview.hitTest(coverPoint, with: event)
}
//2.沒有subView時交給自己來響應,也就是說你無論在哪兒點選都會響應(擴大點選區域)
//當然這裡如果你想擴大到一定的傳回,可以在此處加限制
let isResponse:Bool = false
if isResponse {
return self
}else {
return nil
}
//3.如果你不想當沒有subView時就随便響應,j就傳回nil
return nil
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print(#function)
}
}
6.參考:iOS hitTest