forked from shaojiankui/JKCategories
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jakey
committed
Dec 9, 2016
1 parent
bf64b0c
commit dca7f40
Showing
4 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |