当在表单外发生触摸时,如何关闭呈现为"表单"的模态视图控制器?

mnd*_*hkr 20 cocoa-touch uiviewcontroller ipad

我有一个视图控制器(带有UIWebView)我以表单样式呈现.我必须在视图控制器的视图的UIToolbar中放置一个"完成"按钮才能将其解除.但是,由于以"表单"样式呈现它会在视图控制器的视图之外留下大量未使用的空间......我在徘徊..有没有办法检测视图外的触摸?在那个"变灰"的地方?提前致谢

Lac*_*che 18

在iOS 4.2上,具有模态表单的基于导航控制器的应用程序的视图层次结构如下所示viewWillAppear:.一旦出现模态视图,UITransitionView就被包含模态视图层次结构的UIDropShadowView所取代.

UIWindow
    UILayoutContainerView (regular hierarchy)
    UIDimmingView 
    UITransitionView
Run Code Online (Sandbox Code Playgroud)

Apple警告不要依赖内置视图的子视图,因为这些视图被认为是私有的,并且可能在将来的版本中发生变化.这可能会破坏您的应用.

方法1
我们可以在窗口的最后两个子视图之间插入透明视图,并通过关闭模态窗体来响应触摸事件.

此实现检查窗口的视图层次结构是否符合预期,如果没有,将默默地执行任何操作.

创建和使用DismissingViewviewWillAppear:在模态视图控制器.

DismissingView *dismiss = [[DismissingView alloc] initWithFrame:window.frame 
                            selector:@selector(dismissView:) target:self];
[dismiss addToWindow:window];
[dismiss release];
Run Code Online (Sandbox Code Playgroud)

并实施.

@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
Run Code Online (Sandbox Code Playgroud)

方法2
第一种方法是首选方法,因为这种方法对模态形式的每个触摸事件都有额外的逻辑.

将透明视图添加为窗口的前视图.模态视图框架内的触摸事件将传递给它,而框架外的触摸事件将忽略模态窗体.

在它出现之前,模态视图层次结构如下所示.当它出现时,UIDropShadowView将替换窗口子视图中的UITransitionView.UILayoutContainerView

UIDropShadowView
    UIImageView
    UILayoutContainerView
        UINavigationTransitionView
            UIViewControllerWrapperView
                UIView (the modal view)
        UINavigationBar
Run Code Online (Sandbox Code Playgroud)

实现与以前大致相同,使用新属性并addToWindow:替换为addToWindow:modalView:.

viewWillAppear:由于UIDropShadowView取代了UITransitionView,因此仍然可以将DismissingView 添加到窗口中.

如果视图层次结构不符合预期,我们将再次无声地执行任何操作.

@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];
}
Run Code Online (Sandbox Code Playgroud)