你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> (iOS)響應事件傳遞, nextResponder研究

(iOS)響應事件傳遞, nextResponder研究

編輯:IOS開發綜合
響應事件傳遞, nextResponder研究

這裡,我們考慮以下二種情況。

 

問題1。 如何調用父view的controller裡面的方法?

答案如下:
[[self superview ].nextResponder method];
[[[self superview ] nextResponder] method];
[self.nextResponder method];
上面的都可以,看情況使用,使用的時候最好判斷一下。

官方解釋
UIView implements this method by returning the UIViewController object that manages it (if it has one) or its superview (if it doesn’t); UIViewController implements the method by returning its view’s superview; UIWindow returns the application object, and UIApplication returns nil.

即如下代碼可以進行判斷:

id next = [self nextResponder];
while(![next isKindOfClass:[ViewController class]])//這裡跳不出來。。。有人說這裡跳不出來,其實是因為它沒有當前這個view放入ViewController中,自然也就跳不出來了,會死循環,使用時需要注意。
{
next = [next nextResponder];
}
if ([next isKindOfClass:[ViewController class]])
{
controller = (ViewController *)next;
}


問題2:當一個子view需要接收點擊事件,而父view也需要接收點擊事件, 如何做?

當然, 你可能會說直接調用mysubview.superView即可, 這樣做也確實是可以做到,但有時子view是不一定知道有這個特定的父view的存在的,如動態添加子view。

所以這裡就可以用到消息響應鏈拉技術。

下面要做的也就是,讓子view接收這些事件後,同時把這些事件繼續向上傳,會一直傳到UIApplication為止。 而在傳的過程中,如果子view接收了這些事件,那麼事件會自然終止,我們現在可以做的是同時讓子view接收事件,而且還讓事件不終止,並繼續向上傳。

摘取一部分說明:

當用戶 與 iPhone的觸摸屏 產生 互動時,硬件 就會探測到 物理接觸 並且 通知 操作系統。接著 操作系統 就會創建 相應的事件 並且 將 其 傳遞給 當前正在運行的應用程序的事件隊列。然後 這項事件 會被事件循環 傳遞給 優先響應者物件。優先響應者物件 是 事件 被觸發時 和 用戶 交互的物件,比如 按鈕物件、視圖物件。如果 我們 編寫了 代碼 讓 優先響應者 處理 這種類型的事件,那麼 它 就會處理 這種類型的事件。處理完 某項事件後,響應者 有 兩個選項:1、將 其 丟棄;2、將 其 傳遞給 響應鏈條中的下一個響應者。下一個響應者的地址 存儲 在當前響應者物件所包含的變量nextResponder當中。如果 優先響應者 無法處理 一項事件,那麼 這項事件 就傳遞給 下一個響應者,直到 這項事件 到達 能處理它的響應者 或者 到達 響應鏈條的末端,也就是 UIApplication類型的物件。UIApplication類型的物件 收到 一項事件後,也是 要麼 處理,要麼 丟棄。

比如 有 一個視圖物件,這個視圖物件上 有 一個按鈕物件。當用戶 觸摸 這個按鈕物件時,作為優先響應者,這個按鈕物件 就會收到 一項事件。如果 這個按鈕物件 無法處理 這項事件,就會將 這項事件 傳遞給 視圖物件。如果 視圖物件 無法處理 這項事件,就會將 這項事件 傳遞給 視圖控制器物件。以此類推。

應該注意的 是 當我們 在使用 響應鏈條時,一項事件 並不會自動地 從一個響應者 傳遞到 下一個響應者。如果 要將 一項事件 從一個響應者 傳遞到 下一個響應者,我們 必須編寫 代碼 才能辦到。

要做的如下:

view的代碼如下:

 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

// 這裡可以做子view自己想做的事,做完後,事件繼續上傳,就可以讓其父類,甚至父viewcontroller獲取到這個事件了

[[selfnextResponder]touchesBegan:toucheswithEvent:event];

}

 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

{

[[selfnextResponder]touchesEnded:toucheswithEvent:event];

}

 

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

{

[[selfnextResponder] touchesCancelled:toucheswithEvent:event];

}

 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

{

[[selfnextResponder] touchesMoved:toucheswithEvent:event];

}

另外需要注意的是:在重寫這幾個方法時,最好保證這幾個方法都重寫,否則事件響應鏈可能會變混亂。這是我的猜測哈,沒有實際驗證過。

一.responder對象

在iOS系統中,能夠響應並處理事件的對象稱之為responder object, UIResponder是所有responder對象的基類,在UIResponder類中定義了處理各種事件,包括觸摸事件(Touch Event)、運動事件(Motion Event)和遠程控制事件(Remote-Control Events)的編程接口,其中處理觸摸事件(Touch Event)的編程接口如下:
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
這四個方法分別處理觸摸開始事件,觸摸移動事件,觸摸終止事件,以及觸摸跟蹤取消事件。

UIApplication, UIViewController,UIView和所有繼承自UIView的UIKit類(包括UIWindow,繼承自UIView)都直接或間接的繼承自UIResponder,所以它們的實例都是responder object對象,都實現了上述4個方法。UIResponder中的默認實現是什麼都不做,但UIKit中UIResponder的直接子類(UIView,UIViewController…)的默認實現是將事件沿著responder chain繼續向上傳遞到下一個responder,即nextResponder。所以在定制UIView子類的上述事件處理方法時,如果需要將事件傳遞給next responder,可以直接調用super的對應事件處理方法,super的對應方法將事件傳遞給next responder,即使用

[super touchesBegan:touches withEvent:event];

不建議直接向nextResponder發送消息,這樣可能會漏掉父類對這一事件的其他處理。

[self.nextResponder  touchesBegan:touches withEvent:event];

 

另外,在定制UIView子類的事件處理方法時,如果其中一個方法沒有調用super的對應方法,則其他方法也需要重寫,不使用super的方法,否則事件處理流程會很混亂。

注:UIKit框架的類層次結構圖見:UIKit Framework Reference

二.responder chain

上文提到了responder chain,responder chain是一系列連接的responder對象,通過responder對象可以將處理事件的責任傳遞給下一個,更高級的對象,即當前responder對象的nextResponder。
iOS中responder chain的結構為:
\

  • UIView的nextResponder屬性,如果有管理此view的UIViewController對象,則為此UIViewController對象;否則nextResponder即為其superview。
  • UIViewController的nextResponder屬性為其管理view的superview.
  • UIWindow的nextResponder屬性為UIApplication對象。
  • UIApplication的nextResponder屬性為nil。

    iOS系統在處理事件時,通過UIApplication對象和每個UIWindow對象的sendEvent:方法將事件分發給具體處理此事件的responder對象(對於觸摸事件為hit-test view,其他事件為first responder),當具體處理此事件的responder不處理此事件時,可以通過responder chain交給上一級處理。

    1. 如果hit-test view或first responder不處理此事件,則將事件傳遞給其nextResponder處理,若有UIViewController對象則傳遞給UIViewController,傳遞給其superView。
    2. 如果view的viewController也不處理事件,則viewController將事件傳遞給其管理view的superView。
    3. 視圖層級結構的頂級為UIWindow對象,如果window仍不處理此事件,傳遞給UIApplication.
    4. 若UIApplication對象不處理此事件,則事件被丟棄。

      三.巧妙利用nextResponder

      通過UIViewController的view屬性可以訪問到其管理的view對象,及此view的所有subviews。但是根據一個view對象,沒有直接的方法可以得到管理它的viewController,但我們使用responder chain可以間接的得到,代碼如下:

      @implementation UIView (ParentController)
      -(UIViewController*)parentController{
          UIResponder *responder = [self nextResponder];
          while (responder) {
      	if ([responder isKindOfClass:[UIViewController class]]) {
      		return (UIViewController*)responder;
      	}
      	responder = [responder nextResponder];
          }
          return nil;
      }
      @end
      
      
      
      
      
      
      
      

      iOS事件機制(一)

      DEC 7TH, 2013

      運用的前提是掌握 掌握的本質是理解

      \

      本篇內容將圍繞iOS中事件及其傳遞機制進行學習和分析。在iOS中,事件分為三類:

      • 觸控事件(單點、多點觸控以及各種手勢操作)
      • 傳感器事件(重力、加速度傳感器等)
      • 遠程控制事件(遠程遙控iOS設備多媒體播放等)

        這三類事件共同構成了iOS設備豐富的操作方式和使用體驗,本次就首先來針對第一類事件:觸控事件,進行學習和分析。

        Gesture Recognizers

        Gesture Recognizers是一類手勢識別器對象,它可以附屬在你指定的View上,並且為其設定指定的手勢操作,例如是點擊、滑動或者是拖拽。當觸控事件 發生時,設置了Gesture Recognizers的View會先通過識別器去攔截觸控事件,如果該觸控事件是事先為View設定的觸控監聽事件,那麼Gesture Recognizers將會發送動作消息給目標處理對象,目標處理對象則對這次觸控事件進行處理,先看看如下流程圖。

        \

        在iOS中,View就是我們在屏幕上看到的各種UI控件,當一個觸控事件發生時,Gesture Recognizers會先獲取到指定的事件,然後發送動作消息(action message)給目標對象(target),目標對象就是ViewController,在ViewController中通過事件方法完成對該事件的處理。Gesture Recognizers能設置諸如單擊、滑動、拖拽等事件,通過Action-Target這種設計模式,好處是能動態為View添加各種事件監聽,而不用去實現一個View的子類去完成這些功能。

        以上過程就是我們在開發中在方法中常見的設置action和設置target,例如為UIButton設置監聽事件等。

        常用手勢識別類

        在UIKit框架中,系統為我們事先定義好了一些常用的手勢識別器,包括點擊、雙指縮放、拖拽、滑動、旋轉以及長按。通過這些手勢識別器我們可以構造豐富的操作方式。

        \

        在上表中可以看到,UIKit框架中已經提供了諸如UITapGestureRecognizer在內的六種手勢識別器,如果你需要實現自定義的手勢識別器,也可以通過繼承UIGestureRecognizer類並重寫其中的方法來完成,這裡我們就不詳細討論了。

        每一個Gesture Recognizer關聯一個View,但是一個View可以關聯多個Gesture Recognizer,因為一個View可能還能響應多種觸控操作方式。當一個觸控事件發生時,Gesture Recognizer接收一個動作消息要先於View本身,結果就是Gesture Recognizer作為View處理觸控事件的代表,或者叫代理。當Gesture Recognizer接收到指定的事件時,它就會發送一條動作消息(action message)給ViewController並處理。

        連續和不連續動作

        \

        觸控動作同時分為連續動作(continuous)和不連續動作(discrete),連續動作例如滑動和拖拽,它會持續一小段時間,而不連續動作例如單擊,它瞬間就會完成,在這兩類事件的處理上又稍有不同。對於不連續動作,Gesture Recognizer只會給ViewContoller發送一個單一的動作消息(action message),而對於連續動作,Gesture Recognizer會發送多條動作消息給ViewController,直到所有的事件都結束。

        為一個View添加GestureRecognizer有兩種方式,一種是通過InterfaceBuilder實現,另一種就是通過代碼實現,我們看看通過代碼來如何實現。

        MyViewContoller.m
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        
        - (void)viewDidLoad {
             [super viewDidLoad];
        
             // 創建並初始化手勢對象
             UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
                  initWithTarget:self action:@selector(respondToTapGesture:)];
        
             // 指定操作為單擊一次
             tapRecognizer.numberOfTapsRequired = 1;
        
             // 為當前View添加GestureRecognizer
             [self.view addGestureRecognizer:tapRecognizer];
        
             // ...
        }
        

        通過上述代碼,我們實現了為當前MyViewController的View添加一個單擊事件,首先構造了UITapGestureRecognizer對象,指定了target為當前ViewController本身,action就是後面自己實現的處理方法,這裡就呼應了前文提到的Action-Target模式。

        在事件處理過程中,這兩種方式所處的狀態又各有不同,首先,所有的觸控事件最開始都是處於可用狀態(Possible),對應UIKit裡面的UIGestureRecognizerStatePossible類,如果是不連續動作事件,則狀態只會從Possible轉變為已識別狀態(Recognized,UIGestureRecognizerStateRecognized)或者是失敗狀態(Failed,UIGestureRecognizerStateFailed)。例如一次成功的單擊動作,就對應了Possible-Recognized這個過程。

        \

        如果是連續動作事件,如果事件沒有失敗並且連續動作的第一個動作被成功識別(Recognized),則從Possible狀態轉移到Began(UIGestureRecognizerStateBegan)狀態,這裡表示連續動作的開始,接著會轉變為Changed(UIGestureRecognizerStateChanged)狀態,在這個狀態下會不斷循環的處理連續動作,直到動作執行完成變轉變為Recognized已識別狀態,最終該動作會處於完成狀態(UIGestureRecognizerStateEnded),另外,連續動作事件的處理狀態會從Changed狀態轉變為Canceled(UIGestureRecognizerStateCancelled)狀態,原因是識別器認為當前的動作已經不匹配當初對事件的設定了。每個動作狀態的變化,Gesture Recognizer都會發送消息(action message)給Target,也就是ViewController,它可以根據這些動作消息進行相應的處理。例如一次成功的滑動手勢動作就包括按下、移動、抬起的過程,分別對應了Possible-Began-Changed-Recognized這個過程。

        UITouch & UIEvent

        在屏幕上的每一次動作事件都是一次Touch,在iOS中用UITouch對象表示每一次的觸控,多個Touch組成一次Event,用UIEvent來表示一次事件對象。

        \

        在上述過程中,完成了一次雙指縮放的事件動作,每一次手指狀態的變化都對應事件動作處理過程中得一個階段。通過Began-Moved-Ended這幾個階段的動作(Touch)共同構成了一次事件(Event)。在事件響應對象UIResponder中有對應的方法來分別處理這幾個階段的事件。

        • touchesBegan:withEvent:
        • touchesMoved:withEvent:
        • touchesEnded:withEvent:
        • touchesCancelled:withEvent:

          後面的參數分別對應UITouchPhaseBegan、UITouchPhaseMoved、UITouchPhaseEnded、UITouchPhaseCancelled這幾個類。用來表示不同階段的狀態。

          事件傳遞

          \

          如上圖,iOS中事件傳遞首先從App(UIApplication)開始,接著傳遞到Window(UIWindow),在接著往下傳遞到View之前,Window會將事件交給GestureRecognizer,如果在此期間,GestureRecognizer識別了傳遞過來的事件,則該事件將不會繼續傳遞到View去,而是像我們之前說的那樣交給Target(ViewController)進行處理。

          響應者鏈(Responder Chain)

          通常,一個iOS應用中,在一塊屏幕上通常有很多的UI控件,也就是有很多的View,那麼當一個事件發生時,如何來確定是哪個View響應了這個事件呢,接下來我們就一起來看看。

          響應者對象(Responsder Object)

          響應者對象是能夠響應並且處理事件的對象,UIResponder是所有響應者對象的父類,包括UIApplication、UIView和UIViewController都是UIResponder的子類。也就意味著所有的View和ViewController都是響應者對象。

          第一響應者(First Responder)

          第一響應者是第一個接收事件的View對象,我們在Xcode的Interface Builder畫視圖時,可以看到視圖結構中就有First Responder。

          \

          這裡的First Responder就是UIApplication了。另外,我們可以控制一個View讓其成為First Responder,通過實現 canBecomeFirstResponder方法並返回YES可以使當前View成為第一響應者,或者調用View的becomeFirstResponder方法也可以,例如當UITextField調用該方法時會彈出鍵盤進行輸入,此時輸入框控件就是第一響應者。

          事件傳遞機制

          如上所說,,如果hit-test view不能處理當前事件,那麼事件將會沿著響應者鏈(Responder Chain)進行傳遞,知道遇到能處理該事件的響應者(Responsder Object)。通過下圖,我們來看看兩種不同情況下得事件傳遞機制。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved