UIWebView - 在<img>标签上启用动作表

K J*_*K J 14 iphone uiwebview ios4 ios

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

我尝试过放入img { -webkit-touch-callout: inherit; }css这样的东西,但是没用.另一方面,当我双击并按住图像时,会出现一个复制气球.

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

提前致谢!

Kyl*_*lls 27

是苹果已在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;
}
Run Code Online (Sandbox Code Playgroud)

现在您可以检测用户点击图像并实际找到他们点击的图像网址,但我们需要更改 - (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];
}
Run Code Online (Sandbox Code Playgroud)

(注意: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];
    }
}
Run Code Online (Sandbox Code Playgroud)

这将检查用户想要做什么以及处理/大多数/他们,只有"保存图像"操作需要另一种方法来处理它.为了进步,我使用了MBProgressHub.添加MBProgressHUB*progressHud; 到.h中的接口声明并在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;
Run Code Online (Sandbox Code Playgroud)

和 - (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];
}
Run Code Online (Sandbox Code Playgroud)

并且添加[progressHud release]; 对dealloc方法.

希望这能告诉您如何将一些选项添加到苹果遗漏的webView中.虽然您可以添加更多内容,例如instapaper的"Read Later"选项或"Open In 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];
}
Run Code Online (Sandbox Code Playgroud)

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

// in -viewDidLoad

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stopSelection:) name:@"TapAndHoldShortNotification" object:nil];


- (void)stopSelection:(NSNotification*)notification{
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
}
Run Code Online (Sandbox Code Playgroud)

它只是一个小小的变化,但它修复了烦人的小虫子,你会看到2个菜单(标准的和你的).

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

  • 这很好用.谢谢,谢天谢地! (2认同)