????面試官:“如何制作一個(gè)菱形的Button和措,比如現(xiàn)在button的背景圖是個(gè)菱形,如何實(shí)現(xiàn)點(diǎn)擊圖片中的菱形內(nèi)有響應(yīng)而點(diǎn)擊菱形外沒(méi)有響應(yīng)蜕煌?“?
? ? 今天上班時(shí)腦子里突然閃過(guò)了這個(gè)場(chǎng)景和這個(gè)問(wèn)題派阱,上述是一個(gè)真實(shí)的場(chǎng)景,當(dāng)然結(jié)果是悲慘的.... ? 我一方面責(zé)怪自己沒(méi)有在面試后就馬上去總結(jié)下這個(gè)問(wèn)題斜纪,另一方面開(kāi)始考慮這個(gè)問(wèn)題應(yīng)該如何實(shí)現(xiàn)贫母。雖然說(shuō)亡羊補(bǔ)牢已經(jīng)晚了,就當(dāng)是給自己的一個(gè)教訓(xùn)吧盒刚。廢話(huà)說(shuō)了這么多腺劣,那到底如何才能實(shí)現(xiàn)上述需求呢?
????首先我想試用button的layer層去繪制整個(gè)button因块,結(jié)果當(dāng)然是否定的橘原,layer層負(fù)責(zé)button的繪制和顯示,但是它并沒(méi)有響應(yīng)交互(觸摸)事件的接口涡上,所以無(wú)論這個(gè)button長(zhǎng)啥樣兒趾断,負(fù)責(zé)響應(yīng)的部分還是原來(lái)的樣子∠判福看樣子只能另行他法了歼冰,其實(shí)到目前為止我們的思路都是處于視覺(jué)上去解決這個(gè)事情。實(shí)際上應(yīng)該從iOS響應(yīng)觸碰事件的角度去考慮耻警,因此不得不扯出一個(gè)關(guān)鍵詞:響應(yīng)鏈隔嫡。
????在iOS中,交互事件分為三類(lèi)——觸控事件甘穿、傳感器事件腮恩、遠(yuǎn)程控制事件。既然是按鍵的點(diǎn)擊響應(yīng)温兼,那這里我們只談?wù)動(dòng)|控事件秸滴。當(dāng)用戶(hù)與App發(fā)生觸摸交互行為時(shí),系統(tǒng)不斷發(fā)送給我們App的那些事件對(duì)象募判,然后按照特定的路徑傳遞給我們App中的一些對(duì)象來(lái)處理荡含。iOS中我們用UITouch表示觸摸對(duì)象,UIEvent表示觸摸事件届垫,UIEvent中包含與用戶(hù)觸摸操作相關(guān)的所有UITouch對(duì)象(allTouches)释液。UIKit使用UIResponder作為響應(yīng)對(duì)象,來(lái)響應(yīng)系統(tǒng)傳遞過(guò)來(lái)的事件并進(jìn)行處理装处。UIApplication误债、UIViewController、UIView、和所有從UIView派生出來(lái)的UIKit類(lèi)(包括UIWindow)都直接或間接地繼承自UIResponder類(lèi)寝蹈。而它則為我們提供了四種方法來(lái)響應(yīng)觸控事件李命。
1- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
2- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
3- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
4- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
? ? 其中UITouch類(lèi)提供了觸控操作的時(shí)間、location以及tapCount等屬性(readonly)箫老。這就表明我們可以通過(guò)覆寫(xiě)上述四種方法來(lái)實(shí)現(xiàn)對(duì)任意一種視圖的自定義觸控封字。
響應(yīng)鏈?zhǔn)鞘裁矗繌淖置嬉饬x描述槽惫,響應(yīng)鏈?zhǔn)且幌盗邢噙B的響應(yīng)者對(duì)象周叮。而從系統(tǒng)角度出發(fā),響應(yīng)鏈?zhǔn)且粋€(gè)可以響應(yīng)用戶(hù)交互行為的過(guò)程界斜。針對(duì)觸控事件,當(dāng)一個(gè)用戶(hù)點(diǎn)擊了App視圖上的某一個(gè)視圖時(shí)合冀,系統(tǒng)中經(jīng)歷了以下兩個(gè)關(guān)鍵過(guò)程:
? ? 1各薇、返回觸摸事件發(fā)生的Hit-test視圖
首先,觸摸事件由UIApplication對(duì)象傳遞給App的關(guān)鍵窗口對(duì)象(key window)君躺,而窗口對(duì)象則嘗試把事件傳遞給觸摸發(fā)生的視圖峭判,這個(gè)視圖被稱(chēng)作hit-test視圖,而尋找hit-test視圖的過(guò)程被稱(chēng)作hit-testing棕叫。Hit-testing包括檢查觸摸事件是否發(fā)生在任何相關(guān)視圖對(duì)象的范圍內(nèi)林螃, 如果是,則遞歸地檢查所有視圖的子視圖俺泣。在視圖層次中的最底層視圖疗认,如果它包含了觸摸點(diǎn),那么它就是hit-test視圖伏钠。等 iOS決定了hit-test視圖之后横漏,它把觸摸事件傳遞給該視圖以便處理。hitTest:withEvent: 方法為給定的CGPoint 和 UIEvent返回hit-test 視圖熟掂。hitTest:withEvent:方法首先會(huì)調(diào)用pointInside:withEvent: 方法缎浇。如果觸摸點(diǎn)在視圖范圍內(nèi),則pointInside:withEvent:返回YES赴肚。然后素跺,方法遞歸地給每個(gè)子視圖調(diào)用hitTest:withEvent:方法并。如果傳遞到hitTest:withEvent:方法的點(diǎn)不再視圖的范圍內(nèi)誉券,pointInside:withEvent:方法返回NO,則該觸摸點(diǎn)被忽視指厌,并且hitTest:withEvent:返回nil.?如果一個(gè)子視圖返回NO,則視圖層次的整個(gè)分支都被忽視横朋。如果hit-test視圖被找到仑乌,則又它處理觸摸事件。
? ? 2、由Hit-test視圖開(kāi)始的觸摸事件傳遞
????很多類(lèi)型的事件都在事件傳遞中依賴(lài)響應(yīng)鏈晰甚。 響應(yīng)鏈?zhǔn)且幌盗邢噙B的響應(yīng)者對(duì)象衙传。 它由第一個(gè)響應(yīng)者開(kāi)始,以應(yīng)用對(duì)象結(jié)束厕九。 如果第一響應(yīng)者不能處理該事件蓖捶,它把事件傳遞給響應(yīng)鏈中的下一個(gè)響應(yīng)者。在觸摸事件中扁远,如果hit-test視圖無(wú)法處理一個(gè)觸摸事件俊鱼,則該事件以hit-test視圖開(kāi)始的響應(yīng)鏈中往上傳遞。
扯了那么多題外話(huà)畅买,那到底如何實(shí)現(xiàn)一個(gè)菱形的button呢并闲?其實(shí)關(guān)鍵點(diǎn)就在尋找hit-test視圖中,我們可以自定義button的子類(lèi)谷羞,并在創(chuàng)建按鍵時(shí)實(shí)例化其子類(lèi)帝火,當(dāng)我們點(diǎn)擊button時(shí),系統(tǒng)會(huì)進(jìn)入尋找觸摸事件發(fā)生的Hit-test視圖的過(guò)程湃缎,此時(shí)我們通過(guò)覆寫(xiě)pointInside:withEvent:方法(入?yún)?huì)傳入觸控點(diǎn)CGPoint)犀填,如果觸點(diǎn)落在了菱形外,我們就返回NO嗓违,則該點(diǎn)就會(huì)被忽視九巡,無(wú)法響應(yīng)點(diǎn)擊事件。如果落在了菱形內(nèi)蹂季,返回YES冕广,表明被找到,則可以響應(yīng)點(diǎn)擊事件乏盐。那么如何區(qū)分菱形的內(nèi)外呢佳窑? 其實(shí)這是一個(gè)仁者見(jiàn)仁的事情,OBShapedButton為我們提供了一種思路:
如果button的背景圖是個(gè)菱形父能,由于圖片是矩形神凑,那么菱形外的alpha值必定為0,因此何吝,我們可以通過(guò)計(jì)算得出圖片中當(dāng)前觸摸點(diǎn)(CGPoint)的alpha值溉委,再與我們的預(yù)設(shè)門(mén)限值作比較,如果觸摸點(diǎn)在菱形內(nèi)則alpha大于預(yù)設(shè)值爱榕,返回YES瓣喊,反之則返回NO。上面的代碼判斷alpha值黔酥,下面的代碼為覆寫(xiě)pointInside:withEvent方法藻三,利用alpha的判斷結(jié)果確定觸摸點(diǎn)在菱形外還是菱形內(nèi)洪橘。這樣就達(dá)到了當(dāng)點(diǎn)擊菱形外時(shí),因?yàn)閜ointInside:withEvent返回NO棵帽,從而系統(tǒng)放棄了對(duì)hit-test視圖的尋找熄求,從而不會(huì)進(jìn)行響應(yīng)的效果了。
? ?? ? 其實(shí)掌握了方法逗概,無(wú)所謂菱形圓形三角形弟晚,判斷方法都是一樣的,而根據(jù)point的alpha值也只是其中一種思路逾苫,畢竟內(nèi)部原理了解過(guò)后卿城,實(shí)現(xiàn)方式就有很多種嘛。