ios的hitTest方法以及不規(guī)則區(qū)域內(nèi)觸摸事件處理方法(轉(zhuǎn))

概述
在正常的使用場景中赁项,我們處理了比較多的矩形區(qū)域內(nèi)觸摸事件,比如UIButton澈段、UIControl悠菜。一般來說,這些控件的圖形以及觸摸區(qū)域都是矩形或者圓角矩形的败富。但是在一些特殊應(yīng)用場景中我們有時不得不面對這樣一種比較嚴(yán)苛的需求悔醋,比如要求程序只對某個圓形、五角形等非常規(guī)區(qū)域的點(diǎn)擊事件進(jìn)行處理兽叮,這就需要花點(diǎn)功夫了芬骄。本文以圓形為例子來介紹此類場景的處理方法。

先看下面一張圖(附圖1)鹦聪,我們的目標(biāo)是實(shí)現(xiàn)如下自定義tabbar账阻。中間帶突起圓形的自定義tabbar曾一度流行,今天我們來粗糙地實(shí)現(xiàn)一下泽本。

在附圖一中淘太,紅色代表tabbar,上面有三個藍(lán)色按鈕规丽。在三個按鈕中我們重點(diǎn)解決按鈕A蒲牧,因?yàn)樗幸话氲膮^(qū)域突在tabbar的有效區(qū)域外。

對于按鈕A嘁捷,我們有以下兩個問題需要解決:

1造成、如何準(zhǔn)確過濾掉A外接矩形里非藍(lán)色區(qū)域的點(diǎn)擊事件?

2雄嚣、如何讓A的上半部分也能響應(yīng)觸摸事件晒屎?

其實(shí)兩個問題的解決方法是基本一致的喘蟆。在iOS中所有控件都是以矩形的方式存在的,在圖2中盡管藍(lán)色部分看起來是圓形鼓鲁,但當(dāng)點(diǎn)擊外接矩形內(nèi)的非圓形區(qū)域時也會默認(rèn)觸發(fā)點(diǎn)擊事件蕴轨。因此,我們需要用一些手段把觸摸事件“攔截”下來骇吭。想要“攔截”事件橙弱,就必須了解iOS的事件分發(fā)機(jī)制,也就是當(dāng)你點(diǎn)擊設(shè)備屏幕后燥狰,iOS是如何決定由那個view去最終響應(yīng)你的觸摸棘脐!下面插播一小段關(guān)于iOS事件分發(fā)的介紹:

==================================

當(dāng)你手指觸摸屏幕后會發(fā)生以下事情:觸摸事件被封裝成一個UIEvent事件,去當(dāng)前iOS操作系統(tǒng)的active app隊(duì)列中取當(dāng)前活躍的APP龙致,把event傳給它--->event傳給UIApplication--->傳給UIWindow的root view controller(rootVC)--->調(diào)用rootVC.view的所有subviews的hitTest:event:方法蛀缝。哪個view的hitTest:event方法返回非nil值,則觸摸事件就交給該view處理目代。關(guān)于事件分發(fā)的詳細(xì)機(jī)制及舉例可以參考技術(shù)哥大神的文章

==================================

分析
讓我們重新回到探討的問題上屈梁。通過以上簡介我們可以知道,想“攔截”觸摸事件榛了,則應(yīng)該在tabbar的hitTest:event方法中做處理(坐標(biāo)判斷等)在讶。以下是具體的demo源碼:

import <UIKit/UIKit.h>

@interface panelView : UIView

@end

import "panelView.h"

@implementation panelView

  • (id)initWithFrame:(CGRect)frame
    {
    self = [super initWithFrame:frame];
    if (self) {
    [self initSubviews];
    }
    return self;
    }

  • (void)initSubviews
    {
    UIButton *roundBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    roundBtn.frame = CGRectMake(self.frame.size.width / 2 - 30, -30, 60, 60);
    roundBtn.backgroundColor = [UIColor blueColor];
    roundBtn.layer.cornerRadius = 30;
    roundBtn.tag = 10086;
    [roundBtn addTarget:self action:@selector(onBtnPressed:)
    forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:roundBtn];

    UIButton *leftBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    leftBtn.frame = CGRectMake(0, 15, 30, 30);
    leftBtn.backgroundColor = [UIColor blueColor];
    leftBtn.tag = 10087;
    [leftBtn addTarget:self action:@selector(onBtnPressed:)
    forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:leftBtn];

    UIButton *rightBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    rightBtn.frame = CGRectMake(self.frame.size.width - 30, 15, 30, 30);
    rightBtn.backgroundColor = [UIColor blueColor];
    rightBtn.tag = 10088;
    [rightBtn addTarget:self action:@selector(onBtnPressed:)
    forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:rightBtn];
    }

  • (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
    UIView *hitView = nil;
    //NSLog(@"point:%@", NSStringFromCGPoint(point));
    UIButton *roundBtn = (UIButton *)[self viewWithTag:10086];
    UIButton *leftBtn = (UIButton *)[self viewWithTag:10087];
    UIButton *rightBtn = (UIButton *)[self viewWithTag:10088];
    BOOL pointInRound = [self touchPointInsideCircle:roundBtn.center radius:30 targetPoint:point];
    if (pointInRound) {
    hitView = roundBtn;
    } else if(CGRectContainsPoint(leftBtn.frame, point)) {
    hitView = leftBtn;
    } else if(CGRectContainsPoint(rightBtn.frame, point)) {
    hitView = rightBtn;
    } else {
    hitView = self;
    }
    return hitView;
    }

  • (BOOL)touchPointInsideCircle:(CGPoint)center radius:(CGFloat)radius targetPoint:(CGPoint)point
    {
    CGFloat dist = sqrtf((point.x - center.x) * (point.x - center.x) +
    (point.y - center.y) * (point.y - center.y));
    return (dist <= radius);
    }

  • (void)onBtnPressed:(id)sender
    {
    UIButton *btn = (UIButton *)sender;
    NSLog(@"btn tag:%d", btn.tag);
    }
    @end

在hitTest方法中最重要的是判斷按鈕A所在的區(qū)域,其實(shí)僅僅用到兩點(diǎn)的距離公式來圈出藍(lán)色部分所在的圓形霜大,判斷方法如下:

  • (BOOL)touchPointInsideCircle:(CGPoint)center radius:(CGFloat)radius targetPoint:(CGPoint)point
    {
    CGFloat dist = sqrtf((point.x - center.x) * (point.x - center.x) +
    (point.y - center.y) * (point.y - center.y));
    return (dist <= radius);
    }

而判斷點(diǎn)是否在按鈕B/C內(nèi)就更簡單了构哺,系統(tǒng)提供了封裝好的api:

1
bool CGRectContainsPoint(CGRect rect, CGPoint point)

最終,關(guān)于事件“攔截”的判斷如下:

  • (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
    UIView *hitView = nil;
    //NSLog(@"point:%@", NSStringFromCGPoint(point));
    UIButton *roundBtn = (UIButton *)[self viewWithTag:10086];
    UIButton *leftBtn = (UIButton *)[self viewWithTag:10087];
    UIButton *rightBtn = (UIButton *)[self viewWithTag:10088];
    BOOL pointInRound = [self touchPointInsideCircle:roundBtn.center radius:30 targetPoint:point];
    if (pointInRound) {
    hitView = roundBtn;
    } else if(CGRectContainsPoint(leftBtn.frame, point)) {
    hitView = leftBtn;
    } else if(CGRectContainsPoint(rightBtn.frame, point)) {
    hitView = rightBtn;
    } else {
    hitView = self;
    }
    return hitView;
    }

此外僧诚,在hitTest中還可以玩其他花樣遮婶,比如將本該由按鈕A響應(yīng)的時間強(qiáng)制性轉(zhuǎn)發(fā)給其他按鈕,這只需在hitTest的返回值中修改一下即可湖笨!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蹦骑,隨后出現(xiàn)的幾起案子慈省,更是在濱河造成了極大的恐慌,老刑警劉巖眠菇,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件边败,死亡現(xiàn)場離奇詭異,居然都是意外死亡捎废,警方通過查閱死者的電腦和手機(jī)笑窜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來登疗,“玉大人排截,你說我怎么就攤上這事嫌蚤。” “怎么了断傲?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵脱吱,是天一觀的道長。 經(jīng)常有香客問我认罩,道長箱蝠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任垦垂,我火速辦了婚禮宦搬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘劫拗。我一直安慰自己床三,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布杨幼。 她就那樣靜靜地躺著撇簿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪差购。 梳的紋絲不亂的頭發(fā)上四瘫,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音欲逃,去河邊找鬼找蜜。 笑死,一個胖子當(dāng)著我的面吹牛稳析,可吹牛的內(nèi)容都是我干的洗做。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼彰居,長吁一口氣:“原來是場噩夢啊……” “哼诚纸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起陈惰,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤畦徘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后抬闯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體井辆,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年溶握,在試婚紗的時候發(fā)現(xiàn)自己被綠了杯缺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡睡榆,死狀恐怖萍肆,靈堂內(nèi)的尸體忽然破棺而出袍榆,到底是詐尸還是另有隱情,我是刑警寧澤匾鸥,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布蜡塌,位于F島的核電站,受9級特大地震影響勿负,放射性物質(zhì)發(fā)生泄漏馏艾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一奴愉、第九天 我趴在偏房一處隱蔽的房頂上張望琅摩。 院中可真熱鬧,春花似錦锭硼、人聲如沸房资。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轰异。三九已至,卻和暖如春暑始,著一層夾襖步出監(jiān)牢的瞬間搭独,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工廊镜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牙肝,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓嗤朴,卻偏偏與公主長得像配椭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子雹姊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

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