什么可以通过触及(并显示所有)子视图?

taut 发布于 2019-11-10 iphone 最后更新 2019-11-10 12:10 206 浏览

我有以下子视图链:

UIViewController.view -+
                       |-> UIView (subclass) -+
                       |                      +-> UIToolbar 
                       |
                       +------------------------> UIWebView
在子类中,我覆盖其-touchesEnded:forEvent:方法,以便通过CAAnimation在单击时隐藏和显示UIToolbar,以及发出导致视图控制器隐藏其导航栏的NSNotification。 如果我没有将UIWebView作为子视图添加到视图控制器的视图中,那么这可以正常工作。 如果我然后将UIWebView添加为视图控制器视图的子视图,则UIToolbar不会出现,并且我没有获得动画效果。 UIWebView响应触摸,但子类UIView不响应。但是,通知会被触发,导航栏会被隐藏。 安排这些子视图的最佳方法是:
  1. 可以使UIToolbar在屏幕上下滑动
  2. UIWebView可见仍然可以接收其典型的触摸事件:放大/缩小和双击以重置缩放级别
正确的答案将符合这两个标准。
编辑 我在这方面取得了一些进展,但我无法区分触摸事件,因此我不应该触发工具栏隐藏/显示方法。 我将视图控制器的视图属性设置为UIView子类,并通过[self.view insertSubview:self.webView atIndex:0]将Web视图插入到-subviews数组的顶部。 我将以下方法添加到UIView子类:
- (UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event {
    UIView *subview = [super hitTest:point withEvent:event];
if (event.type == UIEventTypeTouches) {
    [self toggleToolbarView:self];

// get touches
NSSet *touches = [event allTouches];

NSLog(@"subview: %@", subview);
NSLog(@"touches: %@", touches);

// individual touches
for (UITouch *touch in touches) {
    switch (touch.phase) {
        case UITouchPhaseBegan:
            NSLog(@"UITouchPhaseBegan");
            break;
        case UITouchPhaseMoved:
            NSLog(@"UITouchPhaseMoved");
            break;
        case UITouchPhaseCancelled:
            NSLog(@"UITouchPhaseCancelled");
            break;
        case UITouchPhaseStationary:
            NSLog(@"UITouchPhaseStationary");
            break;      
        default:
            NSLog(@"other phase...");
            break;
        }
    }
}

return subview;

}

方法-toggleToolbarView:触发NSTimer定时的CAAnimation,其隐藏/显示UIToolbar。 问题是触摸拖动组合导致-toggleToolbarView:被触发(以及放大或缩小Web视图)。我想要做的是仅在有触摸事件时触发-toggleToolbarView:。 当我调用上面的方法-hitTest:withEvent:时,看起来UIWebView吸收了触摸。这两个NSLog报表显示,这是从父UIView-hitTest:withEvent:要么是UIWebViewUIToolbar(当它是在屏幕上,触摸)返回UIView*touches数组为空,因此无法区分触摸事件类型。 还有一件事我会尝试,但我想在这里记录这个尝试,以防其他人有快速建议。感谢您的继续帮助。


编辑二 我在UIWebView捏合/缩放和响应双击方面取得了一些进展。我也可以用单击隐藏/显示工具栏。 我没有弄乱UIWebView实例的私有UIScroller,而是访问其内部私有UIWebDocumentView。但放大后,整个网页视图不会正确重绘。 这意味着:例如,如果我的文档包含PDF或SVG矢量插图,则放大会导致内容模糊,就好像构成渲染的PDF或矢量插图内容的图像切片没有被重新渲染一样新的缩放系数。 为了记录这一点,我将:
  1. 拨打UIView (subclass)而不是ViewerOverlayView
  2. UIWebView名为ViewerWebView
两者分别是UIViewUIWebView的子类。 我的ViewerWebView包含一个新的ivar(使用@property + @synthesized):
UIView *privateWebDocumentView;
(此UIView实际上是指向UIWebDocumentView实例的指针,但是为了避免Apple将应用程序标记为拒绝,我将其转换为UIView,以简单地传递触摸事件。) ViewerWebView的实施覆盖了-hitTest:withEvent:
- (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *_subview = [super hitTest:point withEvent:event];
    self.privateWebDocumentView = _subview;
    return _subview;
}
我的ViewerOverlayView包含新的ivars(包含@property + @synthesized):
ViewerWebView *webView;
UIView *webDocumentView;
在叠加视图的实现中,我重写了-touchesBegan:withEvent:-touchesMoved:withEvent:-touchesEnded:withEvent:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    self.touchHasMoved = NO;
    if (self.webView) {
        UITouch *touch = [touches anyObject];
        CGPoint touchPoint = [touch locationInView:self];
        self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event];
    }
    [super touchesBegan:touches withEvent:event];
    [self.webDocumentView touchesBegan:touches withEvent:event];
}
  • (void) touchesMoved:(NSSet )touches withEvent:(UIEvent )event { self.touchHasMoved = YES; if (self.webView) { UITouch *touch = [touches anyObject]; CGPoint touchPoint = [touch locationInView:self]; self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event]; } [super touchesMoved:touches withEvent:event]; [self.webDocumentView touchesMoved:touches withEvent:event]; }

  • (void) touchesEnded:(NSSet )touches withEvent:(UIEvent )event { if(!self.touchHasMoved) [self toggleToolbarView:self]; if (self.webView) { UITouch *touch = [touches anyObject]; CGPoint touchPoint = [touch locationInView:self]; self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event]; } [super touchesEnded:touches withEvent:event]; [self.webDocumentView touchesEnded:touches withEvent:event]; }

在视图控制器中,我执行以下操作:

  1. 实例化ViewerOverlayView并将其添加为视图控制器的view属性的子视图
  2. 实例化视图控制器的ViewerWebView实例并将其插入子视图数组的底部
  3. ViewerOverlayView网络视图属性设置为视图控制器的网络视图
所以现在我的透明ViewerOverlayView正确地将缩放/拉伸/双击触摸事件传递给ViewerWebView实例的私有UIWebDocumentView实例。然后UIWebDocumentView调整大小。 但是,内容不会使用新比例重绘。因此,基于矢量的内容(例如PDF,SVG)在放大时看起来很模糊。 这是视图看起来没有缩放,漂亮和锐利的样子: UIWebView, no scaling 这是放大时视图的样子,如果没有所有这些自定义事件的话,它就不会像以前那样清晰: UIWebView, blurry zoom 有趣的是,我只能在两个缩放级别之间重新缩放。一旦我停止双击拉伸以放大任何级别,它然后“快速回退”到您在上图中看到的帧。 我在ViewerWebView及其内部UIWebDocumentView实例上尝试了-setNeedsDisplay。两种方法调用都不起作用。 尽管我想避免使用私有方法,但由于我希望最终获得此应用程序的批准,我还尝试了访问私有UIWebDocumentView方法-_webCoreNeedsDisplaythis suggestion
[(UIWebDocumentView *)self.webDocumentView _webCoreNeedsDisplay];
此方法导致应用程序从unrecognized selector异常中崩溃。也许Apple已将此方法的名称从SDK 3.0更改为3.1.2。 除非有办法让Web视图重绘其内容,否则我想我必须废弃透明覆盖视图并只绘制Web视图,以使Web视图正常工作。太糟糕了! 如果有人有关于重绘后缩放的想法,请随时添加答案。 再次感谢大家的投入。顺便说一句,我没有取消这个问题的赏金 - 我不知道为什么它不会自动被授予这个帖子的第一个答案,因为我原本期望从以前的经验中发生。
编辑III 我发现这个网页显示了how to subclass the application window,以便观察点击并将这些点击转发给视图控制器。 没有必要在整个应用程序中的任何地方实现其委托,只有在我想观察UIWebView上的点击时才这样,所以这对于文档查看器来说可能非常有效。 到目前为止,我可以观察点击,隐藏和显示工具栏,UIWebView的行为类似于Web视图。我可以放大和缩小Web视图,并正确地重新呈现文档。 虽然学习如何以更通用的方式管理水龙头会很好,但这看起来是一个非常好的解决方案。
已邀请:

lillo

赞同来自:

我正在阅读你的问题的方式,你想拦截事件来触发一些动作(动画工具栏,发布通知),同时允许事件也到达其自然目的地。 如果我试图这样做,我会将UIToolbar直接作为UIViewController.view的子视图。 UIWebView也是直接子视图。 UIViewController.view应该是UIView的子类,它需要覆盖

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
通过发回您想要接收事件的视图(UIToolbar或UIWebView),视图可以侧面成为事件处理的一部分,同时您仍有机会触发所需的操作。 一个例子可能是:
- (UIView *)hitTest:(CGPoint) point withEvent:(UIEvent *)event {
    UIView* subview = [super hitTest:point withEvent:event];
    /* Use the event.type, subview to decide what actions to perform */
    // Optionally nominate a default event recipient if your view is not completely covered by subviews
    if (subview == self) return self.webViewOutlet;
    return subview;
}

pnon

赞同来自:

如果您首先将UIView子类添加到视图控制器的视图中,然后按照您的描述添加UIWebView,UIWebView将完全覆盖UIView子类(假设两个视图完全重叠,如您在问题注释中所提到的那样)。 听起来你不想要那个 - 你想要Web视图上方的子类。您需要以相反的顺序将它们添加到视图控制器的视图中(添加Web视图,然后添加自定义UIView)或调用:

[viewContoller.view insertSubview:webView belowSubview:subclass];
有了这个,让我们谈谈如何拦截触摸事件。我将建议一种不同的方法,让我做一些类似的事情。 Here's the question and answer I created for it - 随时可以查看有关此技术的更多详细信息。 我们的想法是您将UIApplication子类化并覆盖-sendEvent:方法。你会看起来像这样,我想:
- (void)sendEvent:(UIEvent *)event {
    [super sendEvent:event];
NSSet *allTouches = [event allTouches];
    if ([allTouches count] > 0) {
        UITouch *touch = [allTouches anyObject];
        if ([allTouches count] == 1 && touch.phase == UITouchPhaseEnded && touch.tapCount == 1) {
            // Do something here
        }
    }
}
我不认为这会让你100%在那里,但希望这是一个开始。特别是,您可能想要使用if条件来检查您正在处理的UITouches的属性。我不确定我是否包括正确的支票,或者他们是否能抓住你正在寻找的确切条件。 祝你好运!

onam

赞同来自:

好的,这个怎么样:

  • 在自定义UIView子类中,添加指向UIWebView的指针
  • 将一个ivar添加到UIView子类,BOOL hasMoved;
  • UIView子类中,覆盖这样的触摸方法:
    -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
      hasMoved = NO;
      [webView touchesBegan:touches withEvent:event];
    }
    -(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
      hasMoved = YES;
      [webView touchesMoved:touches withEvent:event];
    }
    -(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
      if(!hasMoved) [self hideToolbar];
      [webView touchesEnded:touches withEvent:event];
    }
    
    然后将自定义视图放在Web视图的顶部。看起来太简单了,可能是 - 可能需要一些魔法来正确处理多个触摸,你可能需要在touchesBegan:方法中使用NSTimer作为延迟来处理双击情况,但我认为一般的想法是声音...也许?

dsit

赞同来自:

取2:与上面相同,但重写方法如下:

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = NO;
    [[[webView subviews] objectAtIndex:0] touchesBegan:touches withEvent:event];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = YES;
    [[[webView subviews] objectAtIndex:0] touchesMoved:touches withEvent:event];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if(!hasMoved) [self toggleToolbar];
    [[[webView subviews] objectAtIndex:0] touchesEnded:touches withEvent:event];
}
-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [[[webView subviews] objectAtIndex:0] touchesCancelled:touches withEvent:event];
}
这有点......有点儿。这允许您拖动Web视图。但它没有正确处理其他触摸事件(链接,捏,任何东西)。所以。可能不是很有用,但它对我来说是进步!