将触摸传递到(并显示所有)子视图的工作原理是什么? [英] What works to pass touches through to (and display all) subviews?

查看:49
本文介绍了将触摸传递到(并显示所有)子视图的工作原理是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下子视图链:

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

在子类中,我重写了它的-touchesEnded:forEvent:方法,以便通过CAAnimation一次单击即可隐藏和显示UIToolbar,并发出导致视图控制器执行操作的NSNotification隐藏其导航栏.

In the subclass, I override its -touchesEnded:forEvent: method in order to hide and show the UIToolbar on a single tap touch, through a CAAnimation, as well as issue an NSNotification that causes the view controller to hide its navigation bar.

如果我不将UIWebView作为子视图添加到视图控制器的视图,则此方法正常工作.

If I do not add the UIWebView as a subview to the view controller's view, then this works properly.

如果我随后将UIWebView添加为视图控制器视图的子视图,则UIToolbar不会出现,并且我不会获得动画效果. UIWebView响应触摸,但子类UIView则不响应.不过,该通知确实会被触发,导航栏也不会被隐藏.

If I then add the UIWebView as a subview of the view controller's view, then the UIToolbar does not appear, and I do not get the animation effect. The UIWebView responds to touches but the subclassed UIView does not. The notification does get fired, though, and the navigation bar does get hidden.

安排这些子视图的最佳方法是什么?

What is the best way to arrange these subviews so that:

  1. UIToolbar可以在屏幕上上下滑动
  2. 可见的UIWebView仍然可以接收其典型的触摸事件:放大/缩小并点按两次以重置缩放级别
  1. The UIToolbar can be made to slide on and off the screen
  2. The UIWebView is visible can still receive its typical touch-events: zoom in/out and double-tap to reset zoom level

正确答案将同时符合这两个条件.

A correct answer will meet both criteria.

编辑

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

I made some progress with this, but I cannot distinguish touch events and thus I fire a toolbar hide/show method when it should not be fired.

我将视图控制器的view属性设置为UIView子类,然后通过[self.view insertSubview:self.webView atIndex:0]将Web视图插入到-subviews数组的顶部.

I set the view controller's view property to the UIView subclass, and I inserted the web view at the top of the -subviews array via [self.view insertSubview:self.webView atIndex:0].

我在UIView子类中添加了以下方法:

I added the following method to the UIView subclass:

- (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*.

The method -toggleToolbarView: triggers an NSTimer-timed CAAnimation that hides/shows a UIToolbar*.

问题是触摸拖动组合会导致触发-toggleToolbarView:(以及放大或缩小Web视图).我想做的是导致-toggleToolbarView:仅在发生仅触摸事件时才被触发.

The problem is that a touch-drag combination causes -toggleToolbarView: to be fired (as well as zooming in or out of the web view). What I would like to do is cause -toggleToolbarView: to be fired only when there is a touch-only event.

当我调用上述方法-hitTest:withEvent:时,UIWebView看起来很糟.这两个NSLog语句表明,从父级UIView*-hitTest:withEvent:返回的UIView*UIWebViewUIToolbar(在屏幕上被触摸时). touches数组为空,因此无法区分触摸事件类型.

When I call the above method -hitTest:withEvent:, it looks like the UIWebView sucks up the touches. The two NSLog statements show that the UIView* that is returned from the parent UIView*'s -hitTest:withEvent: is either the UIWebView or the UIToolbar (when it is on-screen and touched). The touches array is empty and therefore the touch event types cannot be distinguished.

我将尝试另一件事,但如果其他人有快速建议,我想在此记录下这一尝试.感谢您一直以来的帮助.

There's one more thing I will try, but I wanted to record this attempt here in case others have quick suggestions. Thanks for your continued help.

EDIT II

我在使UIWebView捏住/缩放并响应双抽头方面取得了一些进展.我也可以隐藏/显示带有单键的工具栏.

I made some progress with getting the UIWebView to pinch/zoom and respond to double-taps. I can also hide/show the toolbar with single-taps.

我可以访问其内部的私有UIWebDocumentView,而不是弄乱UIWebView实例的私有UIScroller.但是放大后,整个Web视图无法正确重绘.

Instead of messing with the UIWebView instance's private UIScroller, I access its inner, private UIWebDocumentView. But the web view as a whole does not redraw properly after zooming in.

这是什么意思:例如,如果我的文档包含PDF或SVG矢量图,则放大会导致内容模糊,就好像没有重新构成组成的PDF或矢量插图内容的图块一样. -以新的缩放系数渲染.

What this means: If my document contains a PDF or an SVG vector illustration, for example, zooming in causes the contents to be blurry, as if the image tiles that make up the rendered PDF or vector illustration contents are not being re-rendered at the new zoom factor.

出于记录目的,我将:

  1. 呼叫UIView (subclass)而不是ViewerOverlayView

UIWebView被称为ViewerWebView

两者都是UIViewUIWebView的子类.

我的ViewerWebView包含一个新的ivar(带有@property + @synthesized):

My ViewerWebView contains a new ivar (with @property + @synthesized):

UIView *privateWebDocumentView;

(此UIView实际上是指向UIWebDocumentView实例的指针,但是为避免Apple将应用程序标记为拒绝,我将其强制传递给UIView只是为了简单地将其传递给触摸事件.)

(This UIView is actually a pointer to a UIWebDocumentView instance, but to avoid Apple flagging the app for refusal I cast this to a UIView for the simple purpose of passing it touch events.)

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):

My ViewerOverlayView contains new ivars (with @property + @synthesized):

ViewerWebView *webView;
UIView *webDocumentView;

在覆盖视图的实现中,我覆盖了-touchesBegan:withEvent:-touchesMoved:withEvent:-touchesEnded:withEvent:

In the implementation of the overlay view, I override -touchesBegan:withEvent:, -touchesMoved:withEvent: and -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];
}

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

In the view controller, I do the following:

  1. 实例化ViewerOverlayView并将其添加为视图控制器的view属性的子视图

  1. Instantiate the ViewerOverlayView and add it as a subview of the view controller's view property

实例化视图控制器的ViewerWebView实例,并将其插入子视图数组的底部

Instantiate the view controller's ViewerWebView instance and insert it at the bottom of the subviews array

ViewerOverlayView Web视图属性设置为视图控制器的Web视图

Set the ViewerOverlayView web view property to the view controller's web view

因此,现在我的透明ViewerOverlayView正确地将缩放/拉伸/双击事件传递给了ViewerWebView实例的私有UIWebDocumentView实例.然后UIWebDocumentView调整大小.

So now my transparent ViewerOverlayView correctly passes zoom/stretch/double-tap touch events to the ViewerWebView instance's private UIWebDocumentView instance. The UIWebDocumentView then resizes.

但是,内容不会以新比例重新绘制自身.因此,基于矢量的内容(例如PDF,SVG)在放大时显得模糊.

However, the content does not redraw itself with the new scale. So vector-based content (e.g. PDF, SVG) looks blurry when zooming in.

这是没有缩放,美观又清晰的视图的样子:

Here's what the view looks like without scaling, nice and sharp:

这是放大后视图的外观,不如不使用所有自定义事件那么清晰:

Here's what the view looks like when zoomed in, which is not as sharp as it would otherwise be without all this custom event munging:

有趣的是,我只能在两个缩放级别之间进行缩放.一旦我停止双击以在任何级别上进行放大,然后它就会快照"回到上图所示的帧中.

Interestingly, I can only rescale between two zoom levels. Once I stop double-tap-stretching to zoom in at any level, it then "snaps back" to the frame you see in the image above.

我在ViewerWebView及其内部的UIWebDocumentView实例上尝试了-setNeedsDisplay.两种方法调用均无效.

I tried -setNeedsDisplay on the ViewerWebView and its inner UIWebDocumentView instance. Neither method call worked.

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

Despite wanting to avoid private methods, because I want to eventually get this app approved, I also tried this suggestion of accessing the private UIWebDocumentView method -_webCoreNeedsDisplay:

[(UIWebDocumentView *)self.webDocumentView _webCoreNeedsDisplay];

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

This method caused the app to crash from an unrecognized selector exception. Perhaps Apple has changed the name of this method from SDK 3.0 to 3.1.2.

除非有一种方法可以使Web视图重新绘制其内容,否则我想我将不得不废弃透明的叠加视图并仅绘制Web视图,以使Web视图正常工作.糟透了!

Unless there is a way to cause the web view to redraw its contents, I guess I'm going to have to scrap the transparent overlay view and just draw a web view, in order to get the web view to work properly. That sucks!

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

If anyone has thoughts about redrawing-after-scaling, please feel free to add an answer.

再次感谢大家的投入.顺便说一句,我没有取消对这个问题的赏金-我不知道为什么它不会自动授予该线程的第一个答案,因为我原本希望从以前的经验中得出.

Thanks again to all of you for your input. As an aside, I did not cancel the bounty on this question -- I do not know why it was not automatically awarded to the first answer to this thread, as I was otherwise expecting to happen from previous experience.

编辑III

我发现此网页显示

I found this web page that shows how to subclass the application window so as to observe taps and forward those taps to the view controller.

没有必要在整个应用程序中的任何地方实现其委托,而仅在我要观察UIWebView的位置上使用它,因此这对于文档查看器来说可能真的很好.

It's not necessary to implement its delegate everywhere throughout the application, only where I want to observe taps on UIWebView, so this may work really well for a document viewer.

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

So far, I can observe taps, hide and show the toolbar, and the UIWebView behaves like a web view. I can zoom in and out of the web view and the document is re-rendered properly.

虽然最好以一种通用的方式来学习如何管理水龙头,但这看起来是一个很好的解决方案.

While it would be nice to learn how to manage taps in a more generic fashion, this looks like a pretty great solution.

推荐答案

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

The way I'm reading your question, you want to intercept the events to trigger some action (animate toolbar, post notification), while allowing the event to also reach its natural destination.

如果我尝试执行此操作,则将UIToolbar直接放在UIViewController.view的子视图中. UIWebView仍然是直接子视图.

If I were trying to do this, I would put the UIToolbar directly as a subview of the UIViewController.view. The UIWebView remains a direct subview also.

UIViewController.view应该是UIView的子类,并且需要覆盖

The UIViewController.view should be a subclass of UIView, and it needs to override

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

通过发送回要接收事件的视图(UIToolbar或UIWebView),视图可以避开事件处理的一部分,而您仍然有机会触发所需的操作.

The view can side-step being part of the event processing by sending back the view that you want to receive the event (UIToolbar or UIWebView), while you still get a chance to trigger the actions you want.

一个例子可能是:

- (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;
}

这篇关于将触摸传递到(并显示所有)子视图的工作原理是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆