8

我有以下子视图链:

UIViewController.view -+
                       |-> UIView (subclass) -+
                       |                      +-> UIToolbar 
                       |
                       +------------------------> UIWebView

在子类中,我重写了它的-touchesEnded:forEvent:方法,以便在UIToolbar单击时隐藏和显示,通过 a CAAnimation,以及发出一个NSNotification导致视图控制器隐藏其导航栏的问题。

如果我不将UIWebView作为子视图添加到视图控制器的视图中,那么它可以正常工作。

如果我然后添加UIWebView作为视图控制器视图的子视图,则UIToolbar不会出现,并且我没有获得动画效果。UIWebView响应触摸,但子类不UIView响应。但是,通知确实被触发了,并且导航栏确实被隐藏了。

安排这些子视图的最佳方法是什么,以便:

  1. UIToolbar可以在屏幕上滑动和滑出
  2. 可见仍然可以接收其UIWebView典型的触摸事件:放大/缩小和双击以重置缩放级别

正确的答案将满足这两个标准。


编辑

我在这方面取得了一些进展,但我无法区分触摸事件,因此我在不应该触发工具栏隐藏/显示方法时触发它。

我将视图控制器的视图属性设置为UIView子类,并-subviews通过[self.view insertSubview:self.webView atIndex:0].

我在子类中添加了以下方法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*从 parent 返回的 theUIView*-hitTest:withEvent:theUIWebView或 the UIToolbar(当它在屏幕上并被触摸时)。该touches数组为空,因此无法区分触摸事件类型。

还有一件事我会尝试,但我想在这里记录一下这个尝试,以防其他人有快速的建议。感谢您的持续帮助。


编辑二

UIWebView我在捏/缩放和双击响应方面取得了一些进展。我还可以通过单击隐藏/显示工具栏。

我没有弄乱UIWebView实例的 private UIScroller,而是访问它的内部 private UIWebDocumentView。但是放大后,整个 Web 视图无法正确重绘。

这意味着:如果我的文档包含 PDF 或 SVG 矢量图,例如,放大会导致内容模糊,就好像构成渲染的 PDF 或矢量图内容的图像图块没有被重新渲染新的缩放系数。

为了记录这一点,我将:

  1. UIView (subclass)改为调用ViewerOverlayView

  2. UIWebView称为ViewerWebView

两者分别是UIView和的子类UIWebView

MyViewerWebView包含一个新的 ivar(带@property+ @synthesized):

UIView *privateWebDocumentView;

(这UIView实际上是一个指向UIWebDocumentView实例的指针,但为了避免 Apple 将应用程序标记为拒绝,我将其转换为 a 只是UIView为了传递触摸事件。)

ViewerWebView覆盖的实现-hitTest:withEvent:

- (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *_subview = [super hitTest:point withEvent:event];
    self.privateWebDocumentView = _subview;
    return _subview;
}

MyViewerOverlayView包含新的 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,无缩放

这是放大后的视图,它不像没有所有这些自定义事件的情况下那样清晰:

UIWebView,模糊缩放

有趣的是,我只能在两个缩放级别之间重新缩放。一旦我停止双击拉伸以放大任何级别,它就会“弹回”到您在上图中看到的帧。

我尝试-setNeedsDisplayViewerWebView及其内部UIWebDocumentView实例。两种方法调用都不起作用。

尽管想避免使用私有方法,但因为我希望最终获得该应用程序的批准,我也尝试了访问私有方法的建议UIWebDocumentView-_webCoreNeedsDisplay

[(UIWebDocumentView *)self.webDocumentView _webCoreNeedsDisplay];

此方法导致应用程序因unrecognized selector异常而崩溃。或许 Apple 已将此方法的名称从 SDK 3.0 更改为 3.1.2。

除非有办法让 web 视图重绘其内容,否则我想我将不得不废弃透明覆盖视图并只绘制一个 web 视图,以使 web 视图正常工作。太糟糕了!

如果有人对缩放后重绘有想法,请随时添加答案。

再次感谢大家的意见。顺便说一句,我没有取消这个问题的赏金——我不知道为什么它没有自动授予这个线程的第一个答案,因为我原本期望从以前的经验中发生这种情况。


编辑三

我发现这个网页显示了如何对应用程序窗口进行子类化,以便观察点击并将这些点击转发到视图控制器。

没有必要在整个应用程序的任何地方都实现它的委托,只需要在我想观察点击的地方实现它UIWebView,所以这对于文档查看器来说可能非常有效。

到目前为止,我可以观察点击、隐藏和显示工具栏,其UIWebView行为类似于 web 视图。我可以放大和缩小 Web 视图,并且正确地重新呈现文档。

虽然学习如何以更通用的方式管理水龙头会很好,但这看起来是一个非常棒的解决方案。

4

4 回答 4

3

我正在阅读您的问题的方式,您希望拦截事件以触发某些操作(动画工具栏、发布通知),同时允许事件也到达其自然目的地。

如果我试图这样做,我会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;
}
于 2009-12-09T08:02:01.863 回答
1

如果您首先将 UIView 子类添加到视图控制器的视图中,然后按照您的描述添加 UIWebView,那么 UIWebView 将完全覆盖 UIView 子类(假设两个视图完全重叠,正如您在问题评论中提到的那样)。

听起来你不想要那个 - 你想要web 视图上方的子类。您需要以相反的顺序将它们添加到视图控制器的视图中(添加 Web 视图,然后添加自定义 UIView)或调用:

[viewContoller.view insertSubview:webView belowSubview:subclass];

说了这么多,我们来谈谈如何拦截触摸事件。我将提出一种不同的方法,让我可以做类似的事情。这是我为它创建的问题和答案- 请随时查看有关此技术的更多详细信息。

这个想法是你继承 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 的属性。我不确定我是否包含正确的检查,或者他们是否会抓住您正在寻找的确切情况。

祝你好运!

于 2009-12-09T08:02:51.853 回答
1

好吧,这个呢:

- 在您的自定义UIView子类中,添加一个指向您的UIWebView

- 向子类添加 ivar UIViewBOOL 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 视图的顶部。似乎太简单了,而且可能是——正确处理多次触摸可能需要一些魔法,并且您可能需要NSTimertouchesBegan:方法中使用 a 作为延迟来处理双击情况,但我认为总体思路是合理的。 。也许?

于 2009-12-11T06:12:39.620 回答
1

取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 视图。但它没有正确处理其他触摸事件(跟随链接,捏,任何东西)。所以。可能不是很有用,但对我来说是进步!

于 2009-12-16T09:39:21.480 回答