iOS事件響應(yīng)鏈中hitTest的應(yīng)用示例

// 作用: 去尋找最適合的View
// 什么時(shí)候調(diào)用: 當(dāng)一個(gè)事件傳遞給當(dāng)前View,就會(huì)調(diào)用.
// 返回值: 返回的是誰,誰就是最適合的View(就會(huì)調(diào)用最適合的View的touch方法)
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        return super.hitTest(point, with: event)
}

hitTest的底層實(shí)現(xiàn):

  • 1.先看自己是否能接受觸摸事件
  • 2.再看觸摸點(diǎn)是否在自己身上
  • 3.從后往前遍歷子控件裁僧,拿到子控件后订讼,再次重復(fù)1,2步驟挟憔,要把父控件上的坐標(biāo)點(diǎn)轉(zhuǎn)換為子控件坐標(biāo)系下的點(diǎn)狸窘,再次執(zhí)行hitTest方法
  • 4.若是最后還沒有找到合適的view墩朦,那么就return self,自己就是合適的view

備注:當(dāng)控件接收到觸摸事件的時(shí)候翻擒,不管能不能處理事件氓涣,都會(huì)調(diào)用hitTest方法

應(yīng)用實(shí)例

1.擴(kuò)大UIButton的響應(yīng)熱區(qū)

import UIKit

private var ts_touchAreaEdgeInsets: UIEdgeInsets = .zero

extension UIButton {
    /// Increase your button touch area.
    /// If your button frame is (0,0,40,40). Then call button.ts_touchInsets = UIEdgeInsetsMake(-30, -30, -30, -30), it will Increase the touch area
    public var ts_touchInsets: UIEdgeInsets {
        get {
            if let value = objc_getAssociatedObject(self, &ts_touchAreaEdgeInsets) as? NSValue {
                var edgeInsets: UIEdgeInsets = .zero
                value.getValue(&edgeInsets)
                return edgeInsets
            }else {
                return .zero
            }
        }
        set(newValue) {
            var newValueCopy = newValue
            let objCType = NSValue(uiEdgeInsets: .zero).objCType
            let value = NSValue(&newValueCopy, withObjCType: objCType)
            objc_setAssociatedObject(self, &ts_touchAreaEdgeInsets, value, .OBJC_ASSOCIATION_RETAIN)
        }
    }
    
    open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        if UIEdgeInsetsEqualToEdgeInsets(self.ts_touchInsets, .zero) || !self.isEnabled || self.isHidden {
            return super.point(inside: point, with: event)
        }
        
        let relativeFrame = self.bounds
        let hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.ts_touchInsets)
        
        return hitFrame.contains(point)
    }
}

使用示例

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    areaBtn.ts_touchInsets = .init(top: -30, left: -30, bottom: -30, right: -30)
}

也可以自定義UIButton,在自定義的button里實(shí)現(xiàn)

import UIKit
class TouchIncreaseButton: UIButton {
    
    private let btnWidth : CGFloat = 44
    private let btnHeight : CGFloat = 44

    private func hitTestBounds(minimumHitTestWidth minWidth : CGFloat,minimumHitTestHeight minHeight : CGFloat) -> CGRect {
        var hitTestBounds = self.bounds
        if minWidth > bounds.size.width {
            hitTestBounds.size.width = minWidth
            hitTestBounds.origin.x -= (hitTestBounds.size.width - bounds.size.width)/2
        }
        if minHeight > bounds.size.height {
            hitTestBounds.size.height = minHeight
            hitTestBounds.origin.y -= (hitTestBounds.size.height - bounds.size.height)/2
        }
        
        return hitTestBounds
    }
    
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        
        let rect = hitTestBounds(minimumHitTestWidth: btnWidth, minimumHitTestHeight: btnHeight)
        return rect.contains(point)
    }
}

2.子view超出了父viewbounds響應(yīng)事件

demo.png

重載父viewhitTest(_ point: CGPoint, with event: UIEvent?)方法

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    for subView in self.subviews {
        // 把父類point點(diǎn)轉(zhuǎn)換成子類坐標(biāo)系下的點(diǎn)
        let convertedPoint = subView.convert(point, from: self)
        
        // 注意點(diǎn):hitTest方法內(nèi)部會(huì)調(diào)用pointInside方法牛哺,詢問觸摸點(diǎn)是否在這個(gè)控件上
        // 根據(jù)point,找到適合響應(yīng)事件的這個(gè)View
        let hitTestView = subView.hitTest(convertedPoint, with: event)
        if hitTestView != nil {
            return hitTestView
        }
    }
    return nil
}

3.使部分區(qū)域失去響應(yīng).

tableView.png

場景需求:如圖,tableView占整個(gè)屏幕,tableView底下是一個(gè)半透明的HUD,點(diǎn)擊下面沒有內(nèi)容區(qū)域,要讓HUD去響應(yīng)事件.

在自定義的tableView中重載hitTest方法

// tableView會(huì)攔截底部`backgroundView`事件的響應(yīng),
// 實(shí)現(xiàn)點(diǎn)擊tableViewCell 之外的地方,讓tableView底下的backgroundView響應(yīng)tap事件
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if let hitView = super.hitTest(point, with: event) , hitView.isKind(of: BTTableCellContentView.self) {
        return hitView
    }
    // 返回nil 那么事件就不由當(dāng)前控件處理
    return nil;
}

4.讓非scrollView區(qū)域響應(yīng)scrollView拖拽事件

scrollView.png

如圖,這是一個(gè)使用scrollView自定義實(shí)現(xiàn)的卡片式輪播器劳吠,如何實(shí)現(xiàn)拖拽scrollView兩邊的view區(qū)域引润,和拖拽中間scrollView一樣的效果呢?只需要在scrollView的父View重載hitTest方法

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if self.point(inside: point, with: event) == true {
        return scrollView
    }
    return nil
}

3.1.使自定義Button失去響應(yīng)

class NoEventButton: UIButton {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        return nil
    }
}

參考:iOS事件響應(yīng)鏈中Hit-Test View的應(yīng)用
ios開發(fā)事件處理之 四:hittest方法的底層實(shí)現(xiàn)與應(yīng)用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末痒玩,一起剝皮案震驚了整個(gè)濱河市淳附,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蠢古,老刑警劉巖奴曙,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異草讶,居然都是意外死亡洽糟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進(jìn)店門堕战,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坤溃,“玉大人,你說我怎么就攤上這事嘱丢⌒浇椋” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵屿讽,是天一觀的道長昭灵。 經(jīng)常有香客問我,道長伐谈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任试疙,我火速辦了婚禮诵棵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘祝旷。我一直安慰自己履澳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布怀跛。 她就那樣靜靜地躺著距贷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吻谋。 梳的紋絲不亂的頭發(fā)上忠蝗,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天,我揣著相機(jī)與錄音漓拾,去河邊找鬼阁最。 笑死戒祠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的速种。 我是一名探鬼主播姜盈,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼配阵!你這毒婦竟也來了馏颂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棋傍,失蹤者是張志新(化名)和其女友劉穎饱亮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舍沙,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡近上,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拂铡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壹无。...
    茶點(diǎn)故事閱讀 40,438評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖感帅,靈堂內(nèi)的尸體忽然破棺而出斗锭,到底是詐尸還是另有隱情,我是刑警寧澤失球,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布岖是,位于F島的核電站,受9級特大地震影響实苞,放射性物質(zhì)發(fā)生泄漏豺撑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一黔牵、第九天 我趴在偏房一處隱蔽的房頂上張望聪轿。 院中可真熱鬧,春花似錦猾浦、人聲如沸陆错。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽音瓷。三九已至,卻和暖如春夹抗,著一層夾襖步出監(jiān)牢的瞬間绳慎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偷线,地道東北人磨确。 一個(gè)月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像声邦,于是被迫代替她去往敵國和親乏奥。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評論 2 359

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