【问题标题】:how to dismiss a modal view controller presented as "Form Sheet" when a touch occur outside the form sheet?当表单外部发生触摸时,如何关闭显示为“表单”的模态视图控制器?
【发布时间】:2014-06-17 04:42:34
【问题描述】:

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

【问题讨论】:

  • 这里有更简单的解决方案:stackoverflow.com/questions/2623417/…
  • @Cédric 该解决方案对于我们的用例来说还不够好。添加一个超出视图边界的弹出框,然后 bam.该解决方案失败了。

标签: cocoa-touch ipad uiviewcontroller


【解决方案1】:

在 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];
}

【讨论】:

    【解决方案2】:

    我认为正确的答案是“不要那样做”

    【讨论】:

      猜你喜欢
      • 2012-02-24
      • 1970-01-01
      • 2013-10-14
      • 2013-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多