UI基础 - convertPoint:和convertRect: | 当子视图超出父视图时如何响应

■ 坐标转换

1. convertPoint: 和 convertRect: 两方法是分别将 point、rect 从目标视图中转换到当前视图里,并返回在当前视图中的像素值。注:fromView、toView 参数如果是 nil,那么其缺省值是 self.view.window

2. 代码示例

 1     // 红色视图
 2     UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, 100, 280, 200)];
 3     redView.backgroundColor = [UIColor redColor];
 4     [self.view addSubview:redView];
 5 
 6     // 蓝色视图
 7     UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 320, 100, 80)];
 8     blueView.backgroundColor = [UIColor blueColor];
 9     [self.view addSubview:blueView];
10     
11     // 灰色视图:红色视图的子视图
12     UIView *grayView = [[UIView alloc] initWithFrame:CGRectMake(30, 50, 140, 100)];
13     grayView.backgroundColor = [UIColor grayColor];
14     [redView addSubview:grayView];
15     
16     // ---------------- fromView -----------------
17     CGPoint newPointI = [self.view convertPoint:grayView.center fromView:redView];
18     NSLog(@"redView 的子视图 grayView 相对于在 self.view 中的中心位置:%@",NSStringFromCGPoint(newPointI));// {100, 200}
19     // {30 + 140/2.0,  100 + 50 + 100/2.0}
20 
21 
22     CGRect newRectI = [self.view convertRect:grayView.frame fromView:redView];
23     NSLog(@"redView 的子视图 grayView 相对于在 self.view 中的位置:%@",NSStringFromCGRect(newRectI));// {{30, 150}, {140, 100}}
24     // {{30, 100 + 50}, {140, 100}}
25 
26     CGRect newRectII = [grayView convertRect:CGRectMake(50, 50, 20, 20) fromView:blueView];
27     NSLog(@"blueView 内的 CGRect 相对于在 grayView 中的位置:%@",NSStringFromCGRect(newRectII));    // {{20, 220}, {20, 20}}
28     // {{-30 + 50, -(100 + 50) + 320 + 50}, {20, 20}}
29 
30     // ------------------- toView -------------------
31     CGPoint newPointIV  = [redView convertPoint:grayView.center toView:self.view];
32     NSLog(@"redView 的子视图 grayView 相对于在 self.view 中的中心位置:%@",NSStringFromCGPoint(newPointIV));// {100, 200}
33 
34     CGRect newRectV = [redView convertRect:grayView.frame toView:self.view];
35     NSLog(@"redView 的子视图 grayView 相对于在 self.view 中的位置:%@",NSStringFromCGRect(newRectV));// {{30, 150}, {140, 100}}
36 
37     CGRect newRectIVI = [blueView convertRect:CGRectMake(50, 50, 20, 20) toView:grayView];
38     NSLog(@"blueView 内的 CGRect 相对于在 grayView 中的位置:%@",NSStringFromCGRect(newRectIVI));   // {{20, 220}, {20, 20}}

■ 应用场景

1. 我们知道 UIButton 超出父视图的部分是无法响应事件的....... 如何使其超出父视图的部分,同样可以响应触摸事件?我们知道 UIView 提供了两个方法来确定响应者

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

每次点击 View 时,两个方法各会调用两次:分别是入栈、出栈操作。注:superView 先入栈,其调用时机分别是消息传递时、响应时

2.  在 RedView 上面添加一个超出其范围的 Btn,当我们点击超出范围的区域时,同样可以触发 Btn 绑定的方法

// - RedView.m

 1 #import "RedView.h"
 2 
 3 @implementation RedView
 4 
 5 // 是否能接受触摸事件
 6 -(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
 7 
 8     UIView *view = [super hitTest:point withEvent:event];
 9     NSLog(@"%s---%@",__FUNCTION__,view);// 超出 self 的点击区域会返回 nil
10     // 在这里我们通过判断 nil 的值,就可以找到超出 self 范围的子视图
11     if (view == nil) {
12         for (UIView *subView in self.subviews) {
13             // subView 就是 testBtn
14             CGPoint convertPoint = [subView convertPoint:point fromView:self];// 坐标转换
15             if (CGRectContainsPoint(subView.bounds, convertPoint)) {
16                 view = subView;
17             }
18         }
19     }
20     return view;
21 }
22 
23 // 触摸点是否在自己身上
24 -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
25 
26     NSLog(@"pointInside:%@",[super pointInside:point withEvent:event]?@"yes":@"no");
27     return [super pointInside:point withEvent:event];
28 }
29 
30 @end

// - ViewController.m

 1 #import "ViewController.h"
 2 #import "RedView.h"
 3 
 4 @implementation ViewController
 5 
 6 - (void)viewDidLoad {
 7     [super viewDidLoad];
 8     self.view.backgroundColor = [UIColor blackColor];
 9 
10     // 父视图 RedView
11     RedView *redView = [[RedView alloc] initWithFrame:CGRectMake(50, 100, 180, 180)];
12     redView.backgroundColor = [UIColor redColor];
13     [self.view addSubview:redView];
14 
15     // 子视图 Btn
16     UIButton *testBtn = [UIButton buttonWithType:UIButtonTypeCustom];
17     testBtn.frame = CGRectMake(120, 120, 130, 130);
18     testBtn.backgroundColor = [UIColor cyanColor];
19     [redView addSubview:testBtn];
20     [testBtn addTarget:self action:@selector(makeClick) forControlEvents:UIControlEventTouchUpInside];
21 
22 }
23 
24 // Btn 超出父视图范围,但是同样可以触发该方法
25 -(void)makeClick{
26 
27     NSLog(@"%s",__func__);
28 }
29 
30 @end

运行效果

日志信息

红色框:鼠标点击在 1 处的日志,makeClick: 方法触发

绿色框:鼠标点击在 2 处的日志,makeClick: 方法同样会触发

蓝色框:鼠标点击在 3 处的日志,makeClick: 方法不会触发

 

posted on 2018-04-04 19:10  低头捡石頭  阅读(70)  评论(0编辑  收藏  举报

导航