本文介绍了UIWebView - 在 <img> 上启用操作表标签的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

仅仅是我还是 <img> 标签上的操作表在 UIWebView 中被禁用了?例如,在 Safari 中,当您想在本地保存图像时,您可以触摸并按住图像以显示操作表.但它不适用于我的自定义 UIWebView.我的意思是,它仍然适用于 <a> 标签,即当我触摸并按住 html 链接时,会显示一个操作表.但不适用于 <img> 标签.

Is it just me or has the action sheet on <img> tags been disabled in UIWebView? In Safari, e.g, when you want to save an image locally, you touch and hold on the image to get an action sheet shown. But it's not working in my custom UIWebView. I mean, it is still working for <a> tags, i.e, when I touch and hold on html links, an action sheet shows up. But not for the <img> tags.

我尝试过将 img { -webkit-touch-callout: inherit;} 在 css 中,这不起作用.另一方面,当我双击并按住图像时,会出现一个复制气球.

I've tried things like putting img { -webkit-touch-callout: inherit; } in css, which didn't work. On the other hand, when I double-tap and hold on the images, a copy-balloon shows up.

所以问题是,<img> 标签的默认操作表标注是否已为 UIWebView 禁用?是这样,有没有办法重新启用它?我用谷歌搜索了很多关于如何在 UIWebView 中禁用它的问答,所以只有我没有看到弹出窗口吗?

So the question is, has the default action sheet callout for <img> tags been disabled for UIWebView? Is so, is there a way to re-enable it? I've googled around and saw many Q&As on how to disable it in UIWebView, so is it just me who aren't seeing the popup?

提前致谢!

推荐答案

是的,Apple 已经在 UIWebViews 中禁用了此功能(以及其他功能),并仅在 Safari 中保留.

Yes apple has disabled this feature (among others) in UIWebViews and kept it for Safari only.

但是您可以通过扩展本教程自己重新创建它,http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/.

However you can recreate this yourself by extending this tutorial, http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/.

完成本教程后,您需要添加一些额外的内容,以便实际保存图像(本教程未涵盖).我在 0.3 秒后添加了一个名为 @"tapAndHoldShortNotification" 的额外通知,它调用了一个仅包含禁用标注代码的方法(以防止在页面仍在加载时弹出默认菜单和您自己的菜单,修复了一些错误).

Once you've finished this tutorial you'll want to add a few extra's so you can actually save images (which the tutorial doesn't cover).I added an extra notification called @"tapAndHoldShortNotification" after 0.3 seconds which calls a method with just the disable callout code in it (to prevent both the default and your own menu popping while the page is still loading, a little bug fix).

此外,要检测图像,您还需要扩展 JSTools.js,这是我的附加功能.

Also to detect images you'll need to extend the JSTools.js, here's mine with the extra functions.

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

现在您可以检测到用户点击图片并实际找出他们点击的图片网址,但我们需要更改 -(void)openContextualMenuAtPoint: 方法以提供额外的选项.

Now you can detect the user clicking on images and actually find out the images url they are clicking on, but we need to change the -(void)openContextualMenuAtPoint: method to provide extra options.

这又是我的(我试图复制 Safari 的行为):

Again here's mine (I tried to copy Safari's behaviour for this):

- (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 文件中声明,以便在整个类中访问它们,以便稍后保存或打开链接.

(NOTES: selectedLinkURL and selectedImageURL are declared in the .h file to let them be accessed throughout the class, for saving or opening the link latter.

到目前为止,我们刚刚回顾了教程代码进行更改,但现在我们将进入教程未涵盖的内容(它在实际提到如何处理保存图像或打开链接之前停止).

So far we've just been going back over the tutorials code making changes but now we will move into what the tutorial doesn't cover (it stops before actually mentioning how to handle saving the images or opening the links).

为了处理用户的选择,我们现在需要添加 actionSheet:clickedButtonAtIndex: 方法.

To handle the users choice we now need to add the actionSheet:clickedButtonAtIndex: method.

-(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 中的接口声明并在 init 方法中设置它(无论您从哪个类处理 webview).

This checks what the user wants to do and handles /most/ of them, only the "save image" operation needs another method to handle that. For the progress I used MBProgressHub.Add an MBProgressHUB *progressHud; to the interface declaration in the .h and set it up in the init method (of whatever class you're handling the webview from).

    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 中的进度面板,以改变实际下载所需的时间,但这是一个更复杂的实现)

And the -(void)saveImageURL:(NSString*)url; method will actually save it to the image library.(A better way would be to do the download through an NSURLRequest and update the progress hud in MBProgressHUDModeDeterminate to deflect how long it'll actually take to download, but this is a more hacked together implementation then that)

-(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 发布];到 dealloc 方法.

And of cause add [progressHud release]; to the dealloc method.

希望这可以向您展示如何将一些选项添加到苹果遗漏的 webView.当然,尽管您可以为此添加更多内容,例如 instapaper 的稍后阅读"选项或在 Safari 中打开"按钮.(看这篇文章的长度,我明白为什么原始教程遗漏了最终的实现细节)

Hopefully this shows you how to add some of the options to a webView that apple left out.Of cause though you can add more things to this like a "Read Later" option for instapaper or a "Open In Safari" button.(looking at the length of this post I'm seeing why the original tutorial left out the finial implementation details)

(更新了更多信息)

有人问我关于我在顶部掩盖的细节,@tapAndHoldShortNotification",所以这是澄清它.

I was asked about the detail I glossed over at the top, the @"tapAndHoldShortNotification", so this is clarifying it.

这是我的 UIWindow 子类,它添加了第二个通知以取消默认选择菜单(这是因为当我尝试教程时它显示了两个菜单).

This is my UIWindow subclass, it adds the second notification to cancel the default selection menu (this is because when I tried the tutorial it showed both menus).

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

然后按如下方式处理通知:

The notification is then handled like this:

// 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 个菜单(标准菜单和您的菜单).

It's only a little change but it fixes the annoying little bug where you get 2 menus appear (the standard one and yours).

您还可以通过在通知触发时发送触摸位置然后从该点显示 UIActionSheet 来轻松添加 iPad 支持,尽管这是在 iPad 之前编写的,因此不包括对此的支持.

Also you could easily add iPad support by sending the touches location as the notification fires and then showing the UIActionSheet from that point, though this was written before the iPad so doesn't include support for that.

这篇关于UIWebView - 在 &lt;img&gt; 上启用操作表标签的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-06 13:03