是我一个人还是在 <img>
上有操作表?标签在 UIWebView 中被禁用了吗?例如,在 Safari 中,当您想在本地保存图像时,您可以触摸并按住图像以显示操作表。但它在我的自定义 UIWebView 中不起作用。我的意思是,它仍在为 <a>
工作标签,即当我触摸并按住 html 链接时,会显示一个操作表。但不适用于 <img>
标签。
我已经尝试过像把 img { -webkit-touch-callout: inherit; }
在 css 中,这是行不通的。另一方面,当我双击并按住图像时,会出现一个复制气球。
所以问题是,有 <img>
的默认操作表标注标签已为 UIWebView 禁用?是这样,有没有办法重新启用它?我四处搜索,看到许多关于如何在 UIWebView 中禁用它的问答,那么只有我没有看到弹出窗口吗?
提前致谢!
最佳答案
是的,苹果已经在 UIWebViews 中禁用了这个功能(以及其他功能),并只为 Safari 保留它。
但是您可以通过扩展本教程自己重新创建它,http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/ .
完成本教程后,您需要添加一些额外内容,以便实际保存图像(本教程未涵盖)。 我在 0.3 秒后添加了一个名为 @"tapAndHoldShortNotification"的额外通知,它调用一个方法,其中仅包含禁用标注代码(以防止默认菜单和您自己的菜单在页面仍在加载时弹出,修复了一个小错误)。
此外,为了检测图像,您还需要扩展 JSTools.js,这是我的,具有额外的功能。
function MyAppGetHTMLElementsAtPoint(x,y) {
var tags = ",";
var e = document.elementFromPoint(x,y);
while (e) {
if (e.tagName) {
tags += e.tagName + ',';
}
e = e.parentNode;
}
return tags;
}
function MyAppGetLinkSRCAtPoint(x,y) {
var tags = "";
var e = document.elementFromPoint(x,y);
while (e) {
if (e.src) {
tags += e.src;
break;
}
e = e.parentNode;
}
return tags;
}
function MyAppGetLinkHREFAtPoint(x,y) {
var tags = "";
var e = document.elementFromPoint(x,y);
while (e) {
if (e.href) {
tags += e.href;
break;
}
e = e.parentNode;
}
return tags;
}
现在您可以检测用户点击图片并实际找出他们点击的图片 url,但我们需要更改 -(void)openContextualMenuAtPoint: 方法以提供额外选项。
这也是我的(我试图为此复制 Safari 的行为):
- (void)openContextualMenuAt:(CGPoint)pt{
// Load the JavaScript code from the Resources and inject it into the web page
NSString *path = [[NSBundle mainBundle] pathForResource:@"JSTools" ofType:@"js"];
NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:jsCode];
// get the Tags at the touch location
NSString *tags = [webView stringByEvaluatingJavaScriptFromString:
[NSString stringWithFormat:@"MyAppGetHTMLElementsAtPoint(%i,%i);",(NSInteger)pt.x,(NSInteger)pt.y]];
NSString *tagsHREF = [webView stringByEvaluatingJavaScriptFromString:
[NSString stringWithFormat:@"MyAppGetLinkHREFAtPoint(%i,%i);",(NSInteger)pt.x,(NSInteger)pt.y]];
NSString *tagsSRC = [webView stringByEvaluatingJavaScriptFromString:
[NSString stringWithFormat:@"MyAppGetLinkSRCAtPoint(%i,%i);",(NSInteger)pt.x,(NSInteger)pt.y]];
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
selectedLinkURL = @"";
selectedImageURL = @"";
// If an image was touched, add image-related buttons.
if ([tags rangeOfString:@",IMG,"].location != NSNotFound) {
selectedImageURL = tagsSRC;
if (sheet.title == nil) {
sheet.title = tagsSRC;
}
[sheet addButtonWithTitle:@"Save Image"];
[sheet addButtonWithTitle:@"Copy Image"];
}
// If a link is pressed add image buttons.
if ([tags rangeOfString:@",A,"].location != NSNotFound){
selectedLinkURL = tagsHREF;
sheet.title = tagsHREF;
[sheet addButtonWithTitle:@"Open"];
[sheet addButtonWithTitle:@"Copy"];
}
if (sheet.numberOfButtons > 0) {
[sheet addButtonWithTitle:@"Cancel"];
sheet.cancelButtonIndex = (sheet.numberOfButtons-1);
[sheet showInView:webView];
}
[selectedLinkURL retain];
[selectedImageURL retain];
[sheet release];
}
(注意:selectedLinkURL 和 selectedImageURL 在 .h 文件中声明,以便在整个类中访问它们,以保存或打开后者的链接。
到目前为止,我们只是回顾教程代码进行更改,但现在我们将进入教程未涵盖的内容(它在实际提到如何处理保存图像或打开链接之前停止)。
为了处理用户的选择,我们现在需要添加 actionSheet:clickedButtonAtIndex: 方法。
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:@"Open"]){
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:selectedLinkURL]]];
}
else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:@"Copy"]){
[[UIPasteboard generalPasteboard] setString:selectedLinkURL];
}
else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:@"Copy Image"]){
[[UIPasteboard generalPasteboard] setString:selectedImageURL];
}
else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:@"Save Image"]){
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(saveImageURL:) object:selectedImageURL];
[queue addOperation:operation];
[operation release];
}
}
这会检查用户想要做什么并处理/大多数/他们,只有“保存图像”操作需要另一种方法来处理。对于进度,我使用了 MBProgressHub。 添加一个 MBProgressHUB *progressHud;到 .h 中的接口(interface)声明并在 init 方法中设置它(无论你从哪个类处理 webview)。
progressHud = [[MBProgressHUD alloc] initWithView:self.view];
progressHud.customView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Tick.png"]] autorelease];
progressHud.opacity = 0.8;
[self.view addSubview:progressHud];
[progressHud hide:NO];
progressHud.userInteractionEnabled = NO;
还有 -(void)saveImageURL:(NSString*)url;方法实际上会将其保存到图像库中。 (更好的方法是通过 NSURLRequest 进行下载并更新 MBProgressHUDModeDeterminate 中的进度 hud 以偏离下载实际需要多长时间,但这是一个更复杂的实现)
-(void)saveImageURL:(NSString*)url{
[self performSelectorOnMainThread:@selector(showStartSaveAlert) withObject:nil waitUntilDone:YES];
UIImageWriteToSavedPhotosAlbum([UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]], nil, nil, nil);
[self performSelectorOnMainThread:@selector(showFinishedSaveAlert) withObject:nil waitUntilDone:YES];
}
-(void)showStartSaveAlert{
progressHud.mode = MBProgressHUDModeIndeterminate;
progressHud.labelText = @"Saving Image...";
[progressHud show:YES];
}
-(void)showFinishedSaveAlert{
// Set custom view mode
progressHud.mode = MBProgressHUDModeCustomView;
progressHud.labelText = @"Completed";
[progressHud performSelector:@selector(hide:) withObject:[NSNumber numberWithBool:YES] afterDelay:0.5];
}
当然要添加 [progressHud release];到 dealloc 方法。
希望这向您展示了如何向 webView 添加一些 apple 遗漏的选项。 当然,您可以向其中添加更多内容,例如 instapaper 的“稍后阅读”选项或“在 Safari 中打开”按钮。 (看看这篇文章的长度,我明白了为什么原始教程遗漏了最终的实现细节)
编辑:(更新了更多信息)
我被问及我在顶部掩盖的细节,@“tapAndHoldShortNotification”,所以这是澄清它。
这是我的 UIWindow 子类,它添加了第二个通知以取消默认选择菜单(这是因为当我尝试教程时它显示了两个菜单)。
- (void)tapAndHoldAction:(NSTimer*)timer {
contextualMenuTimer = nil;
UIView* clickedView = [self hitTest:CGPointMake(tapLocation.x, tapLocation.y) withEvent:nil];
while (clickedView != nil) {
if ([clickedView isKindOfClass:[UIWebView class]]) {
break;
}
clickedView = clickedView.superview;
}
if (clickedView) {
NSDictionary *coord = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:tapLocation.x],@"x",
[NSNumber numberWithFloat:tapLocation.y],@"y",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:@"TapAndHoldNotification" object:coord];
}
}
- (void)tapAndHoldActionShort:(NSTimer*)timer {
UIView* clickedView = [self hitTest:CGPointMake(tapLocation.x, tapLocation.y) withEvent:nil];
while (clickedView != nil) {
if ([clickedView isKindOfClass:[UIWebView class]]) {
break;
}
clickedView = clickedView.superview;
}
if (clickedView) {
NSDictionary *coord = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:tapLocation.x],@"x",
[NSNumber numberWithFloat:tapLocation.y],@"y",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:@"TapAndHoldShortNotification" object:coord];
}
}
- (void)sendEvent:(UIEvent *)event {
NSSet *touches = [event touchesForWindow:self];
[touches retain];
[super sendEvent:event]; // Call super to make sure the event is processed as usual
if ([touches count] == 1) { // We're only interested in one-finger events
UITouch *touch = [touches anyObject];
switch ([touch phase]) {
case UITouchPhaseBegan: // A finger touched the screen
tapLocation = [touch locationInView:self];
[contextualMenuTimer invalidate];
contextualMenuTimer = [NSTimer scheduledTimerWithTimeInterval:0.8 target:self selector:@selector(tapAndHoldAction:) userInfo:nil repeats:NO];
NSTimer *myTimer;
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(tapAndHoldActionShort:) userInfo:nil repeats:NO];
break;
case UITouchPhaseEnded:
case UITouchPhaseMoved:
case UITouchPhaseCancelled:
[contextualMenuTimer invalidate];
contextualMenuTimer = nil;
break;
}
} else { // Multiple fingers are touching the screen
[contextualMenuTimer invalidate];
contextualMenuTimer = nil;
}
[touches release];
}
然后像这样处理通知:
// in -viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stopSelection:) name:@"TapAndHoldShortNotification" object:nil];
- (void)stopSelection:(NSNotification*)notification{
[webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
}
这只是一个小改动,但它修复了出现 2 个菜单(标准菜单和您的菜单)的恼人小错误。
您还可以轻松地添加 iPad 支持,方法是在通知触发时发送触摸位置,然后从该点显示 UIActionSheet,尽管这是在 iPad 之前编写的,因此不包括对此的支持。
关于iphone - UIWebView - 在 <img> 标签上启用操作表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5163831/