我有一个UIWebView
,其中包含我想要自动隐藏的导航栏和工具栏。我已经这样做了,但我想在用户点击UIWebView
时显示它们。
我的问题是UIWebView
捕获了所有触摸事件,我无法拦截我需要的那些。
有没有解决方法呢?
答案 0 :(得分:8)
blog entry取自Google Cache'd(不加载,但谢天谢地Mithin Kumar)here。
(Mithin,我希望你不介意我把它转发到这里;如果你这样做,请在评论中告诉我,我会将其删除。)
最近,我正在开发一个需要在UIWebView上检测tap和事件的项目。我们想要找出用户在UIWebView中点击的HTML元素,然后根据点击的元素执行一些操作。经过一些谷歌搜索,我发现大多数用户在UIWebView上放置一个透明的UIView,重新实现UIResponder类的触摸方法(例如:-touchesBegan:withEvent :),然后将事件传递给UIWebView。详细解释了这种方法{{3}}。该方法存在多个问题。
- 复制/选择停止在UIWebView上工作
- 我们需要创建UIWebView的子类,而Apple说我们不应该对它进行子类化。
- 许多其他UIWebView功能停止工作。
醇>我们最终发现实现这一目标的正确方法是 对UIWindow进行子类化并重新实现-sendEvent:方法。这里 你是如何做到的。首先,创建一个UIWindow子类
#import <UIKit/UIKit.h> @protocol TapDetectingWindowDelegate - (void)userDidTapWebView:(id)tapPoint; @end @interface TapDetectingWindow : UIWindow { UIView *viewToObserve; id <TapDetectingWindowDelegate> controllerThatObserves; } @property (nonatomic, retain) UIView *viewToObserve; @property (nonatomic, assign) id <TapDetectingWindowDelegate> controllerThatObserves; @end
请注意,我们有变量告诉我们UIView在哪个 检测事件和接收事件的控制器 信息。现在,按以下方式实现此类
#import "TapDetectingWindow.h" @implementation TapDetectingWindow @synthesize viewToObserve; @synthesize controllerThatObserves; - (id)initWithViewToObserver:(UIView *)view andDelegate:(id)delegate { if(self == [super init]) { self.viewToObserve = view; self.controllerThatObserves = delegate; } return self; } - (void)dealloc { [viewToObserve release]; [super dealloc]; } - (void)forwardTap:(id)touch { [controllerThatObserves userDidTapWebView:touch]; } - (void)sendEvent:(UIEvent *)event { [super sendEvent:event]; if (viewToObserve == nil || controllerThatObserves == nil) return; NSSet *touches = [event allTouches]; if (touches.count != 1) return; UITouch *touch = touches.anyObject; if (touch.phase != UITouchPhaseEnded) return; if ([touch.view isDescendantOfView:viewToObserve] == NO) return; CGPoint tapPoint = [touch locationInView:viewToObserve]; NSLog(@"TapPoint = %f, %f", tapPoint.x, tapPoint.y); NSArray *pointArray = [NSArray arrayWithObjects:[NSString stringWithFormat:@"%f", tapPoint.x], [NSString stringWithFormat:@"%f", tapPoint.y], nil]; if (touch.tapCount == 1) { [self performSelector:@selector(forwardTap :) withObject:pointArray afterDelay:0.5]; } else if (touch.tapCount > 1) { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(forwardTap :) object:pointArray]; } } @end
以上述方式实现sendEvent方法,然后您可以发送 将您想要的信息备份回控制器。
人们需要记住的事情很少。确保你的 MainWindow.xib文件,窗口是TapDetectingWindow类型而不是 一个UIWindow。只有这样,所有事件都将通过上述事件 重新实现了sendEvent方法。另外,请务必致电[super sendEvent:event]首先然后做你想做的事。
现在,您可以在控制器类中创建UIWebView 以下方式
@interface WebViewController : UIViewController<TapDetectingWindowDelegate> { IBOutlet UIWebView *mHtmlViewer; TapDetectingWindow *mWindow; } - (void)viewDidLoad { [super viewDidLoad]; mWindow = (TapDetectingWindow *)[[UIApplication sharedApplication].windows objectAtIndex:0]; mWindow.viewToObserve = mHtmlViewer; mWindow.controllerThatObserves = self; }
记住你需要在你的文件中编写userDidTapWebView方法 控制器类。这是为了发送而调用的方法 事件信息到控制器类。在我们的情况下,我们 正在UIWebView中发送用户点击的点。
答案 1 :(得分:7)
您可以非常简单地使用UITapGestureRecognizer来检测UIWebView上的点按手势。您必须实现UIGestureRecognizerDelegate方法以允许同时识别。
- (void)viewDidLoad{
[super viewDidLoad];
UITapGestureRecognizer *targetGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
targetGesture.numberOfTapsRequired = 2;
targetGesture.delegate = self;
[self.webView addGestureRecognizer:targetGesture];
}
// called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other
// return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously)
//
// note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
NSLog(@"%@", otherGestureRecognizer);
//if you would like to manipulate the otherGestureRecognizer here is an example of how to cancel and disable it
if([otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){
UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer*)otherGestureRecognizer;
if(tapRecognizer.numberOfTapsRequired == 2 && tapRecognizer.numberOfTouchesRequired == 1){
//this disalbes and cancels all other singleTouchDoubleTap recognizers
// default is YES. disabled gesture recognizers will not receive touches. when changed to NO the gesture recognizer will be cancelled if it's currently recognizing a gesture
otherGestureRecognizer.enabled = NO;
}
}
return YES;
}
-(void)handleTap:(id)sender{
}
答案 2 :(得分:3)
我在 iOS 5-7 ,通用中尝试使用brian.clear's answer(从Mithin Kumar的已灭绝的帖子中复制)时遇到了一些问题,基于故事板的,完全ARC 项目,所以我不得不进行一些更改。我也改进了一些东西,让它更容易理解(至少对我而言)。如果您在2009年尝试使用该答案时遇到问题,也许您应该尝试我的更新。详细说明:
TapDetectingWindow.h
// Created by Cristian Perez <cpr@cpr.name>
// Based on https://stackoverflow.com/a/1859883/423171
#import <UIKit/UIKit.h>
@protocol TapDetectingWindowDelegate
- (void)userDidTapView:(CGPoint)tapPoint;
@end
@interface TapDetectingWindow : UIWindow
@property (nonatomic) UIView *tapDetectingView;
@property (nonatomic) id <TapDetectingWindowDelegate> tapDetectedDelegate;
@end
TapDetectingWindow.m
// Created by Cristian Perez <cpr@cpr.name>
// Based on https://stackoverflow.com/a/1859883/423171
#import "TapDetectingWindow.h"
@implementation TapDetectingWindow
@synthesize tapDetectingView;
@synthesize tapDetectedDelegate;
- (id)initWithFrame:(CGRect)frame
{
return [super initWithFrame:frame];
}
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
if (tapDetectingView == nil || tapDetectedDelegate == nil)
{
return;
}
NSSet *touches = [event allTouches];
if (touches.count != 1)
{
return;
}
UITouch *touch = touches.anyObject;
if (touch.phase != UITouchPhaseEnded)
{
return;
}
if (touch.view != nil && ![touch.view isDescendantOfView:tapDetectingView])
{
return;
}
CGPoint tapPoint = [touch locationInView:tapDetectingView];
NSString *tapPointStr = NSStringFromCGPoint(tapPoint);
if (touch.tapCount == 1)
{
[self performSelector:@selector(notifyTap:) withObject:tapPointStr afterDelay:0.4];
// Make the afterDelay value bigger in order to have more chances of detecting a double tap and thus being able to cancel the single tap event, or make it smaller if you don't care about double taps and want to get the tap event as soon as possible
}
else if (touch.tapCount > 1)
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(notifyTap:) object:tapPointStr];
}
}
- (void)notifyTap:(NSString *)tapPointStr
{
CGPoint tapPoint = CGPointFromString(tapPointStr);
[tapDetectedDelegate userDidTapView:tapPoint];
}
@end
window
你应该在 YourAppDelegate.h 中有这样的东西。不要更改属性的名称!
@interface YourAppDelegate : UIResponder <UIApplicationDelegate>
{
// ...
}
// ...
// The app delegate must implement the window property if it wants to use a main storyboard file
@property (nonatomic) UIWindow *window;
@end
window
属性就像这样,应该在 YourAppDelegate.m 中。同样,不要更改方法的名称!
// Replace the default UIWindow property with a TapDetectingWindow
- (TapDetectingWindow *)window
{
static TapDetectingWindow *tapDetectingWindow = nil;
if (!tapDetectingWindow)
{
tapDetectingWindow = [[TapDetectingWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
}
return tapDetectingWindow;
}
请务必在 MainViewController.h
中采用点按处理协议#import "TapDetectingWindow.h"
@interface MainViewController : UIViewController <TapDetectingWindowDelegate, ...>
{
// ...
}
// ...
@end
指定您的webview(实际上任何UIView都应该工作)并在视图控制器的(void)viewDidLoad 方法中点击事件处理程序
- (void)viewDidLoad
{
// ...
// Allow tap detection in webview
TapDetectingWindow *tapDetectingWindow = (TapDetectingWindow*)[(YouTubeRPAppDelegate*)[[UIApplication sharedApplication] delegate] window];
tapDetectingWindow.tapDetectingView = self.webView; // Your UIWebView
tapDetectingWindow.tapDetectedDelegate = self;
}
只需在视图控制器中实现 userDidTapView 方法
即可- (void)userDidTapView:(CGPoint)tapPoint
{
NSLog(@"Tap detected in webview at %@", NSStringFromCGPoint(tapPoint));
}
答案 3 :(得分:1)
大多数方法都涉及一对复杂的UIView
和UIWebView
子类以及overrode -touchesBegan:withEvent:
等方法。
This JavaScript-based approach拦截了对网络DOM本身的触摸,这似乎是回避更复杂过程的一种聪明方式。我自己没有尝试过,但我很想知道结果,如果你试一试。
答案 4 :(得分:1)
我不确定具体的实现细节,但您需要子类化UIWindow并覆盖sendEvent:。然后,您可以捕获点击事件并相应地处理它们,然后再进入Web视图。希望这能指出你正确的方向!
答案 5 :(得分:1)
创建一个包含整个UIView
的{{1}}子类。然后触摸webview时将触发事件hitTest。注意:除了webView之外,不要向容器中放任何东西。
UIWebView
然后覆盖hitTest事件:
@interface myWebViewContainer : UIView
... ...
@end
答案 6 :(得分:0)
一个更简单的方法,因为iOS 3.2只是使用UITapGestureRecognizer。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UITapGestureRecognizer* tapGestureRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(userDidTapWebView)] autorelease];
[self.pdfWebView addGestureRecognizer:tapGestureRecognizer];
}
- (void)userDidTapWebView {
NSLog(@"TAP");
}