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

标签 iphone ios ios4 uiwebview

是我一个人还是在 <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/

相关文章:

ios - 使用自定义 Xib 在 UITableViewCell 上加载数据

iphone - 从存储在服务器上的 XML 文件中检索数据并加载 iPhone

ios - UIPopoverPresentationController 无法设置箭头颜色

ios - 将 ObjC pod 集成到 Swift 框架中的问题

ios - 在 iOS 中,初始 View Controller 始终与 Root View Controller 相同

iphone - 选择选项卡栏项目时的事件

ipad - 如何在 iPad 的较小区域模拟 navigationController/UITableView DrillDown?

iphone - 如何使 subview 随其父 View 自动调整大小?

iphone - 如何暂停视频并显示一个 UIVIEW 并再次继续播放不同的视频

ios - 将 XCode 6 项目导出到 SVN 存储库