V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
stephenliubp
V2EX  ›  iDev

长按 UIWebView 上的图片保存到相册

  •  
  •   stephenliubp ·
    FantasticLBP · 2017-08-10 10:20:39 +08:00 · 2937 次点击
    这是一个创建于 2663 天前的主题,其中的信息可能已经有所发展或是发生改变。

    不知道各位对于这个需求要如何解决?

    可能有些人会想到 js 与原生交互,js 监听图片点击事件,然后将图片的 url 传递给原生 App 端,然后原生 App 将图片保存到相册,这样子麻烦吗?超麻烦。( 1 )、js 监听图片长按事件;( 2 )、js 将图片 url 传递给原生;( 3 )、原生通过图片的 url 生成 UIImage ;( 4 )、保存 UIImage 到系统相册,巨麻烦啊,大哥,我很懒的好不好

    那么问题跑出来了,怎么办最简单?

    • 鉴于个人道行尚浅,我就将自己的想法说出来

    • 有个 js 的 api:Document.elementFromPoint()

    TheelementFromPoint()method of theDocumentinterface returns the topmost element at the specified coordinates.

    所以根据这个提示,我们完全可以只在 App 原生端做一些代码开发,实现这个需求

    开发步骤

    • 给 UIWebView 添加长按手势
    • 监听手势动作,拿到坐标点(x,y)
    • 弹出对话框,是否保存到相册
    • UIWebView 注入 js:Document.elementFromPoint(x,y).src 拿到 img 标签的 src
    • 拿到图片的 url,生成 UIImage
    • 图片保存到相册

    有巨坑

    • 长按手势事件不能每次都响应,据我猜测 UIWebView 本身就有很多事件,所以实现下 UIGestureRecognizerDelegate 代理方法。长按手势准确率 100%
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
        return YES;
    }
    
    //
    //  ViewController.m
    //  WebView 长按图片保存到相册
    //
    //  Created by 杭城小刘 on 2017/8/2.
    //  Copyright © 2017 年 杭城小刘. All rights reserved.
    //
    
    #import "ViewController.h"
    
    @interface ViewController ()<UIGestureRecognizerDelegate,NSURLSessionDelegate>
    @property (weak, nonatomic) IBOutlet UIWebView *webView;
    
    @end
    
    @implementation ViewController
    
    #pragma mark -- life cycle
    - (void)viewDidLoad{
        [super viewDidLoad];
        
        NSString *htmlURL = [[NSBundle mainBundle] pathForResource:@"saveImage" ofType:@"html"];
        [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:htmlURL]]];
        //给 UIWebView 添加手势
        UILongPressGestureRecognizer* longPressed = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressed:)];
        longPressed.delegate = self;
        [self.webView addGestureRecognizer:longPressed];
    }
    
    #pragma mark -- UIGestureRecognizerDelegate
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
        UIActivityTypeAddToReadingList
        return YES;
    }
    
    - (void)longPressed:(UILongPressGestureRecognizer*)recognizer{
        if (recognizer.state != UIGestureRecognizerStateBegan) {
            return;
        }
        CGPoint touchPoint = [recognizer locationInView:self.webView];
        NSString *imgURL = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
        NSString *urlToSave = [self.webView stringByEvaluatingJavaScriptFromString:imgURL];
        if (urlToSave.length == 0) {
            return;
        }
        
        UIAlertController *alertVC =  [UIAlertController alertControllerWithTitle:@"大宝贝儿" message:@"你真的要保存图片到相册吗?" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"真的啊" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                [self saveImageToDiskWithUrl:urlToSave];
        }];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"大哥,我点错了,不好意思" style:UIAlertActionStyleDefault handler:nil];
        [alertVC addAction:okAction];
        [alertVC addAction:cancelAction];
        [self presentViewController:alertVC animated:YES completion:nil];
    }
    
    #pragma mark - private method
    - (void)saveImageToDiskWithUrl:(NSString *)imageUrl{
        NSURL *url = [NSURL URLWithString:imageUrl];
        
        NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        
        NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue new]];
        
        NSURLRequest *imgRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30.0];
        
        NSURLSessionDownloadTask  *task = [session downloadTaskWithRequest:imgRequest completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (error) {
                return ;
            }
            NSData * imageData = [NSData dataWithContentsOfURL:location];
            dispatch_async(dispatch_get_main_queue(), ^{
                
                UIImage * image = [UIImage imageWithData:imageData];
                UIImageWriteToSavedPhotosAlbum(image, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
            });
        }];
        [task resume];
    }
    
    #pragma mark 保存图片后的回调
    - (void)imageSavedToPhotosAlbum:(UIImage*)image didFinishSavingWithError:  (NSError*)error contextInfo:(id)contextInfo{
        NSString*message =@"嘿嘿";
        if(!error) {
            UIAlertController *alertControl = [UIAlertController alertControllerWithTitle:@"提示" message:@"成功保存到相册" preferredStyle:UIAlertControllerStyleAlert];
            
            UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:nil];
            [alertControl addAction:action];
            [self presentViewController:alertControl animated:YES completion:nil];
        }else{
            message = [error description];
            UIAlertController *alertControl = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
            [alertControl addAction:action];
            [self presentViewController:alertControl animated:YES completion:nil];
        }
    }
    
    @end
    

    附上关键的 js 官方文档:Document.elementFromPoint()

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   6024 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 02:14 · PVG 10:14 · LAX 18:14 · JFK 21:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.