subview事件响应范围

Subview的事件响应

  在view的层级里面,默认情况下subview是可以显示到其父view的frame区域以外的,通过设置clipToBounds属性为YES,可以限制subview的显示区域。但是touch在各个UIView中传递的时候,区域时限制在view的frame内,此处包含两个信息:1、在当前view的frame以外所做的操作是不会传递到该view中的,这一点很容易理解。2、如果touch事件是发生在当前view的frame以外,该view所有的subview将也不会再收到该消息。这一点通常容易被我们忽略,很多奇怪的问题就是这个引起的。

  下面请看一个小例子,定制view的代码如下:

SvTestClipSubviewEvent.h
//
// SvTestClipSubviewEvent.h
// SvUIViewSample
//
// Created by maple on 3/19/12.
// Copyright (c) 2012 smileEvday. All rights reserved.
//
// 默认的情况下,subView可以超出父view的frame,即可以显示到父View的外边
// 但是消息的接受返回却是由于父View的大小限制,即出了父View的subView将不能收到消息
// 在程序中一定要注意当前程序view的最底层是充满整个window的可用区域的,
// 否则将会导致某些区域明明有按钮但是却点不中的问题

#import <UIKit/UIKit.h>

@interface SvTestClipSubviewEvent : UIView

@end
SvTestClipSubviewEvent.m
//
// SvTestClipSubviewEvent.m
// SvUIViewSample
//
// Created by maple on 3/19/12.
// Copyright (c) 2012 smileEvday. All rights reserved.
//

#import "SvTestClipSubviewEvent.h"

@interface SvTestClipSubviewEvent()

- (void)btnAction:(UIButton*)btn;

@end

@implementation SvTestClipSubviewEvent

- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor redColor];

UIButton *testOutBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
testOutBtn.frame = CGRectMake(-80, -50, 70, 30);
[testOutBtn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[testOutBtn setTitle:@"I'm out" forState:UIControlStateNormal];
[self addSubview:testOutBtn];

UIButton *testInBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
testInBtn.frame = CGRectMake(20, 30, 70, 30);
[testInBtn setTitle:@"I'm in" forState:UIControlStateNormal];
[testInBtn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:testInBtn];
}
return self;
}

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

- (void)btnAction:(UIButton*)sender
{
NSLog(@"HI, you tap button %@", [sender titleForState:UIControlStateNormal]);
}

@end

  在程序的ViewController中添加如下测试代码:

SvTestClipSubviewEvent *testClipSubView = [[SvTestClipSubviewEvent alloc] initWithFrame:CGRectMake(100, 100, 150, 150)];
[self.view addSubview:testClipSubView];
[testClipSubView release];

  运行可以看到如下界面:

  该例子中我们设置定制view的背景颜色为红色,然后创建一个位于定制view外的UIButton “I'm out”以及另一个位于定制view内的UIButton “I‘m in”。运行程序,我们可以发现点击"I'm out"按钮时界面根本没有变化,在btnAction:函数中加断点,也不会进去,这就说明该按钮根本没有接受到消息,同时“I'm in”按钮运行正常,点击后控制台会输出信息“...UIViewSample[664:f803] HI, you tap button I'm in”。这些可以说明subview接收事件的范围是受其superview的frame闲限制的,不可能接受到superview的frame以外的touch事件。

  如果我们的程序有些时候看着一些按钮明明正常但是却不能正常接收touch消息的时候,可能就是其显示位置已经跑出了superview的frame范围了。所以程序的rootview一定要时刻充满整个window,否则就可能导致程序的某些地方不能正常相应用户消息。同时建议对于那些只是充当容器的view,最好设置autoresizingMask的值为UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight,这样当其superview的大小发生变化时候就会自动缩放至superview一样大小,保证每一刻都充满其superview,从而避免因显示位置出界导致的不能正常相应用户touch事件等问题。

posted on 2012-03-22 20:56  一片-枫叶  阅读(3886)  评论(0编辑  收藏  举报