在 iOS 4.2 上,带有模式表单的基于导航控制器的应用程序的视图层次结构在 viewWillAppear: 期间如下所示。模态视图出现后,UITransitionView 已被包含模态视图层次结构的 UIDropShadowView 替换。
UIWindow
UILayoutContainerView (regular hierarchy)
UIDimmingView
UITransitionView
Apple 警告不要依赖内置视图的子视图,因为这些视图被认为是私有的,并且可能会在未来的版本中发生变化。这可能会破坏您的应用。
方法 1
我们可以在 window 的最后两个子视图之间插入一个透明视图,并让它通过关闭模态表单来响应触摸事件。
此实现检查窗口的视图层次结构是否符合预期,如果不是,则不执行任何操作。
在模态视图控制器中从viewWillAppear: 创建和使用DismissingView。
DismissingView *dismiss = [[DismissingView alloc] initWithFrame:window.frame
selector:@selector(dismissView:) target:self];
[dismiss addToWindow:window];
[dismiss release];
以及实现。
@interface DismissingView : UIView {
}
@property (nonatomic, retain) id target;
@property (nonatomic) SEL selector;
- (id) initWithFrame:(CGRect)frame target:(id)target selector:(SEL)selector;
@end
@implementation DismissingView
@synthesize target;
@synthesize selector;
- (id) initWithFrame:(CGRect)frame target:(id)target selector:(SEL)selector
{
self = [super initWithFrame:frame];
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
self.selector = selector;
self.target = target;
return self;
}
- (void) addToWindow:(UIWindow*)window
{
NSUInteger count = window.subviews.count;
id v = [window.subviews objectAtIndex:count - 1];
if (![@"UITransitionView" isEqual:NSStringFromClass([v class])]) return;
v = [window.subviews objectAtIndex:count - 2];
if (![@"UIDimmingView" isEqual:NSStringFromClass([v class])]) return;
UIView *front = [window.subviews lastObject];
[window addSubview:self];
[window bringSubviewToFront:front];
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
[self removeFromSuperview];
[target performSelector:selector withObject:self];
}
@end
方法 2
首选第一种方法,因为这种方法对模态表单上的每个触摸事件都有额外的逻辑。
添加透明视图作为窗口的前视图。模态视图框架内的触摸事件被传递给它,而框架外的触摸事件则关闭模态窗体。
在它出现之前,模态视图层次结构看起来像这样。当它出现时,UIDropShadowView 将替换窗口子视图中的 UITransitionView。 UILayoutContainerView
UIDropShadowView
UIImageView
UILayoutContainerView
UINavigationTransitionView
UIViewControllerWrapperView
UIView (the modal view)
UINavigationBar
实现与以前大体相同,只是将一个新属性和addToWindow: 替换为addToWindow:modalView:。
DismissingView 仍然可以添加到viewWillAppear: 中的窗口,因为 UIDropShadowView 取代了 UITransitionView。
同样,如果视图层次结构与预期不符,我们将不执行任何操作。
@property (nonatomic, retain) UIView *view;
- (void) addToWindow:(UIWindow*)window modalView:(UIView*)v
{
while (![@"UILayoutContainerView" isEqual:NSStringFromClass([v class])]) {
v = v.superview;
if (!v) {
return;
}
}
self.view = v;
[window addSubview:self];
}
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
CGPoint p = [self convertPoint:point toView:view];
UIView *v = [view hitTest:p withEvent:event];
return v ? v : [super hitTest:point withEvent:event];
}