第十九章——UIGestureRecognizer 和 UIMenuController【譯】

在第十八章中歹河,您通過(guò)實(shí)現(xiàn) UIResponder 的方法來(lái)處理原始觸摸抵蚊。 有時(shí)你想檢測(cè)一個(gè)特定的觸摸模式——手勢(shì),如捏或滑動(dòng)妒穴。 您可以使用 UIGestureRecognizer 的實(shí)例而不用自己寫代碼來(lái)檢測(cè)常用手勢(shì)搪哪。

UIGestureRecognizer 攔截由視圖處理的觸摸靡努。 當(dāng)它識(shí)別出一個(gè)特定的手勢(shì)時(shí),它就會(huì)根據(jù)你選擇的對(duì)象調(diào)用一個(gè)方法晓折。 SDK 中內(nèi)置了多種類型的手勢(shì)識(shí)別器惑朦。 在本章中,您將使用其中三個(gè)來(lái)允許 TouchTracker 用戶選擇已维,移動(dòng)和刪除線(圖19.1)。 您還將看到如何使用另一個(gè)有趣的 iOS 類已日,UIMenuController

圖19.1 本章末尾的TouchTracker

UIGestureRecognizer子類

您不需要自己去實(shí)例化 UIGestureRecognizer。 相反, UIGestureRecognizer 有很多子類馆里,每個(gè)子類負(fù)責(zé)識(shí)別一個(gè)特定的手勢(shì)伞芹。

要使用 UIGestureRecognizer 子類的實(shí)例,請(qǐng)給它一個(gè) 目標(biāo)動(dòng)作對(duì) 并將其與視圖相關(guān)聯(lián)护奈。 每當(dāng)手勢(shì)識(shí)別器在視圖上識(shí)別其手勢(shì)時(shí)缔莲,它將向其目標(biāo)發(fā)送動(dòng)作消息。 所有 UIGestureRecognizer 動(dòng)作消息具有相同的形式:

func action(_ gestureRecognizer: UIGestureRecognizer) { }

當(dāng)識(shí)別手勢(shì)時(shí)霉旗,手勢(shì)識(shí)別器截取視圖內(nèi)指定的觸摸(圖19.2)痴奏。 因此,在使用了手勢(shì)識(shí)別器的視圖上可能不會(huì)調(diào)用像 touchesBegan(_:with :) 這樣的典型的 UIResponder 方法厌秒。

圖19.2 手勢(shì)識(shí)別器攔截

用 UITapGestureRecognizer 檢測(cè)點(diǎn)擊

您將使用的第一個(gè) UIGestureRecognizer 子類是 UITapGestureRecognizer读拆。 當(dāng)用戶點(diǎn)擊屏幕兩次時(shí),屏幕上的所有線將被清除鸵闪。

打開(kāi) TouchTracker.xcodeprojDrawView.swift檐晕。 添加一個(gè) init?(coder :) 方法并實(shí)例化一個(gè) UITapGestureRecognizer,需要兩次點(diǎn)擊來(lái)觸發(fā)并調(diào)用其目標(biāo)上的動(dòng)作方法。

required init?(coder aDecoder: NSCoder) {
??super.init(coder: aDecoder)

??let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(DrawView.doubleTap(_:)))
??doubleTapRecognizer.numberOfTapsRequired = 2
??addGestureRecognizer(doubleTapRecognizer)
}

現(xiàn)在當(dāng)在 DrawView 的一個(gè)實(shí)例上發(fā)生雙擊時(shí)辟灰,將在該實(shí)例上調(diào)用 doubleTap(_ :) 方法个榕。 在 DrawView.swift 中實(shí)現(xiàn)此方法。

func doubleTap(_ gestureRecognizer: UIGestureRecognizer) {
??print("Recognized a double tap")

??currentLines.removeAll()
??finishedLines.removeAll()
??setNeedsDisplay()
}

請(qǐng)注意芥喇,手勢(shì)識(shí)別器的動(dòng)作方法的參數(shù)是調(diào)用該方法的 UIGestureRecognizer 的實(shí)例西采。 在雙擊的情況下,您不需要識(shí)別器的任何信息乃坤,但在本章后面您將需要用到其他識(shí)別器的信息苛让。

構(gòu)建并運(yùn)行應(yīng)用程序,繪制幾條線湿诊,然后雙擊屏幕以清除它們狱杰。

您可能已經(jīng)注意到(特別是在模擬器上),第一次點(diǎn)擊雙擊會(huì)導(dǎo)致繪制一個(gè)小紅點(diǎn)厅须。 之所以出現(xiàn)這個(gè)點(diǎn)仿畸,是因?yàn)樵诘谝淮吸c(diǎn)擊時(shí),我們?cè)?DrawView 上調(diào)用了 touchesBegan(_:with :)朗和,創(chuàng)建了一條很短的線错沽。 檢查控制臺(tái),您將看到以下事件序列:

touchesBegan(_:with:)
Recognized a double tap
touchesCancelled(_:with:)

手勢(shì)識(shí)別器通過(guò)檢查觸摸事件來(lái)確定他們的特定手勢(shì)是否發(fā)生眶拉。 在識(shí)別手勢(shì)之前千埃,手勢(shì)識(shí)別器攔截所有的 UIResponder 方法調(diào)用。 如果它沒(méi)有識(shí)別出是某個(gè)手勢(shì)忆植,那么每個(gè)調(diào)用都將轉(zhuǎn)發(fā)回到視圖中去放可。

識(shí)別點(diǎn)擊需要觸摸的開(kāi)始和結(jié)束。 這意味著當(dāng)最初調(diào)用 touchesBegan(_:with :) 時(shí)朝刊,UITapGestureRecognizer 不知道該觸摸是否是點(diǎn)擊耀里,因此還是會(huì)在視圖上調(diào)用該方法。 當(dāng)觸摸結(jié)束時(shí)拾氓,識(shí)別出為點(diǎn)擊冯挎,手勢(shì)識(shí)別器會(huì)將該觸摸聲明為自己所有。 它通過(guò)在視圖上調(diào)用 touchesCancelled(_:with :) 來(lái)實(shí)現(xiàn)咙鞍。 之后房官,其它的 UIResponder 方法將無(wú)法在該視圖中調(diào)用。

要防止這個(gè)紅點(diǎn)暫時(shí)出現(xiàn)续滋,您必須防止在視圖中調(diào)用 touchesBegan(_:with :)易阳。 您可以告訴 UIGestureRecognizer 在其視圖上延遲調(diào)用 touchesBegan(_:with :),因?yàn)樵撚|摸有可能會(huì)被識(shí)別為手勢(shì)吃粒。

DrawView.swift 中潦俺,修改 init?(coder :) 來(lái)實(shí)現(xiàn)。

required init?(coder aDecoder: NSCoder) {
??super.init(coder: aDecoder)

??let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(DrawView.doubleTap(_:)))
??doubleTapRecognizer.numberOfTapsRequired = 2
??doubleTapRecognizer.delaysTouchesBegan = true
??addGestureRecognizer(doubleTapRecognizer)
}

構(gòu)建并運(yùn)行應(yīng)用程序,繪制一些線事示,然后雙擊以清除它們早像。 雙擊時(shí),您將不會(huì)再看到紅點(diǎn)肖爵。

多種手勢(shì)識(shí)別器

下一步是添加另一個(gè)允許用戶選擇一條線的手勢(shì)識(shí)別器卢鹦。 (稍后,用戶將能夠刪除所選行劝堪。)您將在 DrawView 上添加只需要一次點(diǎn)擊就觸發(fā)的另一個(gè) UITapGestureRecognizer冀自。

DrawView.swift 中,修改 init?(coder :) 來(lái)添加這個(gè)手勢(shì)識(shí)別器秒啦。

required init?(coder aDecoder: NSCoder) {
??super.init(coder: aDecoder)

??let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(DrawView.doubleTap(_:)))
??doubleTapRecognizer.numberOfTapsRequired = 2
??doubleTapRecognizer.delaysTouchesBegan = true
??addGestureRecognizer(doubleTapRecognizer)

??let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(DrawView.tap(_:)))
??tapRecognizer.delaysTouchesBegan = true
??addGestureRecognizer(tapRecognizer)
}

現(xiàn)在熬粗,在 DrawView.swift 中實(shí)現(xiàn) tap(_ :),將點(diǎn)擊記錄到控制臺(tái)余境。

func tap(_ gestureRecognizer: UIGestureRecognizer) {
??print("Recognized a tap")
}

構(gòu)建并運(yùn)行應(yīng)用程序驻呐。 嘗試點(diǎn)擊和雙擊。 點(diǎn)擊一次將適當(dāng)?shù)南⒂涗浀娇刂婆_(tái)芳来。 然而含末,雙擊可以觸發(fā) tap(_:)doubleTap(_ :) 兩個(gè)方法。

在具有多個(gè)手勢(shì)識(shí)別器的情況下即舌,一個(gè)手勢(shì)識(shí)別器識(shí)別到該觸摸并處理佣盒,但其實(shí)你想要讓另一個(gè)手勢(shì)識(shí)別器去處理這個(gè)手勢(shì),這是很常見(jiàn)的顽聂。 在這些情況下肥惭,您可以在識(shí)別器之間設(shè)置依賴關(guān)系,這些依賴關(guān)系就像:“等等你先別急芜飘,這個(gè)觸摸可能是我的务豺!”

init?(coder:) 中磨总,使 tapDecognizer 等待直到 doubleTapRecognizer 無(wú)法識(shí)別雙擊才能聲明為其自身的點(diǎn)擊嗦明。

required init?(coder aDecoder: NSCoder) {
??super.init(coder: aDecoder)

??let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(DrawView.doubleTap(_:)))
??doubleTapRecognizer.numberOfTapsRequired = 2
??doubleTapRecognizer.delaysTouchesBegan = true
??addGestureRecognizer(doubleTapRecognizer)

??let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(DrawView.tap(_:)))
??tapRecognizer.delaysTouchesBegan = true
??tapRecognizer.require(toFail: doubleTapRecognizer)
??addGestureRecognizer(tapRecognizer)
}

再次構(gòu)建并運(yùn)行應(yīng)用程序,并嘗試一些點(diǎn)擊蚪燕。 點(diǎn)擊發(fā)生后娶牌,單擊現(xiàn)在需要少量時(shí)間去觸發(fā),但雙擊不再觸發(fā) tap(_:) 消息馆纳。

接下來(lái)诗良,讓我們?cè)?DrawView 上構(gòu)建,以便用戶可以在點(diǎn)擊時(shí)選擇一條線鲁驶。 首先鉴裹,在 DrawView.swift 的頂部添加一個(gè)屬性來(lái)保存所選線的索引。

class DrawView: UIView {
??var currentLines = [NSValue:Line]()
??var finishedLines = [Line]()
??var selectedLineIndex: Int?

現(xiàn)在修改 draw(_ :) 將所選線畫成綠色。

override func draw(_ rect: CGRect) {
??finishedLineColor.setStroke()
??for line in finishedLines {
????stroke(line)
??}

??currentLineColor.setStroke()
??for (_,line) in currentLines {
????stroke(line)
??}

??if let index = selectedLineIndex {
????UIColor.green.setStroke()
????let selectedLine = finishedLines[index]
????stroke(selectedLine)
??}
}

仍然在 DrawView.swift 中径荔,添加一個(gè) indexOfLine(at :) 方法返回離給定點(diǎn)最接近的 Line 的索引督禽。

func indexOfLine(at point: CGPoint) -> Int? {
??// Find a line close to point
??for (index, line) in finishedLines.enumerated() {
????let begin = line.begin
????let end = line.end

????// Check a few points on the line
????for t in stride(from: CGFloat(0), to: 1.0, by: 0.05) {
??????let x = begin.x + ((end.x - begin.x) * t)
??????let y = begin.y + ((end.y - begin.y) * t)

??????// If the tapped point is within 20 points, let's return this line
??????if hypot(x - point.x, y - point.y) < 20.0 {
????????return index
??????}
????}
??}

??// If nothing is close enough to the tapped point, then we did not select a line
??return nil
}

stride(from:to:by:) 方法將允許 t 從 from 值開(kāi)始,并且增加到(但不達(dá)到) to 值总处,每次增量為 by狈惫。

還有其他更好的方法可以確定最接近某個(gè)點(diǎn)的線條,但是這個(gè)簡(jiǎn)單的實(shí)現(xiàn)已經(jīng)達(dá)到目的了鹦马。

要傳遞的 point 是手指點(diǎn)擊的點(diǎn)胧谈。 您可以輕松獲取此信息。 每個(gè) UIGestureRecognizer 都有一個(gè) location(in :) 方法荸频。 在手勢(shì)識(shí)別器上調(diào)用此方法將給出在作為參數(shù)傳遞的視圖的坐標(biāo)系中觸發(fā)手勢(shì)的坐標(biāo)菱肖。

DrawView.swift 中,更新 tap(_ :) 來(lái)調(diào)用手勢(shì)識(shí)別器的 location(in:)试溯,將結(jié)果傳遞給 indexOfLine(in:)蔑滓,并將返回的索引賦給 selectedLineIndex

func tap(_ gestureRecognizer: UIGestureRecognizer) {
??print("Recognized a tap")

??let point = gestureRecognizer.location(in: self)
??selectedLineIndex = indexOfLine(at: point)

setNeedsDisplay()
}

如果用戶在選擇一條線時(shí)使用了雙擊來(lái)清除所有線遇绞,應(yīng)用程序?qū)⒈罎ⅰ?要解決這個(gè)問(wèn)題键袱,請(qǐng)更新 doubleTap(_ :)selectedLineIndex 設(shè)置為 nil

func doubleTap(_ gestureRecognizer: UIGestureRecognizer) {
??print("Recognized a double tap")

??selectedLineIndex = nil
??currentLines.removeAll()
??finishedLines.removeAll()
??setNeedsDisplay()
}

構(gòu)建并運(yùn)行應(yīng)用程序摹闽。 畫幾條線蹄咖,然后點(diǎn)擊其中一條。 點(diǎn)擊的線應(yīng)顯示為綠色(請(qǐng)記住付鹿,在點(diǎn)擊而不是雙擊被識(shí)別出來(lái)之前需要一些時(shí)間)澜汤。

UIMenuController

接下來(lái),您需要做到當(dāng)用戶選擇一條線時(shí)舵匾,具有刪除該行的選項(xiàng)的菜單將顯示在用戶點(diǎn)擊的位置俊抵。 有一個(gè)內(nèi)置的類用于提供這種菜單,稱為 UIMenuController(圖19.3)坐梯。 菜單控制器具有 UIMenuItem 對(duì)象的列表徽诲,并且在現(xiàn)有視圖中呈現(xiàn)。 每個(gè) item 都有一個(gè)標(biāo)題(菜單中顯示的內(nèi)容)和一個(gè)動(dòng)作(它發(fā)送到窗口的第一響應(yīng)者的消息)吵血。

圖19.3 UIMenuController

每個(gè)應(yīng)用程序只有一個(gè) UIMenuController谎替。 當(dāng)你想呈現(xiàn)這個(gè)實(shí)例時(shí),你可以用菜單項(xiàng)來(lái)填充它蹋辅,給它一個(gè)矩形來(lái)呈現(xiàn)钱贯,并將其設(shè)置為可見(jiàn)。

DrawView.swifttap(_ :) 方法中執(zhí)行如下操作侦另,如果用戶已經(jīng)點(diǎn)擊了一條線秩命。 如果用戶點(diǎn)擊不在該線附近的某處尉共,則當(dāng)前選中的行將被取消選擇,并且菜單控制器將被隱藏弃锐。

func tap(_ gestureRecognizer: UIGestureRecognizer) {
??print("Recognized a tap")

??let point = gestureRecognizer.location(in: self)
??selectedLineIndex = indexOfLine(at: point)

??// Grab the menu controller
??let menu = UIMenuController.shared

??if selectedLineIndex != nil {

????// Make DrawView the target of menu item action messages
????becomeFirstResponder()

????// Create a new "Delete" UIMenuItem
????let deleteItem = UIMenuItem(title: "Delete",action: #selector(DrawView.deleteLine(_:)))
????menu.menuItems = [deleteItem]

????// Tell the menu where it should come from and show it
????let targetRect = CGRect(x: point.x, y: point.y, width: 2, height: 2)
????menu.setTargetRect(targetRect, in: self)
????menu.setMenuVisible(true, animated: true)
??} else {
????// Hide the menu if no line is selected
????menu.setMenuVisible(false, animated: true)
??}

??setNeedsDisplay()
}

要顯示菜單控制器爸邢,在 UIMenuController 的菜單項(xiàng)中響應(yīng)至少一個(gè)動(dòng)作消息的視圖必須是窗口的第一響應(yīng)者——這就是為什么在設(shè)置菜單控制器之前,要在 DrawView 上調(diào)用方法 becomeFirstResponder()拿愧。

如果您的自定義視圖類要成為第一響應(yīng)者杠河,則還必須覆蓋 canBecomeFirstResponder。 在 DrawView.swift 中浇辜,覆蓋此屬性以返回 true券敌。

override var canBecomeFirstResponder: Bool {
??return true
}

最后,在 DrawView.swift 中實(shí)現(xiàn) deleteLine(_ :)柳洋。

func deleteLine(_ sender: UIMenuController) {
??// Remove the selected line from the list of finishedLines
??if let index = selectedLineIndex {
????finishedLines.remove(at: index)
????selectedLineIndex = nil

????// Redraw everything
????setNeedsDisplay()
??}
}

當(dāng)被呈現(xiàn)時(shí)待诅,菜單控制器通過(guò)每個(gè)菜單項(xiàng)并且詢問(wèn)第一響應(yīng)者是否實(shí)現(xiàn)該項(xiàng)的動(dòng)作方法。 如果第一響應(yīng)者沒(méi)有實(shí)現(xiàn)該方法熊镣,則菜單控制器將不會(huì)顯示相關(guān)的菜單項(xiàng)卑雁。 如果菜單項(xiàng)沒(méi)有由第一響應(yīng)者實(shí)現(xiàn)的動(dòng)作方法,則完全不顯示該菜單绪囱。

構(gòu)建并運(yùn)行應(yīng)用程序测蹲。 畫一條線,點(diǎn)擊它鬼吵,然后從菜單項(xiàng)中選擇 Delete扣甲。

選擇一條線,然后雙擊以清除所有行齿椅,而菜單控制器仍然可見(jiàn)琉挖。 如果 selectedLineIndexnil,菜單控制器應(yīng)該為不可見(jiàn)才對(duì)涣脚。

DrawView.swift 中向 propertiesLineIndex 添加屬性觀察器示辈,如果索引設(shè)置為 nil,則將菜單控制器設(shè)置為不可見(jiàn)遣蚀。

var selectedLineIndex: Int? {
??didSet {
????if selectedLineIndex == nil {
??????let menu = UIMenuController.shared
??????menu.setMenuVisible(false, animated: true)
????}
??}
}

構(gòu)建并運(yùn)行應(yīng)用程序矾麻。 畫一條線,選擇它妙同,然后雙擊背景射富。 線和菜單控制器將不再可見(jiàn)膝迎。

更多手勢(shì)識(shí)別器

在本節(jié)中粥帚,您將添加用戶通過(guò)長(zhǎng)按來(lái)選擇線的功能,然后通過(guò)平移移動(dòng)所選線限次。 這將需要用到另外兩個(gè) UIGestureRecognizer 子類:

UILongPressGestureRecognizerUIPanGestureRecognizer芒涡。

UILongPressGestureRecognizer

DrawView.swift 中柴灯,在 init?(coder:) 中實(shí)例化一個(gè) UILongPressGestureRecognizer 并將其添加到 DrawView

...
??addGestureRecognizer(tapRecognizer)

??let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(DrawView.longPress(_:)))
??addGestureRecognizer(longPressRecognizer)
}

當(dāng)用戶按住 DrawView 時(shí)费尽,將會(huì)調(diào)用 longPress(_ :) 方法赠群。 默認(rèn)情況下,觸摸必須持續(xù) 0.5 秒才被視為長(zhǎng)按旱幼,但如果你想修改的話查描,可以更改手勢(shì)識(shí)別器的 minimumPressDuration

到目前為止柏卤,您已經(jīng)使用過(guò)手勢(shì)了冬三。 點(diǎn)擊 是一個(gè) 獨(dú)立(discrete) 的手勢(shì)。 當(dāng)被識(shí)別時(shí)缘缚,手勢(shì)已經(jīng)結(jié)束勾笆,并且已經(jīng)傳送了動(dòng)作消息。 另一方面桥滨,長(zhǎng)按是一個(gè) 持續(xù)(continuous) 的手勢(shì)窝爪。 持續(xù)的手勢(shì)隨著時(shí)間推移而發(fā)生,為了跟蹤持續(xù)的手勢(shì)發(fā)生了什么齐媒,您可以檢查識(shí)別器的state` 屬性蒲每。

例如,考慮一個(gè)典型的長(zhǎng)按:

  • 當(dāng)用戶觸摸視圖時(shí)喻括,長(zhǎng)按識(shí)別器會(huì)注意到 可能(possible) 是一個(gè)長(zhǎng)按啃勉,但是它必須等待觀看觸摸是否持續(xù)足夠長(zhǎng)以成為長(zhǎng)按手勢(shì)。 識(shí)別器的狀態(tài)是 UIGestureRecognizerState.possible双妨。
  • 一旦用戶持續(xù)足夠長(zhǎng)的時(shí)間淮阐,長(zhǎng)按可以被識(shí)別并且手勢(shì)已經(jīng) 開(kāi)始(began)。 識(shí)別器的狀態(tài)是 UIGestureRecognizerState.began刁品。
  • 當(dāng)用戶移除手指時(shí)泣特,手勢(shì)已經(jīng) 結(jié)束(ended)。 識(shí)別器的狀態(tài)是 UIGestureRecognizerState.ended挑随。

當(dāng)長(zhǎng)按手勢(shì)識(shí)別器從 possible 轉(zhuǎn)移到 began 和從 began 到 ended 時(shí)状您,它將其動(dòng)作消息發(fā)送到其目標(biāo)。 要確定在哪個(gè)轉(zhuǎn)換中觸發(fā)動(dòng)作兜挨,可以查看手勢(shì)識(shí)別器的 state膏孟。

請(qǐng)記住,長(zhǎng)按是較大功能的一部分拌汇。 在下一節(jié)中柒桑,您將使用戶可以通過(guò)用長(zhǎng)按開(kāi)始的同一個(gè)手指拖動(dòng)所選直線來(lái)移動(dòng)所選直線線。 所以這里是實(shí)現(xiàn) longPress(_ :) 動(dòng)作方法的計(jì)劃:當(dāng)識(shí)別器處于 began 狀態(tài)時(shí)噪舀,您將選擇距離手勢(shì)發(fā)生的最近的直線魁淳。 當(dāng)識(shí)別器處于 ended 狀態(tài)時(shí)飘诗,您將取消選擇該線。

DrawView.swift 中界逛,實(shí)現(xiàn) longPress(_ :)昆稿。

func longPress(_ gestureRecognizer: UIGestureRecognizer) {
??print("Recognized a long press")

??if gestureRecognizer.state == .began {
????let point = gestureRecognizer.location(in: self)
????selectedLineIndex = indexOfLine(at: point)

????if selectedLineIndex != nil {
??????currentLines.removeAll()
????}
??} else if gestureRecognizer.state == .ended {
????selectedLineIndex = nil
??}

??setNeedsDisplay()
}

構(gòu)建并運(yùn)行應(yīng)用程序。 畫一條直線息拜,然后按住它; 該行將變?yōu)榫G色并成為所選行溉潭。 當(dāng)您放開(kāi)時(shí),該行將恢復(fù)為其之前的顏色少欺,并且將不再是所選行岛抄。

UIPanGestureRecognizer 和 同時(shí)識(shí)別器

DrawView.swift 中,聲明一個(gè) UIPanGestureRecognizer 作為一個(gè)屬性狈茉,以便您可以在所有方法中訪問(wèn)它夫椭。

class DrawView: UIView {

??var currentLines = [NSValue:Line]()
??var finishedLines = [Line]()
??var selectedLineIndex: Int? {
????...
??}
??var moveRecognizer: UIPanGestureRecognizer!

接下來(lái),在 DrawView.swift 中氯庆,添加代碼到 init?(coder :) 來(lái)實(shí)例化一個(gè) UIPanGestureRecognizer蹭秋,設(shè)置其中的一個(gè)屬性,并將其添加到 DrawView堤撵。

let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(DrawView.longPress(_:)))
??addGestureRecognizer(longPressRecognizer)

??moveRecognizer = UIPanGestureRecognizer(target: self, action: #selector(DrawView.moveLine(_:)))
??moveRecognizer.cancelsTouchesInView = false
??addGestureRecognizer(moveRecognizer)
}

cancelsTouchesInView 是什么仁讨? 每個(gè) UIGestureRecognizer 都具有此屬性,默認(rèn)為 true实昨。 當(dāng) cancelsTouchesInViewtrue 時(shí)洞豁,手勢(shì)識(shí)別器將 “吃” 掉任何可識(shí)別的觸摸,并且視圖將無(wú)法通過(guò)傳統(tǒng)的 UIResponder 方法荒给,例如 touchesBegan(_:with :) 來(lái)處理觸摸丈挟。

通常,這是你想要的志电,但不總是曙咽。 在這種情況下,如果平移手勢(shì)識(shí)別器要觸摸它挑辆,則用戶將無(wú)法繪制線條例朱。 當(dāng)您將 cancelsTouchesInView 設(shè)置為 false 時(shí),您確保手勢(shì)識(shí)別器識(shí)別的任何觸摸也將通過(guò) UIResponder 方法傳遞到視圖鱼蝉。

DrawView.swift 中洒嗤,為動(dòng)作方法添加一個(gè)簡(jiǎn)單的實(shí)現(xiàn):

func moveLine(_ gestureRecognizer: UIPanGestureRecognizer) {
??print("Recognized a pan")
}

構(gòu)建并運(yùn)行應(yīng)用程序并繪制一些直線。 因?yàn)?cancelsTouchesInViewfalse魁亦,所以可以識(shí)別平移手勢(shì)渔隶,也可以繪制線條。 您可以注釋掉設(shè)置 cancelsTouchesInView 的代碼行吉挣,再次運(yùn)行以查看差異派撕。

接下來(lái),當(dāng)用戶的手指移動(dòng)到屏幕上時(shí)睬魂,您將更新 moveLine(_ :) 來(lái)重繪所選線终吼。 但首先,您需要兩個(gè)手勢(shì)識(shí)別器才能處理相同的觸摸氯哮。 通常际跪,當(dāng)手勢(shì)識(shí)別器識(shí)別出其手勢(shì)時(shí),它會(huì)吃掉它喉钢,而其他識(shí)別器沒(méi)有機(jī)會(huì)處理該觸摸姆打。 嘗試一下:運(yùn)行應(yīng)用程序,畫一條線肠虽,按住選擇該線幔戏,然后移動(dòng)你的手指。 控制臺(tái)報(bào)告是長(zhǎng)按而不是平移税课。

在這種情況下闲延,默認(rèn)行為是有問(wèn)題的:為了按住并選擇一條線,然后平移以移動(dòng)線韩玩,在這過(guò)程中您的用戶是不會(huì)將手指抬起的垒玲。 因此,兩個(gè)手勢(shì)應(yīng)該同時(shí)發(fā)生找颓,并且即使長(zhǎng)按手勢(shì)已經(jīng)識(shí)別長(zhǎng)按合愈,也必須允許平移手勢(shì)識(shí)別器識(shí)別平移。

為了允許手勢(shì)識(shí)別器與其他手勢(shì)識(shí)別器同時(shí)識(shí)別其手勢(shì)击狮,您可以從 UIGestureRecognizerDelegate 協(xié)議實(shí)現(xiàn)一種方法:

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool

第一個(gè)參數(shù)是要求引導(dǎo)的手勢(shì)識(shí)別器佛析。 它對(duì)其 委托 說(shuō):“如果在我和另一個(gè)的識(shí)別器之中的某一個(gè)剛剛識(shí)別出了一個(gè)手勢(shì)。 那那個(gè)不識(shí)別的應(yīng)該保持在 possible 的狀態(tài)彪蓬,并繼續(xù)追蹤這個(gè)觸摸嗎说莫?”

請(qǐng)注意,調(diào)用本身并沒(méi)有告訴您兩個(gè)識(shí)別器中哪一個(gè)已經(jīng)識(shí)別出它的手勢(shì)寞焙,因此储狭,有的可能被剝奪了識(shí)別其手勢(shì)的機(jī)會(huì)。

默認(rèn)情況下捣郊,該方法返回 false辽狈,并且手勢(shì)識(shí)別器仍處于 possible 狀態(tài),使手勢(shì)中的觸摸處于 recognized 狀態(tài)呛牲。 您可以實(shí)現(xiàn)返回 true 的方法刮萌,以允許兩個(gè)識(shí)別器在相同的觸摸中識(shí)別各自的手勢(shì)。 (如果您需要確定兩個(gè)識(shí)別器中的哪一個(gè)識(shí)別其手勢(shì)娘扩,則可以檢查它們的 state 屬性着茸。)

要長(zhǎng)時(shí)間啟用平移功能壮锻,您將要給平移手勢(shì)識(shí)別器一個(gè)委托(DrawView)。 然后涮阔,當(dāng)長(zhǎng)按識(shí)別器識(shí)別其手勢(shì)時(shí)猜绣,平移手勢(shì)識(shí)別器將在其委托上調(diào)用同時(shí)識(shí)別方法。 您將在 DrawView 中實(shí)現(xiàn)此方法返回 true敬特。 這將允許平移手勢(shì)識(shí)別器識(shí)別在長(zhǎng)按進(jìn)行過(guò)程中發(fā)生的任何平移掰邢。

首先,在 DrawView.swift 中伟阔,聲明 DrawView 符合 UIGestureRecognizerDelegate 協(xié)議辣之。

class DrawView: UIView, UIGestureRecognizerDelegate {

??var currentLines = [NSValue:Line]()
??var finishedLines = [Line]()
??var selectedLineIndex: Int? {
??...
}
var moveRecognizer: UIPanGestureRecognizer!

接下來(lái),在 init?(coder :) 中皱炉,將 DrawView 設(shè)置為 UIPanGestureRecognizer 的委托怀估。

let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(DrawView.longPress(_:)))
??addGestureRecognizer(longPressRecognizer)

??moveRecognizer = UIPanGestureRecognizer(target: self, action: #selector(DrawView.moveLine(_:)))
??moveRecognizer.delegate = self
??moveRecognizer.cancelsTouchesInView = false
??addGestureRecognizer(moveRecognizer)
}

最后,在 DrawView.swift 中合搅,實(shí)現(xiàn)委托方法并返回 true奏夫。

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
??return true
}

對(duì)于這種情況,只有您的平移手勢(shì)識(shí)別器有一個(gè)委托历筝,除了返回 true 則不需要做更多的事情酗昼。 在更復(fù)雜的情況下,您可以使用傳入的手勢(shì)識(shí)別器來(lái)更仔細(xì)地控制同時(shí)識(shí)別梳猪。

現(xiàn)在麻削,當(dāng)長(zhǎng)按開(kāi)始時(shí),UIPanGestureRecognizer 將繼續(xù)跟蹤觸摸春弥,如果用戶的手指開(kāi)始移動(dòng)呛哟,則識(shí)別器將識(shí)別平移。 要看到差異匿沛,運(yùn)行應(yīng)用程序扫责,畫一條線,選擇它逃呼,然后平移鳖孤。 控制臺(tái)將報(bào)告兩種手勢(shì)。(UIGestureRecognizerDelegate 協(xié)議包括其他方法來(lái)幫助您調(diào)整手勢(shì)識(shí)別器的行為抡笼。訪問(wèn)協(xié)議參考頁(yè)面了解更多信息苏揣。)

除了您已經(jīng)看到的狀態(tài)之外,平移手勢(shì)識(shí)別器支持 changed 狀態(tài)推姻。 當(dāng)手指開(kāi)始移動(dòng)時(shí)平匈,識(shí)別器進(jìn)入 began 狀態(tài)并調(diào)用其目標(biāo)上的方法。 當(dāng)手指圍繞屏幕移動(dòng)時(shí),識(shí)別器轉(zhuǎn)換到 changed 的狀態(tài)增炭,并重復(fù)地在其目標(biāo)上調(diào)用動(dòng)作方法忍燥。 當(dāng)手指離開(kāi)屏幕時(shí),識(shí)別器的狀態(tài)被設(shè)置為 ended隙姿,并且最終在目標(biāo)上調(diào)用該方法梅垄。

下一步是實(shí)現(xiàn)平移識(shí)別器在其目標(biāo)上調(diào)用的 moveLine(_ :) 方法。 在這個(gè)實(shí)現(xiàn)中孟辑,您將在平移識(shí)別器上調(diào)用 translationInView(_ :) 方法哎甲。 該 UIPanGestureRecognizer 方法返回在作為參數(shù)傳遞的視圖的坐標(biāo)系中蔫敲,平移點(diǎn)作為 CGPoint 移動(dòng)的距離饲嗽。 當(dāng)平移手勢(shì)開(kāi)始時(shí),該屬性設(shè)置為零點(diǎn)(其中 xy 為0)奈嘿。 隨著不斷的平移貌虾,這個(gè)值被更新——如果向右平移,它具有高 x 值; 如果平移返回到它開(kāi)始的位置裙犹,它的 translation 將回到零點(diǎn)尽狠。

DrawView.swift 中,實(shí)現(xiàn) moveLine(_ :)叶圃。 請(qǐng)注意袄膏,因?yàn)槟鷮?UIPanGestureRecognizer 類發(fā)送手勢(shì)識(shí)別器方法,所以該方法的參數(shù)必須是 UIPanGestureRecognizer 的實(shí)例的引用掺冠,而不是 UIGestureRecognizer 沉馆。

func moveLine(_ gestureRecognizer: UIPanGestureRecognizer) {
??print("Recognized a pan")

??// If a line is selected...
??if let index = selectedLineIndex {
????// When the pan recognizer changes its position...
????if gestureRecognizer.state == .changed {
??????// How far has the pan moved?
??????let translation = gestureRecognizer.translation(in: self)

??????// Add the translation to the current beginning and end points of the line
??????// Make sure there are no copy and paste typos!
??????finishedLines[index].begin.x += translation.x
??????finishedLines[index].begin.y += translation.y
??????finishedLines[index].end.x += translation.x
??????finishedLines[index].end.y += translation.y

??????// Redraw the screen
??????setNeedsDisplay()
????}
??} else {
????// If no line is selected, do not do anything

return
??}
}

構(gòu)建并運(yùn)行應(yīng)用程序。 觸摸并按住一條線并開(kāi)始拖動(dòng)——您會(huì)發(fā)現(xiàn)該線和您的手指不同步德崭。 這到底是怎么回事斥黑?

您正在將該當(dāng)前的 translation 重復(fù)添加到該線的原始終點(diǎn)。 當(dāng)您最后一次調(diào)用此方法時(shí)眉厨,您實(shí)際上需要讓手勢(shì)識(shí)別器來(lái)報(bào)告 translation 中的更改锌奴。 幸運(yùn)的是,你可以做到這一點(diǎn)憾股。 每次報(bào)告更改時(shí)鹿蜀,您可以將平移手勢(shì)識(shí)別器的 translation 設(shè)置回零點(diǎn)。 然后服球,下一次報(bào)告更改時(shí)耻姥,它將獲得自上次發(fā)生以來(lái)的 translation。

DrawView.swift 中的 moveLine(_ :) 底部附近添加以下代碼行有咨。

finishedLines[index].end.x += translation.x
finishedLines[index].end.y += translation.y

gestureRecognizer.setTranslation(CGPoint.zero, in: self)

// Redraw the screen
setNeedsDisplay()

構(gòu)建并運(yùn)行應(yīng)用程序并移動(dòng)一條線琐簇。 效果還不錯(cuò)

關(guān)于 UIGestureRecognizer 的更多信息##

你只是用到了 UIGestureRecognizer 的一些基礎(chǔ)知識(shí)。 它還有更多的子類,更多的屬性和更多的委托方法——你甚至可以創(chuàng)建自己的識(shí)別器婉商。 本節(jié)將為講解關(guān)于 UIGestureRecognizer 其它的信息似忧。 您可以查看文檔以了解更多信息。

當(dāng)手勢(shì)識(shí)別器在視圖中時(shí)丈秩,它確實(shí)會(huì)為您處理所有的 UIResponder 方法盯捌,如 touchesBegan(_:with :)。 手勢(shì)識(shí)別器是非常貪心的蘑秽,所以他們通常不會(huì)讓視圖接觸到觸摸事件饺著,或者他們至少延遲交付這些事件。 您可以在識(shí)別器上設(shè)置屬性肠牲,如 delayedTouchesBegan幼衰,delaysTouchesEndedcancelsTouchesInView 以更改此行為缀雳。 如果您需要比這種全無(wú)變化的方法更好的控制渡嚣,您可以為識(shí)別器實(shí)現(xiàn)委托方法。

有時(shí)肥印,您可能會(huì)有兩個(gè)手勢(shì)識(shí)別器尋找非常相似的手勢(shì)识椰。 您可以將識(shí)別器鏈接在一起,以便一個(gè)失敗后下一個(gè)才能開(kāi)始使用 require(toFail :) 方法深碱。 你在 init?(coder :) 中使用了這個(gè)方法腹鹉,使得點(diǎn)擊識(shí)別器等待雙擊識(shí)別器失敗。

掌握手勢(shì)識(shí)別器必須了解的一件事是他們?nèi)绾谓忉屗麄兊臓顟B(tài)敷硅。 總體來(lái)說(shuō)功咒,識(shí)別器可以輸入七個(gè)狀態(tài):

  • UIGestureRecognizerState.possible
  • UIGestureRecognizerState.failed
  • UIGestureRecognizerState.began
  • UIGestureRecognizerState.cancelled
  • UIGestureRecognizerState.changed
  • UIGestureRecognizerState.recognized
  • UIGestureRecognizerState.ended

識(shí)別器花費(fèi)大部分時(shí)間在 possible 狀態(tài)。 當(dāng)手勢(shì)轉(zhuǎn)換到 possible 狀態(tài)或 failed 狀態(tài)之外的任何狀態(tài)時(shí)竞膳,識(shí)別器的動(dòng)作消息被發(fā)送航瞭,并且可以查看其 state 屬性以了解原因。

failed 狀態(tài)用于識(shí)別器等待多點(diǎn)觸控手勢(shì)坦辟。 在某些時(shí)候刊侯,用戶的手指可能會(huì)達(dá)到一個(gè)位置,從這個(gè)位置他們?cè)僖膊荒軌蜃R(shí)別識(shí)別器的手勢(shì)锉走。 那么手勢(shì)識(shí)別器識(shí)別失敗滨彻。 識(shí)別器在中斷時(shí)進(jìn)入 canceled 的狀態(tài),例如通過(guò)來(lái)電挪蹭。

如果手勢(shì)是連續(xù)的如平移亭饵,那手勢(shì)識(shí)別器將進(jìn)入 began 狀態(tài),然后進(jìn)入 changed 狀態(tài)梁厉,直到手勢(shì)結(jié)束辜羊。 當(dāng)手勢(shì)結(jié)束(或 canceled 狀態(tài) )時(shí)踏兜,識(shí)別器進(jìn)入 ended(或 canceled)狀態(tài),并在返回到 possible 狀態(tài)之前最后發(fā)送其動(dòng)作消息八秃。

對(duì)于識(shí)別離散手勢(shì)(如點(diǎn)擊)手勢(shì)識(shí)別器碱妆,您只能看到 recognized 狀態(tài)(與 ended 狀態(tài)具有相同的值)。

您在本章中未實(shí)現(xiàn)的四個(gè)內(nèi)置識(shí)別器是 UIPinchGestureRecognizer昔驱,UISwipeGestureRecognizer疹尾,UIScreenEdgePanGestureRecognizerUIRotationGestureRecognizer。 每個(gè)都具有允許您微調(diào)其行為的屬性骤肛。 文檔將向您展示怎么做纳本。

最后,如果您想要識(shí)別的手勢(shì)不是由 UIGestureRecognizer 的內(nèi)置子類實(shí)現(xiàn)的腋颠,那么您可以自己對(duì) UIGestureRecognizer 進(jìn)行子類化繁成。 這不在本書的范圍之內(nèi)。 您可以閱讀 UIGestureRecognizer 文檔的 Methods for Subclassing 部分秕豫,了解需要的內(nèi)容朴艰。

白銀挑戰(zhàn):神秘線

應(yīng)用程序中有一個(gè) bug观蓄。 如果您點(diǎn)擊一條線混移,然后在菜單可見(jiàn)時(shí)開(kāi)始繪制新的,您將拖動(dòng)所選行并同時(shí)繪制新線侮穿。 修復(fù)這個(gè)bug歌径。

黃金挑戰(zhàn):速度和大小

當(dāng)你在畫一條線的時(shí)候,你可以用手勢(shì)識(shí)別器來(lái)記錄下平移的速度亲茅。根據(jù)這個(gè)速度調(diào)整線的厚度回铛。不要只靠猜測(cè) pan 識(shí)別器的速度值可以設(shè)為多大或能有多大。(換句話說(shuō)克锣,將各種速度記錄到控制臺(tái)茵肃。)

白金挑戰(zhàn):顏色

用三指向上滑動(dòng)可以彈出一個(gè)顯示顏色的面板。 選擇這些顏色之一應(yīng)該使您以后繪制的任何線條以該顏色顯示袭祟。 通過(guò)放置該面板不應(yīng)該畫出額外的線验残,或者當(dāng)應(yīng)用程序意識(shí)到處理三指滑動(dòng)時(shí),應(yīng)立即刪除任何繪制的線巾乳。

更多:UIMenuController 和 UIResponderStandardEditActions

UIMenuController 通常負(fù)責(zé)在顯示時(shí)向用戶展示一個(gè)“編輯”菜單您没。 (當(dāng)您按住文本字段或文本視圖時(shí)。)因此胆绊,未修改的菜單控制器(您未設(shè)置菜單項(xiàng)的控件)已經(jīng)具有其所呈現(xiàn)的默認(rèn)菜單項(xiàng)氨鹏,如 剪切(Cut),復(fù)制(Copy) 和其他熟悉的選項(xiàng)压状。 每項(xiàng)都有一個(gè)相關(guān)聯(lián)的動(dòng)作消息仆抵。 例如,當(dāng) Cut 菜單項(xiàng)被點(diǎn)擊時(shí),cut: 被發(fā)送到呈現(xiàn)菜單控制器的視圖镣丑。

UIResponder 的所有實(shí)例都會(huì)實(shí)現(xiàn)這些方法还栓,但是默認(rèn)情況下,這些方法不會(huì)執(zhí)行任何操作传轰。 像 UITextField 這樣的子類會(huì)覆蓋這些方法剩盒,以便對(duì)其上下文進(jìn)行適當(dāng)?shù)牟僮鳎缂羟挟?dāng)前選擇的文本慨蛙。 這些方法都在 UIResponderStandardEditActions 協(xié)議中聲明辽聊。

如果您在視圖中覆蓋了 UIResponderStandardEditActions 的方法,則其菜單項(xiàng)將自動(dòng)顯示在您為該視圖顯示的任何菜單中期贫。 這是因?yàn)椴藛慰刂破髟谄湟晥D上調(diào)用 canPerformAction(_:withSender :) 方法跟匆,它根據(jù)視圖是否實(shí)現(xiàn)此方法返回 truefalse

如果要實(shí)現(xiàn)這些方法之一通砍,但不希望它出現(xiàn)在菜單中玛臂,可以覆蓋 canPerformAction(_:withSender :) 返回 false

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {

??if action == #selector(copy(_:)) {
????return false
??} else {
????// Else return the default behavior
????return super.canPerformAction(action, withSender: sender)
??}
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市封孙,隨后出現(xiàn)的幾起案子迹冤,更是在濱河造成了極大的恐慌,老刑警劉巖虎忌,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泡徙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡膜蠢,警方通過(guò)查閱死者的電腦和手機(jī)堪藐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)挑围,“玉大人松申,你說(shuō)我怎么就攤上這事注益≮逯荩” “怎么了班缰?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)奏瞬。 經(jīng)常有香客問(wèn)我枫绅,道長(zhǎng),這世上最難降的妖魔是什么硼端? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任并淋,我火速辦了婚禮,結(jié)果婚禮上珍昨,老公的妹妹穿的比我還像新娘县耽。我一直安慰自己句喷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布兔毙。 她就那樣靜靜地躺著唾琼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪澎剥。 梳的紋絲不亂的頭發(fā)上锡溯,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音哑姚,去河邊找鬼祭饭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛叙量,可吹牛的內(nèi)容都是我干的倡蝙。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼绞佩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼寺鸥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起品山,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胆建,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后谆奥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體眼坏,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拂玻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年酸些,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片檐蚜。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡魄懂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闯第,到底是詐尸還是另有隱情市栗,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布咳短,位于F島的核電站填帽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咙好。R本人自食惡果不足惜篡腌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望勾效。 院中可真熱鬧嘹悼,春花似錦叛甫、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至限匣,卻和暖如春抖苦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背米死。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工睛约, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哲身。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓辩涝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親勘天。 傳聞我的和親對(duì)象是個(gè)殘疾皇子怔揩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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

  • 手勢(shì)識(shí)別器是附加到視圖的對(duì)象,將低級(jí)別事件處理代碼轉(zhuǎn)換為更高級(jí)別的操作脯丝,它允許視圖以控件執(zhí)行的方式響應(yīng)操作商膊。 手勢(shì)...
    坤坤同學(xué)閱讀 4,062評(píng)論 0 9
  • 手勢(shì)識(shí)別器(Gesture Recognizer)用于識(shí)別觸摸序列并觸發(fā)響應(yīng)事件。當(dāng)手勢(shì)識(shí)別器識(shí)別到一個(gè)手勢(shì)或手勢(shì)...
    pro648閱讀 5,887評(píng)論 0 13
  • 0辉川、緣起 之所以要寫這篇文章表蝙,是因?yàn)榘l(fā)現(xiàn)在實(shí)際編程處理點(diǎn)擊事件的過(guò)程中,知道響應(yīng)鏈和探測(cè)鏈根本沒(méi)有一點(diǎn)用處乓旗。 即使...
    吳佩在天涯閱讀 43,886評(píng)論 33 127
  • block對(duì)象簡(jiǎn)介及語(yǔ)法 什么是block府蛇? block對(duì)象是一組指令,可以像調(diào)用函數(shù)指令那樣調(diào)用block對(duì)象寸齐。...
    dreamCatcher閱讀 1,167評(píng)論 0 14
  • 1.今天早上學(xué)習(xí)了時(shí)間成本 2.今天早上七點(diǎn)出門去地鐵蘇州街B口發(fā)資料(只是發(fā)資料)欲诺,有2個(gè)人主動(dòng)加我抄谐,有兩個(gè)人意...
    鵬鵬YH閱讀 224評(píng)論 1 2