解析iOS開發中的FirstResponder第一響應對象_IOS

來源:互聯網
上載者:User

1. UIResonder

對於C#裡所有的控制項(例如TextBox),都繼承於Control類。而Control類的繼承關係如
下:

複製代碼 代碼如下:

System.Object

  System.MarshalByRefObject

    System.ComponentModel.Component

      System.Windows.Forms.Control


對於iOS裡的UI類,也有類似的繼承關係。

例如對於UITextField,繼承於UIControl;UIControl繼承於UIView,UIView繼承於UIRe
sponder,UIResponder繼承於NSObject。

具體架構可以參見:

http://developer.apple.com/library/ios/#documentation/general/conceptual/Devp
edia-CocoaApp/Responder.html

UIResponder是UIKit架構中的類(Mac OS X Cocoa對應的是AppKit架構)。

 

2. 第一響應對象

在應用的響應對象裡,會有一個成為第一響應對象。

第一響應對象和其他響應對象之間有什麼區別?對於普通的觸摸事件沒什麼區別。就算
我把一個按鈕設定成第一響應對象,當我點擊其他按鈕時,還是會響應其他按鈕,而不
會優先響應第一響應對象。

第一響應對象的區別在於負責處理那些和螢幕位置無關的事件,例如搖動。

蘋果官方文檔的說法是:第一響應對象是視窗中,應用程式認為最適合處理事件的對象

一個班只能有一個班長,應用的響應對象中,只能有一個響應對象成為第一響應對象。

 

3. 成為與取消第一響應對象。

要當第一響應對象,還需要有View來毛遂自薦:

複製代碼 代碼如下:

- (BOOL) canBecomeFirstResponder
{
    returnYES;
}

如果缺少了這段,就算用[view becomeFirstResponder]也不能讓一個view成為第一響應
對象。。。強扭的瓜不甜?好吧不是這個原因。大多數視圖預設只關心與自己有關聯的
事件,並且(幾乎)總是有機會來處理這些事件。以UIButton為例,當使用者單擊某個UIB
utton對象時,無論當前的第一響應對象是哪個視圖,該對象都會收到指定的動作訊息。
當上第一響應對象吃力不討好麼。。。所以只能由某個UIResponder明確表示自己願意成
為第一響應對象才行。(我不知道設計上是基於什麼考慮。。。安全?)

 

在當上第一響應對象時,不同對象可能會有一些特殊的表現。例如UITextField當上的時
候,就會調出一塊小鍵盤。

第一響應對象也有可能被辭退。發送一個resignFirstResponder,就可以勸退。

 

4. 第一響應對象的任務

剛才說了第一響應對象可以處理搖動。就來看個範例吧:

 

複製代碼 代碼如下:

- (void) motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if(motion == UIEventSubtypeMotionShake)
    {
        NSLog(@"Device is beginning to shake");
        [selfsetCircleColor:[UIColorredColor]];
        [selfsetNeedsDisplay];
    }
}

當搖動開始時觸發某些行為。

 

5. 擷取當前第一響應對象

源自這篇討論:http://stackoverflow.com/questions/1823317/get-the-current-firs
t-responder-without-using-a-private-api

提問的傢伙用了如下的方式來擷取

複製代碼 代碼如下:

UIView   *firstResponder = [keyWindow
performSelector:@selector(firstResponder)];

結果被蘋果打回來,說用了非公開的API。。。

於是這傢伙只好苦逼地用遞迴了:

複製代碼 代碼如下:

implementationUIView (FindFirstResponder)
- (UIView *)findFirstResponder
{
    if (self.isFirstResponder) {       
        return self;    
    }
    for (UIView *subView in self.subviews) {
        UIView *firstResponder = [subView findFirstResponder];
        if (firstResponder != nil) {
            return firstResponder;
        }
    }
    return nil;
}
@end

6.View的FirstResponder的釋放問題
今天遇到一個問題,當我隱藏掉一個正在接受使用者輸入的UITextField的時候,鍵盤並不會消失,而且鍵盤仍然接受使用者輸入,再次顯示該TextField時候發現在隱藏狀態下,所有的輸入仍然傳輸到了該TextField中,於是查下官方資料找到如下解釋:
 
Important If you hide a view that is currently the first responder, the view does not automatically resign its first responder status. Events targeted at the first responder are still delivered to the hidden view. To prevent this from happening, you should force your view to resign the first responder status when you hide it.   
 
  意思是如果這個View是當前的第一響應者的時候,隱藏該View並不會自動放棄其第一響應者的身份,而且會繼續以第一響應者的身份接受訊息。我們可以通過在隱藏View之前,手動調用resignFirstResponder來強制該view放棄第一響應者身份。
 
  下面請看小例子:

複製代碼 代碼如下:

SvTestFirstResponder.h

//
//  SvTestFirstResponder.h
//
//  Created by maple on 3/15/12.
//  Copyright (c) 2012 SmileEvday. All rights reserved.
//
//  當一個view時當前響應者時,調用其hidden方法並不會自動放棄第一響應者身份,所有的訊息仍然會發送到這個view
//  可以通過在hidden前強制放棄第一響應者,恢複正常的訊息傳遞
//

#import <UIKit/UIKit.h>

@interface SvTestFirstResponder : UIView {
    UITextField *_inputField;
}

@end


複製代碼 代碼如下:

SvTestFirstResponder.m

//
//  SvTestFirstResponder.m
//
//  Created by maple on 3/15/12.
//  Copyright (c) 2012 SmileEvday. All rights reserved.
//

#import "SvTestFirstResponder.h"

@interface SvTestFirstResponder()

- (void)hiddenInputView:(id)sender;
- (void)showInputView:(id)sender;

@end

@implementation SvTestFirstResponder

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
       
        _inputField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 50)];
        _inputField.center = CGPointMake(160, 50);
        [_inputField setFont:[UIFont systemFontOfSize:24]];
        _inputField.text = @"input you text";
        _inputField.clearsOnBeginEditing = YES;
        _inputField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
        _inputField.borderStyle = UITextBorderStyleRoundedRect;
        [self addSubview:_inputField];
        _inputField.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
       
        UIButton *hiddenBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        hiddenBtn.frame = CGRectMake(0, 0, 115, 40);
        hiddenBtn.center = CGPointMake(80, 110);
        [hiddenBtn setTitle:@"Hide TextField" forState:UIControlStateNormal];
        [hiddenBtn addTarget:self action:@selector(hiddenInputView:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:hiddenBtn];
        hiddenBtn.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
        hiddenBtn.titleLabel.lineBreakMode = UILineBreakModeTailTruncation;
       
        UIButton *showBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        showBtn.frame = CGRectMake(0, 0, 115, 40);
        showBtn.center = CGPointMake(240, 110);
        [showBtn setTitle:@"Show TextField" forState:UIControlStateNormal];
        [showBtn addTarget:self action:@selector(showInputView:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:showBtn];
        showBtn.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
        showBtn.titleLabel.lineBreakMode = UILineBreakModeTailTruncation;
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

- (void)hiddenInputView:(id)sender
{
    _inputField.hidden = YES;
}

- (void)showInputView:(id)sender
{
    _inputField.hidden = NO;
}

@end

這個簡單的例子中,當輸入框進入接受使用者輸入狀態的時候,點擊hide按鈕,鍵盤並不會消失而且會繼續接收使用者輸入並且將使用者輸入傳到TextField中去,後面再點擊Show按鈕的時候你會發現所有在隱藏狀態下輸入的文字都已經成功的被接收。我們可以修改hide方法如下:
 

複製代碼 代碼如下:

- (void)hiddenInputView:(id)sender

{

    if (_inputField.isFirstResponder) {

        [_inputField resignFirstResponder];

    }

    _inputField.hidden = YES;

}

  這樣就可以在隱藏之前強制釋放第一響應者身份,這個問題比較細節,但有時候可能就是這種細節問題導致一些莫名奇妙的問題,在隱藏一些可能成為第一響應者的view之前添加強制釋放第一響應者身份,可能會幫我們避免一些奇怪的問題,而且也幾乎不會有什麼開銷,何樂而不為呢。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.