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一樣簡單方便使用。
兩個初始化方法,帶參數(shù)和不帶參數(shù)。也只能從這兩個初始化方法創(chuàng)建SQWebView實(shí)例。所以要在.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];
}

屬性

因?yàn)槭呛唵芜\(yùn)用,所以前期只有這四個屬性,其中parameters是可以為nil,因?yàn)榧虞d有些網(wǎng)頁不需要參數(shù)。

@interface SQWebView ()

@property (nonatomic, strong, nonnull) UIProgressView *progressView; //進(jìn)度條
@property (nonatomic, strong, nonnull) WKWebView *webView;           //webview
@property (nonatomic, strong, nonnull) NSString *webURLStr;          //requestUrlStr
@property (nonatomic, strong, nullable) NSDictionary *parameters;    //加載url包含參數(shù)

@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) {           // 有參數(shù)要加載參數(shù)
            __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加載進(jìn)度的KVO,當(dāng)webView加載網(wǎng)頁進(jìn)度有變化會觸發(fā)observeValueForKeyPath:ofObject:...這個方法。相應(yīng)的可以在這個方法中改變progressView進(jìn)度,從而達(dá)到有進(jìn)度提示的自定義webView.

KVO回調(diào)

#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(加載網(wǎng)頁進(jìn)度)屬性,所以用WKWebview比UIWebView實(shí)現(xiàn)起來簡直不要太方便了

dealloc中移除觀察者

最后別忘了把self從webView中移除觀察者

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

我朋友的一篇博客對UIWebView的簡單封裝寫的不錯,大家可以去觀摩下webView進(jìn)度條簡單封裝

Swift版

前話

思路跟OC版的是一毛一樣的,就是換了種語言罷了

定義的屬性

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

只有parameters是定義為可選類型,因?yàn)榧虞dURL有不需要參數(shù)的情況,所以可以為nil。

構(gòu)造指定初始化方法(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()
    }

初始化好四個存儲屬性,再調(diào)用父類designated init,接著設(shè)置progressWebView和webView的相關(guān)操作

設(shè)置webView 添加監(jiān)聽對象

    // MARK: - private
    private func setupUI() {
        //設(shè)置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)
        //添加監(jiān)聽webView網(wǎng)頁加載進(jìn)度
        self.webView.addObserver(self, forKeyPath: NSStringFromSelector(#selector(getter: webView.estimatedProgress)), options: .new, context: nil)
        //處理參數(shù)
        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這個方法是處理給定參數(shù)(存在字典中)拼接到要加載的URL中。例如
給定參數(shù):{"par1":"value1"} ,baseURL: https//developer.apple,處理后為:https//developer.apple.com?par1=value1

    // MARK: - 移除監(jiān)聽對象
    deinit {
        self.webView.removeObserver(self, forKeyPath: NSStringFromSelector(#selector(getter: webView.estimatedProgress)))
    }

當(dāng)然別忘了移除監(jiān)聽對象

定義一個方便的構(gòu)造器

    // 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")
    }

這個初始化方法是用來初始化沒有參數(shù)的情況,還有就是防止用init?(coder aDecoder: NSCoder)這個初始化方法。
關(guān)于如何構(gòu)造初始化方法,這里推薦兩篇博文:
Swift3御劍術(shù)(4) Initialization-初始化
The Swift Programming Language (Swift 3.0.1)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容