Skip to content

Commit

Permalink
UIWebView+JKLoadInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
Jakey committed Dec 9, 2016
1 parent bf64b0c commit dca7f40
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 0 deletions.
6 changes: 6 additions & 0 deletions JKCategories-Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@
A2FD5B631A52718200555EA2 /* NSDate+JKUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = A2FD5B621A52718200555EA2 /* NSDate+JKUtilities.m */; };
A2FF6FE71D04334F00320C36 /* NSData+JKPCM.m in Sources */ = {isa = PBXBuildFile; fileRef = A2FF6FE61D04334F00320C36 /* NSData+JKPCM.m */; };
B869399D1CA8EEB500EE7A1A /* UIButton+JKImagePosition.m in Sources */ = {isa = PBXBuildFile; fileRef = B869399C1CA8EEB500EE7A1A /* UIButton+JKImagePosition.m */; };
B87E87E11DFAAFC7008EB83A /* UIWebView+JKLoadInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = B87E87E01DFAAFC7008EB83A /* UIWebView+JKLoadInfo.m */; };
B8A419521DDC07C800BDB39A /* NSString+JKHTML.m in Sources */ = {isa = PBXBuildFile; fileRef = B8A419511DDC07C800BDB39A /* NSString+JKHTML.m */; };
B8C8D1361DED106A0069354F /* UIButton+JKBadge.m in Sources */ = {isa = PBXBuildFile; fileRef = B8C8D1351DED106A0069354F /* UIButton+JKBadge.m */; };
B8C8D1391DED106F0069354F /* UIBarButtonItem+JKBadge.m in Sources */ = {isa = PBXBuildFile; fileRef = B8C8D1381DED106F0069354F /* UIBarButtonItem+JKBadge.m */; };
Expand Down Expand Up @@ -976,6 +977,8 @@
A2FF6FE61D04334F00320C36 /* NSData+JKPCM.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+JKPCM.m"; sourceTree = "<group>"; };
B869399B1CA8EEB500EE7A1A /* UIButton+JKImagePosition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+JKImagePosition.h"; sourceTree = "<group>"; };
B869399C1CA8EEB500EE7A1A /* UIButton+JKImagePosition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+JKImagePosition.m"; sourceTree = "<group>"; };
B87E87DF1DFAAFC7008EB83A /* UIWebView+JKLoadInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIWebView+JKLoadInfo.h"; sourceTree = "<group>"; };
B87E87E01DFAAFC7008EB83A /* UIWebView+JKLoadInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIWebView+JKLoadInfo.m"; sourceTree = "<group>"; };
B8A419501DDC07C800BDB39A /* NSString+JKHTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+JKHTML.h"; sourceTree = "<group>"; };
B8A419511DDC07C800BDB39A /* NSString+JKHTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+JKHTML.m"; sourceTree = "<group>"; };
B8C8D1341DED106A0069354F /* UIButton+JKBadge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+JKBadge.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2398,6 +2401,8 @@
A28BE3281A3E9FDC005C4AC6 /* UIWebView+JKStyle.m */,
A28BE37D1A3EC561005C4AC6 /* UIWebView+JKLoad.h */,
A28BE37E1A3EC561005C4AC6 /* UIWebView+JKLoad.m */,
B87E87DF1DFAAFC7008EB83A /* UIWebView+JKLoadInfo.h */,
B87E87E01DFAAFC7008EB83A /* UIWebView+JKLoadInfo.m */,
A218D8DB1A47A25100AB83CA /* UIWebView+JKCanvas.h */,
A218D8DC1A47A25100AB83CA /* UIWebView+JKCanvas.m */,
A218D8DD1A47A25100AB83CA /* UIWebView+JKJavaScript.h */,
Expand Down Expand Up @@ -3036,6 +3041,7 @@
A218D8E01A47A25100AB83CA /* UIWebView+JKJavaScript.m in Sources */,
A205E4D01C28E59E002A1E33 /* NSURLConnectionDemoViewController.m in Sources */,
A28BE3521A3EA91B005C4AC6 /* NSArray+JKBlock.m in Sources */,
B87E87E11DFAAFC7008EB83A /* UIWebView+JKLoadInfo.m in Sources */,
A22104181CFAC4E500CE6547 /* UIImageView+JKAddition.m in Sources */,
A2C12BA81B1C284F00EAD198 /* UITextField+JKSelect.m in Sources */,
A2AB1A651B381E3900ED88AF /* UIFontDemoViewController.m in Sources */,
Expand Down
1 change: 1 addition & 0 deletions JKCategories/JKUIKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,6 @@
#import "UIWebView+JKMetaParser.h"
#import "UIWebView+JKStyle.h"
#import "UIWebVIew+JKSwipeGesture.h"
#import "UIWebView+JKLoadInfo.h"
#import "UIWindow+JKHierarchy.h"
#endif /* JKUIKit_h */
26 changes: 26 additions & 0 deletions JKCategories/UIKit/UIWebView/UIWebView+JKLoadInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// UIWebView+JKLoadInfo.h
// JKCategories (https://github.com/shaojiankui/JKCategories)
//
// Created by Jakey on 2016/12/9.
// Copyright © 2016年 Jakey. All rights reserved.
//

#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, JKReadyState) {
JK_JKReadyState_uninitialized = 0, //还未开始载入
JK_JKReadyState_loading, //载入中
JK_JKReadyState_interactive, //已加载,文档与用户可以开始交互
JK_JKReadyState_complete //载入完成

};
typedef void(^jk_webViewLoadChangeBlock)(UIWebView *webView,float progress,CGSize contentSize);

@interface UIWebView (JKLoadInfo)<UIWebViewDelegate>
@property (nonatomic, readonly) float jk_progress; // 0.0..1.0
@property (nonatomic, readonly) CGSize jk_contentSize;
@property (nonatomic, readonly) JKReadyState jk_readyState;
@property (copy, nonatomic) jk_webViewLoadChangeBlock jk_webViewLoadChangeBlock;

-(void)setJk_webViewLoadChangeBlock:(jk_webViewLoadChangeBlock)jk_webViewLoadChangeBlock;
@end
295 changes: 295 additions & 0 deletions JKCategories/UIKit/UIWebView/UIWebView+JKLoadInfo.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
//
// UIWebView+JKLoadStatus.m
// JKCategories (https://github.com/shaojiankui/JKCategories)
//
// Created by Jakey on 2016/12/9.
// Copyright © 2016年 Jakey. All rights reserved.
//

#import "UIWebView+JKLoadInfo.h"
#import <objc/runtime.h>

static const void *k_jk_loadingCount = &k_jk_loadingCount;
static const void *k_jk_maxLoadCount = &k_jk_maxLoadCount;
static const void *k_jk_currentURL = &k_jk_currentURL;
static const void *k_jk_interactive = &k_jk_interactive;
static const void *k_jk_progress = &k_jk_progress;
static const void *k_jk_contentSize = &k_jk_contentSize;

static const void *k_jk_readyState = &k_jk_readyState;

static const void *k_jk_realDelegate = &k_jk_realDelegate;
static const void *k_jk_webViewLoadChangeBlock = &k_jk_webViewLoadChangeBlock;


NSString *jk_completeRPCURLPath = @"/jkwebviewprogressproxy/complete";

@implementation UIWebView (JKLoadStatus)
-(void)setJk_loadingCount:(NSUInteger)jk_loadingCount{
NSNumber *number = [[NSNumber alloc]initWithInteger:jk_loadingCount];
objc_setAssociatedObject(self,k_jk_loadingCount, number, OBJC_ASSOCIATION_ASSIGN);
}

-(NSUInteger)jk_loadingCount{
return [objc_getAssociatedObject(self, k_jk_loadingCount) integerValue];
}

-(void)setJk_maxLoadCount:(NSUInteger)jk_maxLoadCount{
NSNumber *number = [[NSNumber alloc]initWithInteger:jk_maxLoadCount];
objc_setAssociatedObject(self,k_jk_maxLoadCount, number, OBJC_ASSOCIATION_ASSIGN);
}

-(NSUInteger)jk_maxLoadCount{
return [objc_getAssociatedObject(self, k_jk_maxLoadCount) integerValue];
}

-(void)setJk_currentURL:(NSURL*)jk_currentURL{
objc_setAssociatedObject(self,k_jk_currentURL, jk_currentURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(NSURL*)jk_currentURL{
return objc_getAssociatedObject(self, k_jk_currentURL) ;
}

-(void)setJk_interactive:(BOOL)jk_interactive{
NSNumber *number = [[NSNumber alloc]initWithBool:jk_interactive];
objc_setAssociatedObject(self,k_jk_interactive, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(BOOL)jk_interactive{
return [objc_getAssociatedObject(self, k_jk_interactive) boolValue];
}

-(void)setJk_progress:(float)jk_progress{
NSNumber *number = [[NSNumber alloc]initWithFloat:jk_progress];
objc_setAssociatedObject(self,k_jk_progress, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(BOOL)jk_progress{
return [objc_getAssociatedObject(self, k_jk_progress) floatValue];
}

-(void)setJk_contentSize:(CGSize)jk_contentSize{
objc_setAssociatedObject(self,k_jk_progress, NSStringFromCGSize(jk_contentSize), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(CGSize)jk_contentSize{
return CGSizeFromString(objc_getAssociatedObject(self, k_jk_progress));
}

-(void)setJk_readyState:(JKReadyState)jk_readyState{
objc_setAssociatedObject(self,k_jk_readyState, @(jk_readyState), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(JKReadyState)jk_readyState{
return (JKReadyState)[objc_getAssociatedObject(self, k_jk_readyState) integerValue];
}

-(void)setJk_realDelegate:(id<UIWebViewDelegate>)jk_realDelegate{
objc_setAssociatedObject(self,k_jk_realDelegate, jk_realDelegate, OBJC_ASSOCIATION_ASSIGN);
}

-(id<UIWebViewDelegate>)jk_realDelegate{
return objc_getAssociatedObject(self, k_jk_realDelegate);
}

-(void (^)(UIWebView *, float))jk_webViewLoadChangeBlock{
return objc_getAssociatedObject(self, k_jk_webViewLoadChangeBlock);
}

- (void)setJk_webViewLoadChangeBlock:(jk_webViewLoadChangeBlock)jk_webViewLoadChangeBlock
{
[self jk_setDelegateIfNoDelegateSet];
objc_setAssociatedObject(self, k_jk_webViewLoadChangeBlock, jk_webViewLoadChangeBlock, OBJC_ASSOCIATION_COPY);
}

#pragma mark --delegate

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([request.URL.path isEqualToString:jk_completeRPCURLPath]) {
[self jk_completeProgress];
return NO;
}

BOOL ret = YES;
if ([self.jk_realDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
ret = [self.jk_realDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
}

BOOL isFragmentJump = NO;
if (request.URL.fragment) {
NSString *nonFragmentURL = [request.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:request.URL.fragment] withString:@""];
isFragmentJump = [nonFragmentURL isEqualToString:webView.request.URL.absoluteString];
}

BOOL isTopLevelNavigation = [request.mainDocumentURL isEqual:request.URL];

BOOL isHTTPOrLocalFile = [request.URL.scheme isEqualToString:@"http"] || [request.URL.scheme isEqualToString:@"https"] || [request.URL.scheme isEqualToString:@"file"];
if (ret && !isFragmentJump && isHTTPOrLocalFile && isTopLevelNavigation) {
self.jk_currentURL = request.URL;
[self jk_reset];
}
return ret;
}

- (void)webViewDidStartLoad:(UIWebView *)webView
{
self.jk_readyState = JK_JKReadyState_loading;

if ([self.jk_realDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.jk_realDelegate webViewDidStartLoad:webView];
}

self.jk_loadingCount++;
self.jk_maxLoadCount = fmax(self.jk_maxLoadCount, self.jk_loadingCount);
[self jk_startProgress];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if ([self.jk_realDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[self.jk_realDelegate webViewDidFinishLoad:webView];
}

self.jk_loadingCount--;
[self jk_incrementProgress];

NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];

BOOL interactive = [readyState isEqualToString:@"interactive"];
if (interactive) {
self.jk_interactive = YES;
self.jk_readyState = JK_JKReadyState_interactive;

NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@://%@%@'; document.body.appendChild(iframe); }, false);", webView.request.mainDocumentURL.scheme, webView.request.mainDocumentURL.host, jk_completeRPCURLPath];
[webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS];
}

BOOL isNotRedirect = self.jk_currentURL && [self.jk_currentURL isEqual:webView.request.mainDocumentURL];
BOOL complete = [readyState isEqualToString:@"complete"];
if (complete && isNotRedirect) {
[self jk_completeProgress];
}
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if ([self.jk_realDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
[self.jk_realDelegate webView:webView didFailLoadWithError:error];
}

self.jk_loadingCount--;
[self jk_incrementProgress];

NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];

BOOL interactive = [readyState isEqualToString:@"interactive"];
if (interactive) {
self.jk_interactive = YES;
NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@://%@%@'; document.body.appendChild(iframe); }, false);", webView.request.mainDocumentURL.scheme, webView.request.mainDocumentURL.host, jk_completeRPCURLPath];
[webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS];
}

BOOL isNotRedirect = self.jk_currentURL && [self.jk_currentURL isEqual:webView.request.mainDocumentURL];
BOOL complete = [readyState isEqualToString:@"complete"];
if ((complete && isNotRedirect) || error) {
[self jk_completeProgress];
}
}

- (void)jk_startProgress
{
if (self.jk_progress < 0.1) {
[self jk_updateProgress:0.1];
}
}
- (void)jk_incrementProgress
{
float progress = self.jk_progress;
float maxProgress = self.jk_interactive ? 0.9 : 0.5f;
float remainPercent = (float)self.jk_loadingCount / (float)self.jk_maxLoadCount;
float increment = (maxProgress - progress) * remainPercent;
progress += increment;
progress = fmin(progress, maxProgress);
[self jk_updateProgress:progress];
}

- (void)jk_completeProgress
{
self.jk_readyState = JK_JKReadyState_complete;
[self jk_updateProgress:1.0];
// CGFloat webViewHeight= [[self stringByEvaluatingJavaScriptFromString: @"document.body.offsetHeight"]floatValue];
// NSLog(@"body.offsetHeight:%lf",webViewHeight);
//
// CGFloat webViewHeight2=self.scrollView.contentSize.height;
// NSLog(@"scrollView contentSize:%lf",webViewHeight2);
}

- (void)jk_updateProgress:(float)progress
{
if (progress > self.jk_progress || progress == 0) {
self.jk_progress = progress;
if (self.jk_webViewLoadChangeBlock) {
self.jk_webViewLoadChangeBlock(self,progress,self.scrollView.contentSize);
}
}
}
- (void)jk_reset
{
self.jk_maxLoadCount = self.jk_loadingCount = 0;
self.jk_interactive = NO;
[self jk_updateProgress:0.0];
self.jk_readyState = JK_JKReadyState_uninitialized;
}

-(void)awakeFromNib{
[super awakeFromNib];
[self jk_setDelegateIfNoDelegateSet];
}

- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self jk_setDelegateIfNoDelegateSet];
}
return self;
}
#pragma mark - Delegate Forwarder
- (void)jk_setDelegateIfNoDelegateSet
{
if (self.delegate != (id<UIWebViewDelegate>)self) {
self.jk_realDelegate = self.delegate;
self.delegate = (id<UIWebViewDelegate>)self;
}
}


//- (void)setDelegate:(id<UIWebViewDelegate>)delegate {
//
// if ([[[UIDevice currentDevice] systemVersion] floatValue]>=7.0){
// // UIScrollView delegate keeps some flags that mark whether the delegate implements some methods (like scrollViewDidScroll:)
// // setting *the same* delegate doesn't recheck the flags, so it's better to simply nil the previous delegate out
// // we have to setup the realDelegate at first, since the flag check happens in setter
//// [super setDelegate:nil];
//// self.delegate = nil;
// self.jk_realDelegate = delegate != self ? delegate : nil;
//// [super setDelegate:delegate ? self : nil];
// self.delegate = delegate ? self : nil;
//
// }else {
//// [super setDelegate:delegate];
//// self.delegate = delegate;
//
// }
//}

//- (BOOL)respondsToSelector:(SEL)selector {
// return [super respondsToSelector:selector] || [self.jk_realDelegate respondsToSelector:selector];
//}
//- (id)forwardingTargetForSelector:(SEL)selector {
// id delegate = self.jk_realDelegate;
// return [delegate respondsToSelector:selector] ? delegate : [super forwardingTargetForSelector:selector];
//}
@end

0 comments on commit dca7f40

Please sign in to comment.