iphone - UIWebView - Enabling Action Sheets on <img> tags -


is me or has action sheet on <img> tags been disabled in uiwebview? in safari, e.g, when want save image locally, touch , hold on image action sheet shown. it's not working in custom uiwebview. mean, still working <a> tags, i.e, when touch , hold on html links, action sheet shows up. not <img> tags.

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

so question is, has default action sheet callout <img> tags been disabled uiwebview? so, there way re-enable it? i've googled around , saw many q&as on how disable in uiwebview, me aren't seeing popup?

thanks in advance!

yes apple has disabled feature (among others) in uiwebviews , kept safari only.

however can recreate extending tutorial, http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/.

once you've finished tutorial you'll want add few extra's can save images (which tutorial doesn't cover). added notification called @"tapandholdshortnotification" after 0.3 seconds calls method disable callout code in (to prevent both default , own menu popping while page still loading, little bug fix).

also detect images you'll need extend jstools.js, here's mine 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; } 

now can detect user clicking on images , find out images url clicking on, need change -(void)opencontextualmenuatpoint: method provide options.

again here's mine (i tried copy safari's behaviour this):

- (void)opencontextualmenuat:(cgpoint)pt{     // load javascript code resources , inject web page     nsstring *path = [[nsbundle mainbundle] pathforresource:@"jstools" oftype:@"js"];     nsstring *jscode = [nsstring stringwithcontentsoffile:path encoding:nsutf8stringencoding error:nil];     [webview stringbyevaluatingjavascriptfromstring:jscode];      // tags @ 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 image 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 link 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]; } 

(notes: selectedlinkurl , selectedimageurl declared in .h file let them accessed throughout class, saving or opening link latter.

so far we've been going on tutorials code making changes move tutorial doesn't cover (it stops before mentioning how handle saving images or opening links).

to handle users choice need add 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];     } } 

this checks user wants , handles /most/ of them, "save image" operation needs method handle that. progress used mbprogresshub. add mbprogresshub *progresshud; interface declaration in .h , set in init method (of whatever class you're handling 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; 

and -(void)saveimageurl:(nsstring*)url; method save image library. (a better way download through nsurlrequest , update progress hud in mbprogresshudmodedeterminate deflect how long it'll take download, more hacked implementation 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]; } 

and of cause add [progresshud release]; dealloc method.

hopefully shows how add of options webview apple left out. of cause though can add more things "read later" option instapaper or "open in safari" button. (looking @ length of post i'm seeing why original tutorial left out finial implementation details)

edit: (updated more info)

i asked detail glossed on @ top, @"tapandholdshortnotification", clarifying it.

this uiwindow subclass, adds second notification cancel default selection menu (this because when tried tutorial 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 make sure event processed usual      if ([touches count] == 1) { // we're interested in one-finger events         uitouch *touch = [touches anyobject];          switch ([touch phase]) {             case uitouchphasebegan:  // finger touched 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 touching screen         [contextualmenutimer invalidate];         contextualmenutimer = nil;     }     [touches release]; } 

the notification handled 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';"]; } 

it's little change fixes annoying little bug 2 menus appear (the standard 1 , yours).

also add ipad support sending touches location notification fires , showing uiactionsheet point, though written before ipad doesn't include support that.


Comments