美文网首页IOS开发程序员
UIWebView/WKWebView对<input ty

UIWebView/WKWebView对<input ty

作者: frog78 | 来源:发表于2017-07-28 09:26 被阅读1134次

           在iOS开发中,经常会用到UIWebView/WKWebView来加载Html5。特别是随着Hybrid开发的流行,有的公司直接砍掉大部分的Native开发,转而大量采用H5页面替代Native页面。这就要多研究研究UIWebView/WKWebView了。本文主要针对Hybrid开发中遇到的一个小问题,提供一个解决方案。

            当H5中含有<input type=file>标签时,点击选择文件按钮会默认弹出Native的文件选择菜单,包含相机拍照、从相册选择两个选项。这是由于系统对<input type=file>对标签进行了监听,并做了处理。

    Html5文件 默认弹出窗口

            现在项目中不满足于这两个选项,如果想自己加一个,或者做任何自己想要的定制,就需要捕获这个<input type=file>标签,并在事件里面实现自己想要实现的功能。那么如何捕获这个标签呢?用UIWebView/WKWebView的代理是不行的,没有哪个代理方法会回调这个标签的监听事件。下面提供一种解决方案。

          主要思路是,虽然拦截不了js发给Native的通知,但是可以通过Runtime拦截Native弹出窗口,因为知道这个窗口是被present出来的。通过断点,可以看到,对于UIWebView来说,present的的是UIDocumentMenuViewController,并通过其代理UIWebFileUploadPanel完成文件的上传。WKWebView也是类似的。

    断点查看方法调用

            因此可以通过Runtime来hook出UIViewController的presentViewController方法,拿到将要被present的UIViewController,并判断其类型,如果是UIDocumentMenuViewController类型且其代理为UIWebFileUploadPanel(或者WKFileUploadPanel),将present方法return掉,不让他弹出来;如果不是这种类型的,才让present。如下所示:

    - (void)gigi_presentViewController:(UIViewController*)viewControllerToPresent animated:(BOOL)flag completion:(void(^)(void))completion {

          //如果present的viewcontroller是UIDocumentMenuViewController类型,且代理是WKFileUploadPanel或UIWebFileUploadPanel进行拦截

    if([viewControllerToPresent isKindOfClass:[UIDocumentMenuViewController class]]) {

            UIDocumentMenuViewController*dvc =     (UIDocumentMenuViewController*)viewControllerToPresent;

           if([dvc.delegateisKindOfClass:NSClassFromString(@"WKFileUploadPanel")] ||                      [dvc.delegateisKindOfClass:NSClassFromString(@"UIWebFileUploadPanel")]) {

                 self.isFileInputIntercept=YES;

                  dispatch_async(dispatch_get_main_queue(), ^{

                        [self onFileInputIntercept];

                 });

                 return;

            }

       }

      //正常情况下的present

     [selfgigi_presentViewController:viewControllerToPresentanimated:flagcompletion:completion];

    }

            并在return之前执行想要自己实现的代码,做自己想干的事。在这里执行了一个[self onFileInputIntercept]方法,把拦截传递出去。这样就大功告成了吗?不是的。尝试一下,发现,第一次点击时,阻止默认窗口弹出来是可以的,但是,第二次时,就不会调用present方法了,因此就无法进行拦截。

            究其原因,发现默认窗口弹出后,在窗口消失时,调用了dismisViewControllerAnimated这个回调,并执行了它的bolck completion。如果执行了这个block,第二次就能够正常拦截;如果不执行block,第二次就无法拦截。这个completion到底是怎么实现的,不得而知,因为是系统内部实现的,看不到源码,但是也不需要知道。只需要知道它是一定要执行的就行。那么怎么来执行这个block呢。没错,可以通过UIDocumentMenuViewController来模拟取消,加上这关键的一句:

    [dvc.delegate documentMenuWasCancelled:dvc];

            来模拟窗口被取消,从而执行那个至关重要的completion block。那么在dismisViewControllerAnimated也要做一些处理,如下所示:

    - (void)gigi_dismissViewControllerAnimated:(BOOL)flag completion:(void(^)(void))completion {

            //如果进行了拦截,禁止当前viewcontroller的dismiss

            if(self.isFileInputIntercept) {

                    self.isFileInputIntercept=NO;

                    completion();

                    return;

            }

            //正常情况下viewcontroller的dismiss

            [selfgigi_dismissViewControllerAnimated:flagcompletion:^{

                    if(completion) {

                           completion();

                    }

            }];

    }

            至此才大功告成,对<input type=file>进行了有效的拦截。

            最后,附上本文的Demo代码地址:https://github.com/frog78/Gigi

    参考文章:

    http://news.91.com/mip/s594a8f1b155c.html


    相关文章

      网友评论

      • Kael_Thas_:自定义相机和相册后,选择出的图片怎么传给网页呢?
        Kael_Thas_:@frog78 明白了,谢谢
        frog78:有两种思路:
        1、配合混合开发框架使用,通过混合开发框架和Web交互。
        2、或者研究一下UIWebFileUploadPanel/WKFileUploadPanel,看有没有回传路径的方法。
        我们用的第一种。
      • playboy:方法不错,最近也碰到这个麻烦的问题了
        playboy:@Kael_Thas_ 图片可以作为字符串传回给网页
        Kael_Thas_:选择图片后你是怎么传回网页的?

      本文标题:UIWebView/WKWebView对<input ty

      本文链接:https://www.haomeiwen.com/subject/zkgakxtx.html