WKWebView+UIProgressView的简单封装(OC & Swift)

OC版

初始化

//
//  SQWebView.h
//  UUTravel
//
//  Created by Dev on 2017/3/24.
//  Copyright © 2017年 shaoqing. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface SQWebView : UIView

- (instancetype)initWithFrame:(CGRect)frame withurl:(NSString* )webURLStr;
- (instancetype)initWithFrame:(CGRect)frame withurl:(NSString* )webURLStr and:(NSDictionary*)parameters;

@end

SQWebView继承自UIView,可以像UIButton或UIImageView一样简单方便使用。
两个初始化方法,带参数和不带参数。也只能从这两个初始化方法创建SQWebView实例。所以要在.m文件防止使用init或者initWithFrame初始化方法

- (instancetype)initWithFrame:(CGRect)frame {
    @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"必须使用SQWebView.h文件中声明的初始化方法" userInfo:nil];
}

- (instancetype)init {
    @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"必须使用SQWebView.h文件中声明的初始化方法" userInfo:nil];
}

属性

因为是简单运用,所以前期只有这四个属性,其中parameters是可以为nil,因为加载有些网页不需要参数。

@interface SQWebView ()

@property (nonatomic, strong, nonnull) UIProgressView *progressView; //进度条
@property (nonatomic, strong, nonnull) WKWebView *webView;           //webview
@property (nonatomic, strong, nonnull) NSString *webURLStr;          //requestUrlStr
@property (nonatomic, strong, nullable) NSDictionary *parameters;    //加载url包含参数

@end

自定义初始化方法

- (instancetype)initWithFrame:(CGRect)frame withurl:(NSString* )webURLStr{
    self = [super initWithFrame:frame];
    if (self) {
        _webURLStr = webURLStr.copy;
        _parameters = nil;
        _webView = [self webView];
        _progressView = [self progressView];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame withurl:(NSString* )webURLStr and:(NSDictionary*)parameters{
    self = [super initWithFrame:frame];
    if (self) {
        _webURLStr = webURLStr.copy;
        _parameters = parameters.copy;
        _webView = [self webView];
        _progressView = [self progressView];
    }
    return self;
}

属性的 set 方法

- (UIProgressView *)progressView {
    if (!_progressView) {
        UIProgressView *progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.bounds.size.width, 2.0)];
        _progressView = progressView;
        [self addSubview:_progressView];
    }
    return _progressView;
}

一般加载条宽 2.0,在SQWebview最顶端

- (WKWebView *)webView {
    if (!_webView) {
        WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.bounds.size.width, self.bounds.size.height - 2.0)];
        
        [webView addObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress)) options:NSKeyValueObservingOptionNew context:nil];
        _webView = webView;
        [self addSubview:_webView];
        if (_parameters) {           // 有参数要加载参数
            __block NSString* baseURLStr = self.webURLStr.copy;
            NSArray<NSString *>* parametersKey = [_parameters allKeys];
            [parametersKey enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSString *entity = [NSString stringWithFormat:@"%@%@=%@", idx == 0? @"?":@"&", obj, _parameters[obj]];
                baseURLStr = [baseURLStr stringByAppendingString:entity];
            }];
            [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:baseURLStr]]];
        } else {
            [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.webURLStr]]];
        }
    }
    return _webView;
    
}

addObserver: forKeyPath:方法添加观察webView加载进度的KVO,当webView加载网页进度有变化会触发observeValueForKeyPath:ofObject:...这个方法。相应的可以在这个方法中改变progressView进度,从而达到有进度提示的自定义webView.

KVO回调

#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))] && [object isEqual:self.webView]) {
        if (self.webView.estimatedProgress == 1.0) {
            self.progressView.progress = 0.0;
        } else {
            [self.progressView setProgress:self.webView.estimatedProgress animated:YES];
        }
    } else {
        // Make sure to call the superclass's implementation in the else block in case it is also implementing KVO
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

如果你使用的是UIWebView的话是没有estimatedProgress(加载网页进度)属性,所以用WKWebview比UIWebView实现起来简直不要太方便了

dealloc中移除观察者

最后别忘了把self从webView中移除观察者

#pragma mark - dealloc
- (void)dealloc {
    [self.webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
}

我朋友的一篇博客对UIWebView的简单封装写的不错,大家可以去观摩下webView进度条简单封装

Swift版

前话

思路跟OC版的是一毛一样的,就是换了种语言罢了

定义的属性

    var webURLStr: String
    var parameters: [String : String]?
    
    var webView: WKWebView
    var progressWebView: UIProgressView

只有parameters是定义为可选类型,因为加载URL有不需要参数的情况,所以可以为nil。

构造指定初始化方法(designated init)

    // MARK: - designated init
    init(webURLStr: String, parameters: [String : String]?, frame: CGRect) {
        self.webURLStr = webURLStr
        self.parameters = parameters
        self.webView = WKWebView()
        self.progressWebView = UIProgressView()
        super.init(frame: frame)
        //custom ui
        setupUI()
    }

初始化好四个存储属性,再调用父类designated init,接着设置progressWebView和webView的相关操作

设置webView 添加监听对象

    // MARK: - private
    private func setupUI() {
        //设置point&size
        self.progressWebView.frame = CGRect(x: 0.0, y: 0.0, width: self.bounds.size.width, height: 2.0)
        self.webView.frame = CGRect(x: 0.0, y: 0.0, width: self.bounds.size.width, height: self.bounds.size.height)
        
        self.addSubview(self.webView)
        self.addSubview(self.progressWebView)
        //添加监听webView网页加载进度
        self.webView.addObserver(self, forKeyPath: NSStringFromSelector(#selector(getter: webView.estimatedProgress)), options: .new, context: nil)
        //处理参数
        let requestUrl: String
        if let parameters = self.parameters {
            requestUrl = self.urlWith(parameters, baseurl: self.webURLStr)
        } else {
            requestUrl = self.webURLStr
        }
        //加载webView
        self.webView.load(URLRequest(url: URL(string: requestUrl)!))
    }
    
    private func urlWith(_ dictionary: [String : String], baseurl url: String) -> String {
        var i = 0
        var resultUrl = url
        for (key, value) in dictionary {
            resultUrl = resultUrl + (i == 0 ? "?":"&") + key + "=" + value
            i += 1
        }
        return resultUrl
    }

代码都有注释,其中func urlWith(_ dictionary: [String : String], baseurl url: String) -> String这个方法是处理给定参数(存在字典中)拼接到要加载的URL中。例如
给定参数:{"par1":"value1"} ,baseURL: https//developer.apple,处理后为:https//developer.apple.com?par1=value1

    // MARK: - 移除监听对象
    deinit {
        self.webView.removeObserver(self, forKeyPath: NSStringFromSelector(#selector(getter: webView.estimatedProgress)))
    }

当然别忘了移除监听对象

定义一个方便的构造器

    // MARK: - convenience init
    convenience init(webURLStr: String, frame: CGRect) {
        self.init(webURLStr: webURLStr, parameters: nil, frame: frame)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

这个初始化方法是用来初始化没有参数的情况,还有就是防止用init?(coder aDecoder: NSCoder)这个初始化方法。
关于如何构造初始化方法,这里推荐两篇博文:
Swift3御剑术(4) Initialization-初始化
The Swift Programming Language (Swift 3.0.1)

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

推荐阅读更多精彩内容