不知道大家有木有經(jīng)歷過恨樟,創(chuàng)建一個按鈕在Cell上,但是很多人反饋疚俱,點擊不了劝术,一點擊就走了UITableView的didSelect方法,跳轉進去了。
這其實是響應范圍只有按鈕的位置养晋,UI出來可能就只有這么小點衬吆,那我們要怎么做?
可能有小伙伴會說,那就擴大按鈕frame啊匙握,多簡單的事咆槽,立馬點擊范圍就擴大了。
可是圈纺。秦忿。。如果擴大了蛾娶,尤其是里面有圖片的灯谣,一來圖片拉伸了,二來可能影響了其他控件的位置蛔琅。
那能不能變通一下?
那就要了解一下響應鏈和事件傳遞了胎许。
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIApplication : UIResponder
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, CALayerDelegate>
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment>
@interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>
我們都知道繼承自UIButton的控件是可以點擊,這是因為UIApplication罗售,UIView辜窑,UIViewController都繼承自UIResponder, UIButton->UIControl->UIView, 最終繼承自UIView.
備注一下:很多小伙伴都知道的寨躁,查找控件的父視圖控件方法. nextResponder, 例如:有button類型的按鈕btn, btn. nextResponder, btn. nextResponder的父視圖btn. nextResponder. nextResponder...以此類推
事件的分發(fā)和傳遞穆碎。
1.當iOS程序中發(fā)生觸摸事件后,系統(tǒng)會將事件加入到UIApplication管理的一個任務隊列中
2.UIApplication將處于任務隊列最前端的事件向下分發(fā)职恳。即UIWindow所禀。
3.UIWindow將事件向下分發(fā),即UIView放钦。
4.UIView首先看自己是否能處理事件色徘,觸摸點是否在自己身上。如果能操禀,那么繼續(xù)尋找子視圖褂策。
5.遍歷子控件,重復以上兩步颓屑。
6.如果沒有找到辙培,那么自己就是事件處理者。如果
7.如果自己不能處理邢锯,那么不做任何處理扬蕊。
其中 UIView不接受事件處理的情況主要有以下三種:
1)alpha <0.01
2)userInteractionEnabled = NO
3.hidden = YES.
這個從父控件到子控件尋找處理事件最合適的view的過程,如果父視圖不接受事件處理(上面三種情況)丹擎,則子視圖也不能接收事件尾抑。事件只要觸摸了就會產(chǎn)生歇父,關鍵在于是否有最合適的view來處理和接收事件,如果遍歷到最后都沒有最合適的view來接收事件再愈,則該事件被廢棄榜苫。
怎么尋找最合適的view
// 此方法返回的View是本次點擊事件需要的最佳View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
// 判斷一個點是否落在范圍內(nèi)
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
// 因為所有的視圖類都是繼承BaseView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 1.判斷當前控件能否接收事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
// 2. 判斷點在不在當前控件
if ([self pointInside:point withEvent:event] == NO) return nil;
// 3.從后往前遍歷自己的子控件
NSInteger count = self.subviews.count;
for (NSInteger i = count - 1; i >= 0; i--) {
UIView *childView = self.subviews[I];
// 把當前控件上的坐標系轉換成子控件上的坐標系
CGPoint childP = [self convertPoint:point toView:childView];
UIView *fitView = [childView hitTest:childP withEvent:event];
if (fitView) { // 尋找到最合適的view
return fitView;
}
}
// 循環(huán)結束,表示沒有比自己更合適的view
return self;
}
事件傳遞給窗口或控件的后,就調(diào)用hitTest:withEvent:方法尋找更合適的view,如果子控件是合適的view翎冲,則在子控件再調(diào)用hitTest:withEvent:查看子控件是不是合適的view垂睬,一直遍歷,直到找到最合適的view抗悍,或者廢棄事件驹饺。
有個這樣的圓形的button:
這樣只要在區(qū)域內(nèi)都絕對可以點擊成功的。
那么缴渊,如果是這樣呢赏壹?
這樣的點小成這樣(只是設極限,現(xiàn)實中會大些衔沼,但是也有可能是按鈕有圖標蝌借,實際frame寬高比較小,類似下圖)
1.自定義按鈕繼承自UIButton
#import "subBtn.h"
@implementation subBtn
// 改變圖片的點擊范圍
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
UIBezierPath *path1 = [UIBezierPath bezierPathWithRect:CGRectMake(-20, -20, 200, 200)];
return [path1 containsPoint:point];
}
@end
2.創(chuàng)建subBtn
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
subBtn *btn = [[subBtn alloc]initWithFrame:CGRectMake(100, 100, 5, 5)];
btn.layer.cornerRadius = btn.frame.size.width / 2;
btn.layer.masksToBounds = YES;
btn.backgroundColor = [UIColor grayColor];
[self.view addSubview:btn];
[btn addTarget:self action:@selector(change) forControlEvents:UIControlEventTouchUpInside];
}
- (void)change{
NSLog(@"please go out");
}
擴大區(qū)域后指蚁,發(fā)現(xiàn)菩佑, 圓點外也是可以點擊的。點擊范圍擴大了凝化。