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事件等問題。
