美文网首页
知识点总结16:如何监听一个控件内部的事件的四种方法

知识点总结16:如何监听一个控件内部的事件的四种方法

作者: 枫之叶_小乙哥 | 来源:发表于2017-02-07 22:34 被阅读113次

    如何监听一个控件内部的事件: 代理(含addTarget), 通知, 内部的某些机制

    • 1.addTarget代理(需要继承UIControl)
    #import "ZGKTextField.h"
    #import <objc/runtime.h>
    
    static NSString *const ZGKPlaceholderColor = @"placeholderLabel.textColor";
    
    @implementation ZGKTextField
    // 从xib中加载
    - (void)awakeFromNib{
        /**** ******************************************************************** ****/
        /*
         UIControlEventEditingDidBegin                                   // UITextField
         UIControlEventEditingChanged
         UIControlEventEditingDidEnd
         UIControlEventEditingDidEndOnExit
         */
        /**** 占位文字的监听 ****/
        // addTarget可以多次添加,但是代理只能有一个,因为一个是add方法,一个set方法
        [self addTarget:self action:@selector(EditingDidBegin:) forControlEvents:UIControlEventEditingDidBegin];
        [self addTarget:self action:@selector(EditingDidEnd:) forControlEvents:UIControlEventEditingDidEnd];
        // 输入文字改变的时候监听
        [self addTarget:self action:@selector(EditingChanged:) forControlEvents:UIControlEventEditingChanged];
        [self addTarget:self action:@selector(DidEndOnExit:) forControlEvents:UIControlEventEditingDidEndOnExit];
    }
    
    - (void)EditingDidBegin:(UITextField*)textField{
        NSLog(@"textField = %@", textField);
        [self setValue:[UIColor redColor] forKeyPath:ZGKPlaceholderColor];
        ZGKLogFunc
    }
    
    - (void)EditingDidEnd:(UITextField*)textField{
        // placeholderLabel是私有属性,所以不能用下面的方法
    //    [self.placeholderLabel setValue:[UIColor whiteColor] forKey:@"textColor"];
        [self setValue:[UIColor yellowColor] forKeyPath:ZGKPlaceholderColor];
        ZGKLogFunc
    }
    
    
    - (void)EditingChanged:(UITextField*)textField{
        ZGKLogFunc
    }
    
    - (void)DidEndOnExit:(UITextField*)textField{
        ZGKLogFunc
    }
    
    • 2.代理方法(需要遵守协议)
    // 从xib中加载
    - (void)awakeFromNib{
      /**** 方法二: 占位文字的监听 ****/
        // 重点1: 一般不设置自己设置自己为代理,例如控制器把该控件重新设置了代理,self.textField.delegate = self,则控件内部设置自己为代理就作废了,因为self.delegate是set方法
        self.delegate = self;
    }
    
    
    #pragma mark - textFieldDelegate
    // 重点二: 返回值是布尔类型的,一般是"询问"代理是否可以执行
    // 是否允许文本框编辑
    - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
        return YES;
    }
    // 是否允许文本框结束编辑
    - (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
        return YES;
    }
    
    
    - (void)textFieldDidBeginEditing:(UITextField *)textField{
        ZGKLogFunc
    }
    
    - (void)textFieldDidEndEditing:(UITextField *)textField{
        ZGKLogFunc
    }
    
    - (void)textFieldDidEndEditing:(UITextField *)textField reason:(UITextFieldDidEndEditingReason)reason{
        ZGKLogFunc
        NSLog(@"reason == %ld", reason);
    }
    
    
    • 3.通知
    // 从xib中加载
    - (void)awakeFromNib{
     /**** 方法三: 占位文字的监听 ****/
        // 通知:textField本来就有通知用于监听textField\
        // 需要注意的点:1.因为该通知textField本来就有,所以直接添加观察者就可以,就像addTarget和代理一样,textField本来就存在相应的
        // 2.在添加通知观察者的时候,object为self,强调了只有自己发的通知才会被执行,如果没有强调object:self,则(账号textField)会接收其他控件(密码textField)发出的名字相同的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beginEditingNotification:) name:UITextFieldTextDidBeginEditingNotification object:self];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endEditingNotification:) name:UITextFieldTextDidEndEditingNotification object:self];
    
    }
    
    #pragma mark - 通知
    - (void)beginEditingNotification:(NSNotification *)noti{
        NSLog(@"%@----%@--%s",[noti.object placeholder], self.placeholder, __func__);
        [self setValue:[UIColor whiteColor] forKeyPath:ZGKPlaceholderColor];
    }
    
    - (void)endEditingNotification:(NSNotification *)noti{
        NSLog(@"%@----%@--%s",[noti.object placeholder], self.placeholder, __func__);
        [self setValue:[UIColor blueColor] forKeyPath:ZGKPlaceholderColor];
    }
    
    - (void)dealloc{
        // 移除特定的通知
    //    [NSNotificationCenter defaultCenter] removeObserver:<#(nonnull id)#> name:<#(nullable NSNotificationName)#> object:<#(nullable id)#>;
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    

    通知的补充:一次性通知和通知在不同线程的执行

    // 从xib中加载
    - (void)awakeFromNib{
        /**** 方法三: 占位文字的监听 ****/
        // 通知:textField本来就有通知用于监听textField\
        // 需要注意的点:1.因为该通知textField本来就有,所以直接添加观察者就可以,就像addTarget和代理一样,textField本来就存在相应的
        // 2.在添加通知观察者的时候,object为self,强调了只有自己发的通知才会被执行,如果没有强调object:self,则(账号textField)会接收其他控件(密码textField)发出的名字相同的通知
        // 3.通知执行方法的线程,取决于添加观察者再哪个线程添加,但是经过检验就算是在子线程中添加通知观察者,执行的方法也是在主线程中执行(非block方式的通知,方法只能在主线程中执行)
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"注册通知执行的线程是:%@", [NSThread currentThread]);
    
    //        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beginEditingNotification:) name:UITextFieldTextDidBeginEditingNotification object:self];
    //        
    //        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endEditingNotification:) name:UITextFieldTextDidEndEditingNotification object:self];
        });
     
        // block方式的通知,经过检验,可以控制执行的方法在哪个子线程中执行,因为queue:[NSOperationQueue mainQueue]
        // 移除block方式的通知,要用属性强引用后,移除
    //    self.observer = [[NSNotificationCenter defaultCenter] addObserverForName:UITextFieldTextDidBeginEditingNotification object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
    //        /**** 主线程中执行方法 ****/
    //        NSLog(@"%@------%s",self.placeholder, __func__);
    //        NSLog(@"block执行BeginEditing通知的线程是:%@", [NSThread currentThread]);
    //        [self setValue:[UIColor whiteColor] forKeyPath:ZGKPlaceholderColor];
    //        
    //        // 一次性通知:(用属性)
    //        [[NSNotificationCenter defaultCenter] removeObserver:self.observer];
    //    }];
    
        // 最简单的一次性通知(不用属性则,在添加完观察者后,方法执行完毕就会释放对象,因此,不能在block内移除观察者),在通知执行方法中移除通知通过属性移除是比较好的
    //    id myObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UITextFieldTextDidEndEditingNotification object:self queue:[[NSOperationQueue alloc]  init] usingBlock:^(NSNotification * _Nonnull note) {
    //        NSLog(@"myObserver - %@", myObserver);
    //
    //        /**** 子线程中执行方法 ****/
    //        NSLog(@"%@------%s",self.placeholder, __func__);
    //        NSLog(@"block执行EndEditing通知的线程是:%@", [NSThread currentThread]);
    //        [self setValue:[UIColor whiteColor] forKeyPath:ZGKPlaceholderColor];
    //        
    //        // 该方法不能移除观察者,要用self.myObserver,需通过属性移除,此时myObserver为null
    //        [[NSNotificationCenter defaultCenter] removeObserver:myObserver];
    //    }];
    //
        
        self.myObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UITextFieldTextDidEndEditingNotification object:self queue:[[NSOperationQueue alloc]  init] usingBlock:^(NSNotification * _Nonnull note) {
            NSLog(@"myObserver - %@", self.myObserver);
            
            /**** 子线程中执行方法 ****/
            NSLog(@"%@------%s",self.placeholder, __func__);
            NSLog(@"block执行EndEditing通知的线程是:%@", [NSThread currentThread]);
            [self setValue:[UIColor whiteColor] forKeyPath:ZGKPlaceholderColor];
            
            // 该方法可以移除观察者
            [[NSNotificationCenter defaultCenter] removeObserver:self.myObserver];
        }];
    }
    
    #pragma mark - 通知
    - (void)beginEditingNotification:(NSNotification *)noti{
        NSLog(@"%@----%@--%s",[noti.object placeholder], self.placeholder, __func__);
        NSLog(@"执行通知的线程是:%@", [NSThread currentThread]);
        [self setValue:[UIColor whiteColor] forKeyPath:ZGKPlaceholderColor];
    }
    
    - (void)endEditingNotification:(NSNotification *)noti{
        NSLog(@"%@----%@--%s",[noti.object placeholder], self.placeholder, __func__);
        NSLog(@"执行通知的线程是:%@", [NSThread currentThread]);
        [self setValue:[UIColor blueColor] forKeyPath:ZGKPlaceholderColor];
    }
    
    - (void)dealloc{
        // 移除特定的通知
    //    [NSNotificationCenter defaultCenter] removeObserver:<#(nonnull id)#> name:<#(nullable NSNotificationName)#> object:<#(nullable id)#>;
        // 移除所有通知
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        
        // 移除block方式的通知
        [[NSNotificationCenter defaultCenter] removeObserver:self.observer];
    }
    
    • 4.内部某些机制
      比如重写UITextField的becomeFirstResponderresignFirstResponder来监听UITextField的获得焦点和失去焦点事件
        /**** ******************************************************************** ****/
        // 我们可以通过利用控件的内部机制(知道控件在某些时刻会调用某些方法,然后重写它),如:
        // 调用时刻: 进入编辑\弹出键盘\获得焦点
        // [self becomeFirstResponder];
        
        // 调用时刻: 退出编辑\退出键盘\失去焦点
        // [self resignFirstResponder];
        /**** ******************************************************************** ****/
    
    #pragma mark - 某些机制
    // 重写方法做事情
    // 调用时刻: 进入编辑\弹出键盘\获得焦点
    
    - (BOOL)becomeFirstResponder{
        [self setValue:[UIColor whiteColor] forKeyPath:ZGKPlaceholderColor];
        return [super becomeFirstResponder];
    }
    
    // 调用时刻: 退出编辑\退出键盘\失去焦点
    
    - (BOOL)resignFirstResponder{
        [self setValue:[UIColor grayColor] forKeyPath:ZGKPlaceholderColor];
        return [super resignFirstResponder];
    }
    
    

    通知相关的补充

    使用block监听通知

    // object对象发出了名字为name的通知, 就在queue队列中执行block
    self.observer = [[NSNotificationCenter defaultCenter] addObserverForName:UITextFieldTextDidBeginEditingNotification object:self queue:[[NSOperationQueue alloc] init] usingBlock:^(NSNotification * _Nonnull note) {
        // 一旦监听到通知, 就会执行这个block中的代码
    }];
    
    // 最后需要移除监听
    [[NSNotificationCenter defaultCenter] removeObserver:self.observer];
    

    一次性通知(监听1次后就不再监听)

    id observer = [[NSNotificationCenter defaultCenter] addObserverForName:UITextFieldTextDidBeginEditingNotification object:self queue:[[NSOperationQueue alloc] init] usingBlock:^(NSNotification * _Nonnull note) {
    
    
        // 移除通知
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }];
    

    其他

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 因为是在子线程注册了通知监听器, 所以beginEditing和endEditing会在子线程中执行
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beginEditing) name:UITextFieldTextDidBeginEditingNotification object:self];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endEditing) name:UITextFieldTextDidEndEditingNotification object:self];
    });
    

    相关文章

      网友评论

          本文标题:知识点总结16:如何监听一个控件内部的事件的四种方法

          本文链接:https://www.haomeiwen.com/subject/ppgmittx.html