iOS开发-Day28-UI自定义控件&消息传递&事件传递机制

1、自定义UIView

//  MyControl.h
#import <UIKit/UIKit.h>
@interface MyControl : UIView
@property(strong,nonatomic) UIView *aView;
@end

//  MyControl.m
#import "MyControl.h"
@implementation MyControl
//重写initWiyhFrame方法
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.aView=[[UIView alloc] initWithFrame:CGRectMake(50, 50, 50, 50)];
       // self.aView=[[UIView alloc] initWithFrame:frame];
        self.aView.backgroundColor=[UIColor redColor];
        [self addSubview:self.aView];
    }
    return self;
}
@end

2、在自定义控件中加入动作

//  MyControl.h
#import <UIKit/UIKit.h>

@interface MyControl : UIView

@property(strong,nonatomic) id  target;

@property(assign,nonatomic) SEL action;

-(void)addTarget:(id) target action:(SEL)action;

@end

//  MyControl.m
#import "MyControl.h"
@implementation MyControl
-(void)addTarget:(id)target action:(SEL)action
{
    self.target=target;
    self.action=action;
}
//重写触摸方法
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    //调用方法
    [self.target performSelector:self.action];
}
@end

//测试:
- (void)viewDidLoad {
    [super viewDidLoad];

    self.myControl=[[MyControl alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
    self.myControl.backgroundColor=[UIColor yellowColor];

    [self.myControl addTarget:self action:@selector(test)];

    [self.view addSubview:self.myControl];   
}
-(void)test
{
    NSLog(@"我的自定义控件");
}

3、数据回传(代理方式)

代理的实现过程

1、在目标控制器的头文件定义代理和代理对应的方法, 并定义代理属性

#import <UIKit/UIKit.h>
//分别为联系人控制器, 联系人信息模型, 添加联系人控制器
@class ContactViewController, Contact, AddViewController;
//设置代理
@protocol AddViewControllerDelegate <NSObject>
@optional
//声明代理的方法, 定义这个方法在源控制器(也是就代理使用者)
- (void)addViewController:(AddViewController *)addVc DidAddContact: (Contact *)contact;
@end

@interface AddViewController : UIViewController
@property (nonatomic, strong) ContactViewController *contacts;//模型
@property (nonatomic, weak) id<AddViewControllerDelegate> delegate; //定义代理属性
@end

2、当某个事件(比如点击事件后需要传递数据给代理)引发代理事件时, 通知代理, 如果代理有某个方法, 就调用这个方法通知代理

//最好将所有参数封装成模型, 直接给代理传递模型
- (IBAction)add {
    //1. 关闭当前控制器(出栈)
    [self.navigationController popViewControllerAnimated:YES];
    //2. 传递数据给上一级控制器(contactViewController)
    //2.1. 通知代理 (调用代理方法)
    if ([self.delegate respondsToSelector:@selector(addViewController:DidAddContact:)]) {
        //初始化模型并复制
        Contact *contact = [[Contact alloc] init];
        contact.name = self.nameField.text;
        contact.phone = self.phoneField.text;
        //给代理传入模型
        [self.delegate addViewController:self DidAddContact:contact];
    }
}

3、代理遵守协议, 代理接到消息后, 执行代理方法

@interface ContactViewController () <AddViewControllerDelegate>
- (IBAction)logout:(id)sender;
@property (nonatomic, strong) NSMutableArray *contacts;
@end


//add控制器的代理方法
- (void)addViewController:(AddViewController *)addVc DidAddContact:(Contact *)contact
{
    //1. 添加模型数据
    [self.contacts addObject:contact];
    //2. 刷新tableView表格
    [self.tableView reloadData];
}
  • 总结:
    1、联系人列表界面点击加号添加联系人(即将进行控制器的跳转), 调用- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender方法(拿到目标控制器,设置添加控制器的代理为self(也就是源控制器)
    2、进行控制器跳转
    3、add方法(目标控制器触发代理事件)关闭当前控制器,判断代理联系人控制器有没有实现某个方法, 调用联系人列表的代理
    4、代理执行这个代理方法

4、ios事件传递
这里写图片描述

  • 如上图,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)。通过下图,我们来看看两种不同情况下得事件传递机制。
    这里写图片描述

左边的情况,接收事件的initial view如果不能处理该事件并且她不是顶层的View,则事件会往它的父View进行传递。initial view的父View获取事件后如果仍不能处理,则继续往上传递,循环这个过程。如果顶层的View还是不能处理这个事件的话,则会将事件传递给它们的ViewController,如果ViewController也不能处理,则传递给Window(UIWindow),此时Window不能处理的话就将事件传递给Application(UIApplication),最后如果连Application也不能处理,则废弃该事件。

右边图的流程唯一不同就在于,如果当前的ViewController是由层级关系的,那么当子ViewController不能处理事件时,它会将事件继续往上传递,直到传递到其Root ViewController,后面的流程就跟之前分析的一样了。

这就是事件响应者链的传递机制,通过这些内容,我们可以更深入的了解事件在iOS中得传递机制,对我们在实际开发中更好的理解事件操作的原理有很大的帮助,也对我们实现复杂布局进行事件处理时增添了多一份的理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值