iOS擴大UIButton的點擊范圍

怎樣來實現(xiàn)這個功能呢杯拐?又有多少種方式可以實現(xiàn)呢霞篡?下面一一來講。

  • 理解事件傳遞過程端逼,用這個來實現(xiàn)擴大點擊范圍
  • 使用Runtime機制擴大點擊范圍

事件傳遞過程

當(dāng)用戶點擊屏幕后朗兵,UIApplication 先響應(yīng)事件,然后傳遞給UIWindow顶滩。如果window可以響應(yīng)余掖。就開始遍歷window的subviews。遍歷的過程中礁鲁,如果第一個遍歷的view1可以響應(yīng)盐欺,那就遍歷這個view1的subviews(依次這樣不停地查找,直至查找到合適的響應(yīng)事件view)仅醇。如果view1不可以響應(yīng)冗美,那就開始對view2進行判斷和子視圖的遍歷。依次類推view3析二,view4…… 如果最后沒有找到合適的響應(yīng)view粉洼,這個消息就會被拋棄。這個就是iOS中的事件鏈叶摄,如下圖所示

事件鏈條

然而事件的響應(yīng)鏈條是事件鏈條的逆向属韧,根據(jù)視圖層級的添加順序從后往前的

關(guān)鍵的兩個方法:UIView方法

// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
// default returns YES if point is in bounds
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;

在系統(tǒng)的UIView中,以下4個條件不執(zhí)行事件響應(yīng)蛤吓。

1.隱藏(hidden=YES)的視圖
2.禁止用戶操作(userInteractionEnabled=NO)的視圖
3.alpha<0.01的視圖
4.視圖超出父視圖的區(qū)域

hitTest:withEvent:方法的實現(xiàn)可能是如下的:

// 因為所有的視圖類都是繼承BaseView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    // 1.判斷當(dāng)前控件能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    
    // 2. 判斷點在不在當(dāng)前控件
    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];
        // 把當(dāng)前控件上的坐標系轉(zhuǎn)換成子控件上的坐標系
        CGPoint childP = [self convertPoint:point toView:childView];
        UIView *fitView = [childView hitTest:childP withEvent:event];
        if (fitView) { // 尋找到最合適的view
            return fitView;
        }
    }
    // 循環(huán)結(jié)束,表示沒有比自己更合適的view
    return self;
}

有了以上的了解宵喂,我們可以利用這個來實現(xiàn)UIButton的點擊范圍,雖然不是那么優(yōu)雅:先來看一下效果

簡單示例

其實就是自定義view柱衔,實現(xiàn)hitTest:withEvent:方法樊破,里面加入一個按鈕愉棱,這就實現(xiàn)了放大點擊范圍唆铐。以上就是通過截斷事件傳遞的過程來實現(xiàn)放大點擊范圍。

Runtime實現(xiàn)方式如下:

@interface UIButton (EnlargeTouchArea)

- (void)setEnlargeEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left;

@end

#import "UIButton+EnlargeTouchArea.h"
#import <objc/runtime.h>

static char topNameKey;
static char rightNameKey;
static char bottomNameKey;
static char leftNameKey;

@implementation UIButton (EnlargeTouchArea)

- (void)setEnlargeEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left {
    objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &rightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &bottomNameKey, [NSNumber numberWithFloat:bottom], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &leftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (CGRect)enlargedRect {
    NSNumber* topEdge = objc_getAssociatedObject(self, &topNameKey);
    NSNumber* rightEdge = objc_getAssociatedObject(self, &rightNameKey);
    NSNumber* bottomEdge = objc_getAssociatedObject(self, &bottomNameKey);
    NSNumber* leftEdge = objc_getAssociatedObject(self, &leftNameKey);
    if (topEdge && rightEdge && bottomEdge && leftEdge) {
        return CGRectMake(self.bounds.origin.x - leftEdge.floatValue,
                          self.bounds.origin.y - topEdge.floatValue,
                          self.bounds.size.width + leftEdge.floatValue + rightEdge.floatValue,
                          self.bounds.size.height + topEdge.floatValue + bottomEdge.floatValue);
    } else {
        return self.bounds;
    }
}

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
    CGRect rect = [self enlargedRect];
    //如果按鈕設(shè)置為不可點擊奔滑、隱藏艾岂、透明度小于等于0.01或者點擊在按鈕內(nèi)部,則直接執(zhí)行父類方法
    if (CGRectEqualToRect(rect, self.bounds) || self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
        return [super hitTest:point withEvent:event];
    }
    //判斷點擊是否在放大的范圍內(nèi)
    return CGRectContainsPoint(rect, point) ? self : nil;
}

@end

以上的分類也可以使用屬性的方式進行關(guān)聯(lián)

@property(nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;

.m
static const NSString *KEY_HIT_TEST_EDGE_INSETS = @"HitTestEdgeInsets";

- (void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets {
    NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:@encode(UIEdgeInsets)];
    objc_setAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIEdgeInsets)hitTestEdgeInsets {
    NSValue *value = objc_getAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS);
    if (value) {
        UIEdgeInsets edgeInsets;
        [value getValue:&edgeInsets];
        return edgeInsets;
    }
    return UIEdgeInsetsZero;
}

以上就是對放大UIButton的點擊范圍的實現(xiàn)朋其,有不足之處請大家指正王浴,--

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脆炎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子氓辣,更是在濱河造成了極大的恐慌秒裕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钞啸,死亡現(xiàn)場離奇詭異几蜻,居然都是意外死亡,警方通過查閱死者的電腦和手機体斩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門梭稚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人絮吵,你說我怎么就攤上這事弧烤。” “怎么了蹬敲?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵暇昂,是天一觀的道長。 經(jīng)常有香客問我粱栖,道長话浇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任闹究,我火速辦了婚禮幔崖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘渣淤。我一直安慰自己赏寇,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布价认。 她就那樣靜靜地躺著嗅定,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渠退。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天脐彩,我揣著相機與錄音碎乃,去河邊找鬼惠奸。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播埂陆,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼缓窜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起克滴,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤逼争,失蹤者是張志新(化名)和其女友劉穎誓焦,沒想到半個月后仍翰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赫粥,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年歉备,在試婚紗的時候發(fā)現(xiàn)自己被綠了傅是。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匪燕。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蕾羊,死狀恐怖喧笔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情龟再,我是刑警寧澤书闸,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站利凑,受9級特大地震影響浆劲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哀澈,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一牌借、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧割按,春花似錦膨报、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弛矛,卻和暖如春够吩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丈氓。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工周循, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人万俗。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓鱼鼓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親该编。 傳聞我的和親對象是個殘疾皇子迄本,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容