将触摸传递到(并显示所有)子视图的工作原理是什么? [英] What works to pass touches through to (and display all) subviews?
问题描述
我有以下子视图链:
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:
-
UIToolbar
可以在屏幕上上下滑动 - 可见的
UIWebView
仍然可以接收其典型的触摸事件:放大/缩小并点按两次以重置缩放级别
- The
UIToolbar
can be made to slide on and off the screen - 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*
是UIWebView
或UIToolbar
(在屏幕上被触摸时). 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.
出于记录目的,我将:
-
呼叫
UIView (subclass)
而不是ViewerOverlayView
UIWebView
被称为ViewerWebView
两者都是UIView
和UIWebView
的子类.
我的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:
-
实例化
ViewerOverlayView
并将其添加为视图控制器的view
属性的子视图
Instantiate the
ViewerOverlayView
and add it as a subview of the view controller'sview
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屋!