源码0306-hitText底层实现

 事件是不是系统事件,是系统事件交给代理处理(比如程序启动完成时交给代理开启一个runRoop);

不是系统事件就交给主窗口处理(keyWindow);

主窗口会在视图中找到最合适的视图来处理触摸事件;

事件传递的过程-事件处理的过程(谁能处理)----

//  XMGWindow.m
//  04-事件的产生和传递
#import "XMGWindow.h"
@implementation XMGWindow


// 事件传递的时候调用
// 什么时候调用:当事件传递给控件的时候,就会调用控件的这个方法,去寻找最合适的view
// 作用:寻找最合适的view

// point:当前的触摸点,point这个点的坐标系就是方法调用者
//- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
//{
//    // 调用系统的做法去寻找最合适的view,返回最合适的view
//    UIView *fitView = [super hitTest:point withEvent:event];
//    
////    NSLog(@"fitView--%@",fitView);
//    return fitView;
//}

// 作用:判断当前这个点在不在方法调用者(控件)上
//- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
//{
//    return YES;
//}

//- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
//{
////    NSLog(@"%s",__func__);
//}
// 点击黄色视图 -》 事件 -》 UIApplication -> UIWindow
// 因为所有的视图类都是继承BaseView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    
    // 1.判断当前控件能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    
    // 2. 判断点在不在当前控件
    if ([self pointInside:point withEvent:event] == NO) return nil;
    
    // 3.从后往前遍历自己的子控件
    NSInteger count = self.subviews.count;
    
    for (NSInteger i = count - 1; i >= 0; i--) {
        UIView *childView = self.subviews[i];
        
        // 把当前控件上的坐标系转换成子控件上的坐标系
        CGPoint childP = [self convertPoint:point toView:childView];
        
        UIView *fitView = [childView hitTest:childP withEvent:event];
        
        
        if (fitView) { // 寻找到最合适的view
            return fitView;
        }
    }
    // 循环结束,表示没有比自己更合适的view
    return self;
}

@end

 

//  BaseView.m
//  04-事件的产生和传递
#import "BaseView.h"

@implementation BaseView

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"%@---touchesBegan",[self class]);
}
// UIApplication -> [UIWindow hitTest:withEvent:] -> whiteView hitTest:withEvent

// 因为所有的视图类都是继承BaseView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
//    NSLog(@"%@--hitTest",[self class]);
//    return [super hitTest:point withEvent:event];
    
    
    // 1.判断当前控件能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    
    // 2. 判断点在不在当前控件
    if ([self pointInside:point withEvent:event] == NO) return nil;
    
    // 3.从后往前遍历自己的子控件
    NSInteger count = self.subviews.count;
    
    for (NSInteger i = count - 1; i >= 0; i--) {
        UIView *childView = self.subviews[i];
        
        // 把当前控件上的坐标系转换成子控件上的坐标系
     CGPoint childP = [self convertPoint:point toView:childView];
        
       UIView *fitView = [childView hitTest:childP withEvent:event];
        
        
        if (fitView) { // 寻找到最合适的view
            return fitView;
        }
        
        
    }
    
    // 循环结束,表示没有比自己更合适的view
    return self;
    
}

@end

 

 07-hitText练习1

当从storyboard往类里面拖线的时候发现不能拖,可以反过来从 类文件(xxx.m)拖到storyboard中;

//  YellowView.m
//  07-hitText练习1
#import "YellowView.h"

@interface YellowView ()

@property (nonatomic, weak) IBOutlet UIButton *btn;

@end

@implementation YellowView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 当前坐标系上的点转换到按钮上的点
    CGPoint btnP = [self convertPoint:point toView:self.btn];
    
    // 判断点在不在按钮上
    if ([self.btn pointInside:btnP withEvent:event]) {
        // 点在按钮上
        return self.btn;
    }else{
        return [super hitTest:point withEvent:event];
    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"%@",self.btn);
    
    NSLog(@"%s",__func__);
}

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

@end

08-hitText练习2

 

//  ViewController.m
//  08-hitText练习2
#import "ViewController.h"
#import "PopBtn.h"

@interface ViewController ()

@end

@implementation ViewController
- (IBAction)popChatView:(PopBtn *)sender {
    // 弹出对话框
    UIButton *chatView = [UIButton buttonWithType:UIButtonTypeCustom];
    
    chatView.bounds = CGRectMake(0, 0, 200, 200);
    chatView.center = CGPointMake(100, -100);
    
    [chatView setBackgroundImage:[UIImage imageNamed:@"对话框"] forState:UIControlStateNormal];
    [chatView setBackgroundImage:[UIImage imageNamed:@"小孩"] forState:UIControlStateHighlighted];
    sender.chatView = chatView;
    [sender addSubview:chatView];
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

 

//  PopBtn.h
//  08-hitText练习2
#import <UIKit/UIKit.h>

@interface PopBtn : UIButton

@property (nonatomic, weak) UIButton *chatView;

@end
//  PopBtn.m
//  08-hitText练习2
#import "PopBtn.h"

@implementation PopBtn
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    
    // 当前控件上的点转换到chatView上
    CGPoint chatP = [self convertPoint:point toView:self.chatView];
    
    // 判断下点在不在chatView上
    if ([self.chatView pointInside:chatP withEvent:event]) {
        return self.chatView;
    }else{
        return [super hitTest:point withEvent:event];
    }
}
//找到合适的事件处理对象,来处理事件;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 获取UITouch
    UITouch *touch = [touches anyObject];
    
    // 获取当前的点
    CGPoint curP = [touch locationInView:self];
    
    // 获取上一个的点
    CGPoint preP = [touch previousLocationInView:self];
    
    // 获取偏移量
    CGFloat offsetX = curP.x - preP.x;
    CGFloat OffsetY = curP.y - preP.y;
    
    // 修改控件的位置
    CGPoint center = self.center;
    center.x += offsetX;
    center.y += OffsetY;
    
    self.center = center;
    
}
@end

 

posted @ 2017-04-05 12:56  laugh  阅读(294)  评论(0编辑  收藏  举报