学习计划(3,4) - WKWebview实现浏览大图并保存本地

由于东西比较多,昨天写好之后也没来得及写文章,所以就一起发布了吧。
今天会涉及的内容包括:

1. 为WKWebview添加进度条
2. 实现一个图片浏览器,并且可以保存本地
3. 实现打开网页,获取所有可用图片
网页加载.gif
最终效果.gif

为WKWebview添加进度条

这个其实比较简单:

1. 将UIProgressView添加到WKWebview中
2. 监听WKWebview的estimatedProgress属性值变化
3. 将estimatedProgress的数据同步到progressView的progress值
4. 移除监听 
5. 自定义一点特效啥的。这个大家怎么开心怎么来
第一步:
初始化UIProgressView
- (UIProgressView *)progressView{
    if(!_progressView){
        _progressView = [[UIProgressView alloc] initWithFrame:(CGRect){0,0,kSCREENWIDTH,0.5}];
        CGAffineTransform transform = CGAffineTransformMakeScale(0.5f, 0.5f);
        _progressView.transform = transform;//设定宽高 
       //设置进度条视图的背景色
        [_progressView setTrackTintColor:hexStrColor(@"#FAFAFA")];
    }return _progressView;
}

第二步:
[self addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];

第三步:
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context{
    
    if ([keyPath isEqualToString:@"estimatedProgress"]) {
       //设置进度条视图的值等于网页加载进度
        self.progressView.progress = self.estimatedProgress;
        if (self.progressView.progress == 1) {
            //如果进度条完成了,设置动画淡出效果
            [UIView animateWithDuration:0.25f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
                self.progressView.transform = CGAffineTransformMakeScale(1.0f, 0.5f);
            } completion:^(BOOL finished) {
                [UIView animateWithDuration:0.2 animations:^{
                    self.progressView.alpha = 0; 
                }];
            }];
        }
    }
}

第四步:移除监听
- (void)dealloc {
    [self removeObserver:self forKeyPath:@"estimatedProgress"];
}

这里要说的就是为什么要用到transform形变.因为你直接在frame中设置它的宽,对于它来说没有影响,它是有默认值的。
那么通过transform形变的设置,我们就可以设置它的高度了。

实现图片浏览器

我们先看一下我们最终的效果


图片浏览器.gif

分析

整个图片浏览器的组成是:


层次.png

从图中就可以简单看出来了:

UIScrollView+若干UIImageView 形成了图片的浏览
UIView+UIButton形成了底层按钮的返回和保存按钮。

视图拼凑起来很简单,所以我们就来分析它的功能:

对外方法的实现

一切都要根据具体的工作需求来定,而我目前的需求是 得到网页中的所有可用图片,并且显示当前点击的图片。
所以,我们需要导入所有的图片地址,和当前点击的图片地址。

为什么是图片地址而不是加载好所有图片传过来呢?

因为,在实际使用中,流量可是很值钱的啊,你一下全给加载好了,很多人家又不需要看的,你噼里啪啦的全给搞好了,这就有点尴尬了。
所以最终的结果就是这样的:


/**
 快速构建图片浏览器

 @param images 图片列表(储存图片下载地址)
 @param selectedImagePath 图片地址
 @return 
 */
+ (instancetype)browseImages:(NSArray *)images
           selectedImagePath:(NSString *)selectedImagePath;

而为什么选中的图片传的是路径却不是坐标尼,因为我哪知道你点的是哪个,除非你网页中设置的好好的。但是我自己实现比较麻烦尼,所以我还缺个前端高手,最好是个萌妹子尼(请祝福我回去不要跪榴莲)。

UIImageView图片如何自适应

关于这个问题,说白就是多读书,多看源码,少玩游戏,少睡觉。因为这玩意完全就是属性值啊。

//适配屏幕
[imageView setContentScaleFactor:[[UIScreen mainScreen] scale]];
//设置图片的显示模式 
 imageView.contentMode =  UIViewContentModeScaleAspectFit;

下面附送的是具体的模式枚举
typedef NS_ENUM(NSInteger, UIViewContentMode) {
//图片拉伸填充至整个UIImageView(图片可能会变形)默认的属性
    UIViewContentModeScaleToFill,
//图片拉伸至完全显示在UIImageView(图片不会变形)
    UIViewContentModeScaleAspectFit,
//图片拉伸至图片的的宽度或者高度等于UIImageView的宽度或者高度为止.看图片的宽高哪一边最接近UIImageView的宽高,一个属性相等后另一个就停止拉伸.
    UIViewContentModeScaleAspectFill,
//调用setNeedsDisplay 方法时,就会重新渲染图片
//下面的属性都是不会拉伸图片的
    UIViewContentModeRedraw,
//中间模式
    UIViewContentModeCenter, 
//顶部
    UIViewContentModeTop,
//底部
    UIViewContentModeBottom,
//左边
    UIViewContentModeLeft,
//右边
    UIViewContentModeRight,
//左上
    UIViewContentModeTopLeft,
//右上
    UIViewContentModeTopRight,
//左下
    UIViewContentModeBottomLeft,
//右下
    UIViewContentModeBottomRight,
};
imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
autoresizingMask的属性,它对应的是一个枚举的值(如下),属性的意思就是自动调整子控件与父控件中间的位置,宽高。
enum {
  //就是不自动调整
   UIViewAutoresizingNone                 = 0,
  //自动调整与superView左边的距离,保证与superView右边的距离不变。
   UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
   //自动调整自己的宽度,保证与superView左边和右边的距离不变
   UIViewAutoresizingFlexibleWidth        = 1 << 1,
   //自动调整与superView左边的距离,保证与左边的距离和右边的距离和原来距左边和右边的距离的比例不变。
   UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
   UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
   //自动调整自己的高度,保证与superView顶部和底部的距离不变
   UIViewAutoresizingFlexibleHeight       = 1 << 4,
   //自动调整与superView底部的距离,也就是说,与superView顶部的距离不变。
   UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
裁剪边缘什么的就比较常见.主要用于一些奇形怪状的图片.
imageView.clipsToBounds  = YES;
如何实现图片的下载
这个就比较简单的通过异步加载,然后设置图片的时候别忘记将其放在主线程中。不然会导致加载延迟。毕竟异步线程它比较任性吧。
- (void)downloadImageWithPath:(NSString *)imagePath
                    imageView:(UIImageView *)imageView{
    [SVProgressHUD show];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"downloadUrl:%@",imagePath);
        NSURL *downloadUrl = [NSURL URLWithString:imagePath];
        NSData *data = [NSData dataWithContentsOfURL:downloadUrl];
        UIImage *image = [UIImage imageWithData:data] ;
        dispatch_async(dispatch_get_main_queue(), ^{
            if(image){
                [SVProgressHUD dismiss];
                [imageView setImage:image];
            }else{//如果是选择一进来就全部加载的,需要把这句话去掉,不然会加载成功了
                //但仍然会显示加载失败.
                [SVProgressHUD showErrorWithStatus:@"加载失败"];
            }
        });
    });
}
图片的加载

这里可以选择,进入界面之后,加载全部内容,那么就需要在添加UIImageView的时候,利用上述方法设置图片了。
那么我选择的是每次滑动的时候去加载图片,如果已经加载的图片就不用再加载了。
那么我们就需要设置UIScrollView的代理方法

//滑动完成的时候执行
- (void)scrollViewDidScroll:(UIScrollView *)scrollView

我们通过该方法在每次滑动完成的时候去加载,先获取当前滑动位置的图片视图对象,然后进行加载就可以了。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    [SVProgressHUD dismiss];
    NSInteger index = scrollView.contentOffset.x/kSCREENWIDTH;
    UIImageView *imageView = (UIImageView *)scrollView.subviews[index];
    self.selectedImageView = imageView;
    //翻动视图的时候加载图片
    if(!imageView.image)
        [self downloadImageWithPath:self.image_list[index] imageView:imageView];
}
如何保存图片

在写之前请在info.plist文件中加入访问本地图片权限,不然会崩溃的哦

Privacy - Photo Library Usage Description

将相机保存到本地的方法有很多种:
https://www.jianshu.com/p/bf20733ba19b
这位小哥总结了一下,我就不做重复的事了(虽然也做了不少,能懒就懒是我自己给自己插的flag)
然后我就使用了引用Photos库的方法

想要使用的话记得导入:

#import <Photos/Photos.h>

下面的方法使用起来就比价简单了,当然我对它的实现原理更加感兴趣,找个时间把原理实现添加到计划中吧。嗯,就这么愉快的说定了。

#pragma mark 下载图片
- (void)downLoadButtonAction{
    NSInteger index = self.scrollView.contentOffset.x/kSCREENWIDTH;
    UIImageView *imageView = (UIImageView *)self.scrollView.subviews[index];
    UIImage *image = imageView.image;
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
            //写入图片到相册
            PHAssetChangeRequest *req = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
    } completionHandler:^(BOOL success, NSError * _Nullable error) {
        if(success){
            [SVProgressHUD showSuccessWithStatus:@"保存成功"];
        }else{
            [SVProgressHUD showErrorWithStatus:@"保存失败"];
        }
    }];
}

关于浏览器部分基本就结束了,还有一个就是记得给UIScrollView添加一个点击就消失的手势事件:

//为滚动视图添加点击消失的事件
 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backAction)];
 [_scrollView addGestureRecognizer:tap];

如何获取网页中的所有图片

这一个步骤就得交给代理来做了。
首先我们就需要实现

//网页加载完成
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;

在真实的情况下,有很多图片貌似获取不到,获取的里面也包含很多的空串,所以我们需要把得到的图片列表过滤一下。所以之前说的前端萌妹子是多么重要。

static  NSString * const imagesJS =
    @"function getImages(){\
    var objs = document.getElementsByTagName(\"img\");\
    var imgScr = '';\
    for(var i=0;i<objs.length;i++){\
    imgScr = imgScr + objs[i].src + '+';\
    };\
    return imgScr;\
    };";
    
    [webView evaluateJavaScript:imagesJS completionHandler:nil];
    [webView evaluateJavaScript:@"getImages()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSArray *urlArray = [NSMutableArray arrayWithArray:[result componentsSeparatedByString:@"+"]];
        //里面如果有空的需要过滤掉
        if(self.image_list.count==0){
            for (NSString *tempURL in urlArray) {
                if(!kStringIsEmpty(tempURL)){
                    [self.image_list addObject:tempURL];
                }
            }
        }
    }];

如何设置点击事件

点击事件,我觉得还是通过与前端的配合,他设置好之后我们直接调用比较好一点,然而我并没有这样可爱的萌妹子,所以,只能用js获取并且给它设置点击事件了。

//这段代码是显示单独的图片。
[webView evaluateJavaScript:@"function registerImageClickAction(){\
     var imgs = document.getElementsByTagName('img');\
     for(var i=0;i<imgs.length;i++){\
     imgs[i].customIndex = i;\
     imgs[i].onclick=function(){\
     window.location.href=''+this.src;\
     }\
     }\
     }" completionHandler:nil];
    
    [webView evaluateJavaScript:@"registerImageClickAction();"completionHandler:nil];

设置了之后,我们就可以看到点击图片然后显示的场景了。然而我们需要自己的图片浏览器啊,所有我们就要拦截点击事件,拦截到之后换上我们自己的控件进行显示。

//在发送请求之前,决定是否跳转,
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    
    //如果是跳转一个新页面
    if (navigationAction.targetFrame.request != nil) {
        NSString *selectedImgURL = navigationAction.request.URL.absoluteString;
        self.imageBrowseView = [GWImageBrowseView browseImages:self.image_list selectedImagePath:selectedImgURL];
        [self.view addSubview:self.imageBrowseView];
         decisionHandler(WKNavigationActionPolicyCancel);
    }else{
       decisionHandler(WKNavigationActionPolicyAllow);
    }
}

DEMO:https://github.com/yanggenwei/GWWebviewPro
可能还不太完善,等我完善了我会更新的哦。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容