IOS筆記044-通知和代理(觀察者模式和代理模式)

 

處理文本輸入框的輸入事件,單擊文本輸入框後要彈出鍵盤。網絡

彈出鍵盤有兩種實現方式:一種代理,一種通知。也就是對應的(觀察者模式和代理模式)。ide

 

一、通知

     1.一、準備工做

        每個應用程序都有一個通知中心(NSNotificationCenter)實例,專門負責協助不一樣對象之間的消息通訊。post

        任何一個對象均可以向通知中心發佈通知(NSNotification),描述本身在作什麼。動畫

        其餘感興趣的對象(Observer)能夠申請在某個特定通知發佈時(或在某個特定的對象發佈通知時)收到這個通知。atom

        通知中心(NSNotificationCenter)提供了相應的方法來幫助發佈通知
        - (void)postNotification:(NSNotification *)notification;
        發佈一個notification通知,可在notification對象中設置通知的名稱、通知發佈者、額外信息等spa

        - (void)postNotificationName:(NSString *)aName object:(id)anObject;
        發佈一個名稱爲aName的通知,anObject爲這個通知的發佈者3d

        - (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
       發佈一個名稱爲aName的通知,anObject爲這個通知的發佈者,aUserInfo爲額外信息代理

        通知中心(NSNotificationCenter)提供了方法來註冊一個監聽通知的監聽器(Observer)
        - (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
            observer:監聽器,即誰要接收這個通知
            aSelector:收到通知後,回調監聽器的這個方法,而且把通知對象當作參數傳入
            aName:通知的名稱。若是爲nil,那麼不管通知的名稱是什麼,監聽器都能收到這個通知
            anObject:通知發佈者。若是爲anObject和aName都爲nil,監聽器都收到全部的通知server

         - (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;
            name:通知的名稱
            obj:通知發佈者
            block:收到對應的通知時,會回調這個block
            queue:決定了block在哪一個操做隊列中執行,若是傳nil,默認在當前操做隊列中同步執行對象

        通知中心不會保留(retain)監聽器對象,在通知中心註冊過的對象,必須在該對象釋放前取消註冊。不然,當相應的通知再次出現時,通知中心仍然會向該監聽器發送消息。由於相應的監聽器對象已經被釋放了,因此可能會致使應用崩潰。

        通知中心提供了相應的方法來取消註冊監聽器
        - (void)removeObserver:(id)observer;
        - (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;

        實現過程:先註冊消息 postNotificationName ,而後在響應的方法的接收消息 addObserver,最後實現接收消息的方法

        由於在系統中又定義好的鍵盤處理通知,因此直接進行監聽就好了。

       UIKIT_EXTERN NSString *const UIKeyboardWillShowNotification; // 即將彈出

       UIKIT_EXTERN NSString *const UIKeyboardDidShowNotification; // 顯示

       UIKIT_EXTERN NSString *const UIKeyboardWillHideNotification;   //即將隱藏

       UIKIT_EXTERN NSString *const UIKeyboardDidHideNotification; // 隱藏       

     1.二、實現過程

//  一、經過通知監聽鍵盤出現消息

// 添加監聽器,監聽鍵盤彈出

[[NSNotificationCenter defaultCenteraddObserver:selfselector:@selector(keyboardComeout:) name:UIKeyboardWillShowNotificationobject:nil];

// 添加監聽器,監聽鍵盤退出

[[NSNotificationCenter defaultCenteraddObserver:selfselector:@selector(keyboardGoaway:) name:UIKeyboardWillHideNotificationobject:nil];

      

// 二、響應通知的方法,顯示鍵盤

- (void)keyboardComeout:(NSNotification *)note

{

    NSLog(@"keyboardComeout--\n%@",note);

    // 取出消息中鍵盤出現後的最終位置,要將字典對象轉換成CGRect

    CGRect rect = [note.userInfo[UIKeyboardFrameEndUserInfoKeyCGRectValue];

    self.viewBotom.constant = rect.size.height;

    // 取出鍵盤彈出所須要的時間

    double animateTime = [note.userInfo[UIKeyboardAnimationDurationUserInfoKeydoubleValue];

    [UIViewanimateWithDuration:animateTime animations:^{

        [self.viewlayoutIfNeeded];

    }];

    /**

     *   NSNotification 內部信息

     NSConcreteNotification 0x7f896bebf4d0 {name = UIKeyboardWillShowNotification; userInfo = {

     UIKeyboardAnimationCurveUserInfoKey = 7;

     UIKeyboardAnimationDurationUserInfoKey = "0.25";

     UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {375, 225}}";

     UIKeyboardCenterBeginUserInfoKey = "NSPoint: {187.5, 779.5}";

     UIKeyboardCenterEndUserInfoKey = "NSPoint: {187.5, 554.5}";

     UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 667}, {375, 225}}";

     UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 442}, {375, 225}}";

     }}

 

     */

}

/**

 *  三、開始滾動界面後隱藏鍵盤

 */

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

{

    //退出鍵盤

    // NSLog(@"scrollViewWillBeginDragging--");

    //[self.view endEditing:YES];//方法1

    [self.textField resignFirstResponder]; // 方法2:誰響應的鍵盤,誰就能夠調用這個方法退出鍵盤(resignFirstResponder)

}

// 隱藏鍵盤

- (void)keyboardGoaway:(NSNotification *)note

{

    NSLog(@"keyboardGoaway--\n%@",note);

    self.viewBotom.constant = 0;

     //取出鍵盤彈出所須要的時間

    double animateTime = [note.userInfo[UIKeyboardAnimationDurationUserInfoKeydoubleValue];

    [UIViewanimateWithDuration:animateTime animations:^{

        [self.viewlayoutIfNeeded];

    }];

}

        彈出效果,實際效果是有動畫的,過渡更好。

        Test

        

    1.三、鍵盤彈出後還要對真個View進行上移,隱藏後下移

       這裏使用更新控件的約束來實現view的移動

        屏幕快照 2015 06 07 15 37 51

       以下屬性:

    @property (weaknonatomicIBOutletNSLayoutConstraint *viewBotom;

        而後控制約束的constant值,就能夠實現view的移動。

        設置方法方法:

    // 取出消息中鍵盤出現後的最終位置,要將字典對象轉換成CGRect

      CGRect rect = [note.userInfo[UIKeyboardFrameEndUserInfoKeyCGRectValue];

    self.viewBotom.constant = rect.size.height;     

    1.四、給UITextField 兩邊添加一個間距

直接使用其自帶的功能便可

    // 設置文本框左邊的內容

    UIView *leftView = [[UIView allocinit];

    leftView.frame = CGRectMake(00100);

    self.messageField.leftView = leftView;

    self.messageField.leftViewMode = UITextFieldViewModeAlways;

     // 右邊間距

    self.messageField.rightView = leftView;

    self.messageField.rightViewMode = UITextFieldViewModeAlways;

    1.五、使用完畢後要手動刪除監聽器

- (void)dealloc

{

    // 使用完畢後要手動移除監聽對象

    [[NSNotificationCenter defaultCenter] removeObserver:self];

}

二、代理模式

    2.一、使用注意事項

         一、在頭文件中實現代理,定義協議
         二、使用方法:1 設置代理,2 遵照協議,3 實現方法
         三、規範:參考蘋果官方的協議實現方式
         四、做用:
                  一、監聽事件:A是監聽B的一些行爲,則A成爲B的代理
                  二、消息傳遞:A想告訴B一些事情,則B成爲A的代理
         五、總結:
                  一、若是想監聽別人的一些行爲,那麼就要成爲別人的代理
                  二、若是你想告訴別人一些事情,那麼就讓別人成爲你得代理
         六、開發步驟
                  一、擬一份協議(格式:控件名 + Delegate),在協議裏聲明一些代理方法(通常代理方法都是@optional)
                  二、聲明一個代理屬性(弱引用) @property (weak,nonatomic) id delegate;
                  三、在內部發生某些行爲時,調用代理對應的代理方法,通知代理內部發生了什麼事情
                  四、設置代理: xxx.delegate = self;
                  五、 yyy對象遵照協議,實現代理方法

    2.二、代碼實現

        先看效果

        屏幕快照 2015 06 08 15 16 58

       首先在xib裏實現界面以下:

        屏幕快照 2015 06 08 15 13 14 

        而後實現文件也比較簡單,在點擊過加載按鈕後,由此對象給代理髮送加載數據通知。

        頭文件實現裏包括代理所遵照協議的定義,包括一個可用方法。

 

@classSLQLoadData;

 

@protocol SLQLoadDataDelegate <NSObject>

@optional

 

/**

 *  代理方法:點擊按鈕操做

*/

- (void)LoadDataDidCilckButton:(SLQLoadData *)loadData;

 

@interface SLQLoadData : UIView

// 代理屬性

@property (weak,nonatomic) id<SLQLoadDataDelegate> delegate;

 

+ (instancetype)loadData;// 返回加載對象

- (void)endingloadData; // 結束加載

@end

 

#import "SLQLoadData.h"

@interfaceSLQLoadData ()

- (IBAction)loadMoreData:(id)sender;

@property (weak, nonatomic) IBOutletUIView *loadView; // 正在加載界面

@property (weak, nonatomic) IBOutletUIButton *loadBtn; // 加載按鈕

@end

 

@implementation SLQLoadData

+ (instancetype)loadData

{

    // NSStringFromClass 將類名轉換成字符串,xib文件名和類名同樣

    return [[[NSBundlemainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];

}

- (IBAction)loadMoreData:(id)sender

{

    // 模擬從網絡加載數據

    //NSLog(@「loadMoreData」);

    self.loadView.hidden = NO;

    self.loadBtn.hidden = YES;

    // 發送消息到代理對象,首先判斷代理方法是否存在

    if ([self.delegate respondsToSelector:@selector(LoadDataDidCilckButton:)])

    {

        [self.delegate  LoadDataDidCilckButton:self]; 

    }

}

// 隱藏正在加載界面

- (void)endingloadData

{

    self.loadView.hidden = YES;

    self.loadBtn.hidden = NO;

}

@end

    2.三、控制器中對代理方法的使用

         一、包含頭文件並遵照協議

#import "SLQLoadData.h"

 

@interfaceViewController () <SLQLoadDataDelegate> // 遵照代理協議

        二、指定代理對象

    SLQLoadData *loadData = [SLQLoadData loadData];

    // 指定控制器爲代理對象

    loadData.delegate = self;

    self.tableView.tableFooterView = loadData;

       三、實現代理方法

// 實現代理方法

- (void)LoadDataDidCilckButton:(SLQLoadData *)loadData

{

    NSLog(@"LoadDataDidCilckButton");

    // 模擬從網絡加載數據,加一個定時器

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        

        SLQFengNews *news = [[SLQFengNews alloc] init];

        news.title = @"高考往事";

        news.topic = NO;

        news.image = @"ad_02";

        news.comment = @"13489";

        // 添加至模型對象

        [self.news addObject:news];

        // 刷新表格

        [self.tableView reloadData];

        

        // 隱藏loadView

        SLQLoadData *load = (SLQLoadData *)self.tableView.tableFooterView;

        [load endingloadData];  

    });

 

}

    2.四、最終效果 -_-

       LoadData 

 總結

       其餘系統自帶通知

        UIDevice類提供了一個單例對象,它表明着設備,經過它能夠得到一些設備相關的信息,好比電池電量值(batteryLevel)、電池狀態(batteryState)、設備的類型(model,好比iPod、iPhone等)、設備的系統(systemVersion)

        經過[UIDevice currentDevice]能夠獲取這個單例對象

       UIDevice對象會不間斷地發佈一些通知,下列是UIDevice對象所發佈通知的名稱常量:
       UIDeviceOrientationDidChangeNotification // 設備旋轉
       UIDeviceBatteryStateDidChangeNotification // 電池狀態改變
       UIDeviceBatteryLevelDidChangeNotification // 電池電量改變
       UIDeviceProximityStateDidChangeNotification // 近距離傳感器(好比設備貼近了使用者的臉部)

       鍵盤通知 

       咱們常常須要在鍵盤彈出或者隱藏的時候作一些特定的操做,所以須要監聽鍵盤的狀態

       鍵盤狀態改變的時候,系統會發出一些特定的通知
       UIKeyboardWillShowNotification // 鍵盤即將顯示
       UIKeyboardDidShowNotification // 鍵盤顯示完畢
       UIKeyboardWillHideNotification // 鍵盤即將隱藏
       UIKeyboardDidHideNotification // 鍵盤隱藏完畢
       UIKeyboardWillChangeFrameNotification // 鍵盤的位置尺寸即將發生改變
       UIKeyboardDidChangeFrameNotification // 鍵盤的位置尺寸改變完畢

       系統發出鍵盤通知時,會附帶一下跟鍵盤有關的額外信息(字典),字典常見的key以下:
       UIKeyboardFrameBeginUserInfoKey // 鍵盤剛開始的frame
       UIKeyboardFrameEndUserInfoKey // 鍵盤最終的frame(動畫執行完畢後)
       UIKeyboardAnimationDurationUserInfoKey // 鍵盤動畫的時間
       UIKeyboardAnimationCurveUserInfoKey // 鍵盤動畫的執行節奏(快慢)

 

       代理和通知比較

       共同點

              利用通知和代理都能完成對象之間的通訊(好比A對象告訴D對象發生了什麼事情, A對象傳遞數據給D對象)

       不一樣點

              代理 : 1個對象只能告訴另1個對象發生了什麼事情

              通知 : 1個對象能告訴N個對象發生了什麼事情, 1個對象能得知N個對象發生了什麼事情

相關文章
相關標籤/搜索