YHWebViewProgress.m 6.4 KB
//
//  YHWebViewProgress.m
//  YohoExplorerDemo
//
//  Created by gaoqiang xu on 3/25/15.
//  Copyright (c) 2015 gaoqiang xu. All rights reserved.
//

#import "YHWebViewProgress.h"
#import "YHExplorerViewController.h"
#import <WebKit/WebKit.h>

NSString *completeRPCURLPath = @"/yhwebviewprogressproxy/complete";

static const float YHWebViewProgressInitialValue = 0.94f;
static const float YHWebViewProgressInteractiveValue = 0.95f;
static const float YHWebViewProgressFinalProgressValue = 0.96f;

@interface YHWebViewProgress ()
@property (nonatomic) NSUInteger loadingCount;
@property (nonatomic) NSUInteger maxLoadCount;
@property (strong, nonatomic) NSURL *currentURL;
@property (nonatomic) BOOL interactive;

@property (nonatomic) float progress;

@end

@implementation YHWebViewProgress

- (instancetype)init
{
    self = [super init];
    if (self) {
        _maxLoadCount = _loadingCount = 0;
        _interactive = NO;
    }
    
    return self;
}

- (void)dealloc
{
    
}

- (void)startProgress
{
    if (self.progress < YHWebViewProgressInitialValue) {
        [self setProgress:YHWebViewProgressInitialValue];
    }
}

- (void)incrementProgress
{
    float progress = self.progress;
    float maxProgress = self.interactive?YHWebViewProgressFinalProgressValue:YHWebViewProgressInteractiveValue;
    if (self.loadingCount == 0) {
        progress = maxProgress;
    } else {
        float remainPercent = (float)self.loadingCount/self.maxLoadCount;
        float increment = (maxProgress-progress) * remainPercent;
        progress += increment;
        progress = fminf(progress, maxProgress);
    }
    
    [self setProgress:progress];
}

- (void)completeProgress
{
    [self setProgress:1.f];
}

- (void)setProgress:(float)progress
{
    if (progress > _progress || progress == 0) {
        _progress = progress;
        
        if (self.progressView) {
            [self.progressView setProgress:progress animated:YES];
        }
    }
}

- (void)reset
{
    self.maxLoadCount = self.loadingCount = 0;
    self.interactive = NO;
    [self setProgress:0.f];
}

- (BOOL)checkIfRPCURL:(NSURLRequest *)request
{
    if ([request.URL.path isEqualToString:completeRPCURLPath]) {
        [self completeProgress];
        return YES;
    }
    
    return NO;
}

#pragma mark - UIWebViewDelegate
- (BOOL)yhExplorer_webView:(YHExplorerViewController *)explorer shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    BOOL ret = YES;
    
    if ([explorer.webView isKindOfClass:UIWebView.class]) {
        BOOL isFragmentJump = NO;
        if (request.URL.fragment) {
            NSString *nonFragmentURL = [request.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:request.URL.fragment] withString:@""];
            isFragmentJump = [nonFragmentURL isEqualToString:((UIWebView *)explorer.webView).request.URL.absoluteString];
        }
        
        BOOL isTopLevelNavigation = [request.mainDocumentURL isEqual:request.URL];
        
        BOOL isHTTP = [request.URL.scheme isEqualToString:@"http"] || [request.URL.scheme isEqualToString:@"https"];
        if (ret && !isFragmentJump && isHTTP && isTopLevelNavigation) {
            self.currentURL = request.URL;
            [self reset];
        }
    }
    
    return ret;
}

- (void)yhExplorer_webViewDidStartLoad:(YHExplorerViewController *)explorer
{
    if ([explorer.webView isKindOfClass:UIWebView.class]) {
        self.loadingCount++;
        
        self.maxLoadCount = self.loadingCount;
        
        [self startProgress];
    }
}

- (void)yhExplorer_webViewDidFinishLoad:(YHExplorerViewController *)explorer
{
    if ([explorer.webView isKindOfClass:UIWebView.class]) {
        UIWebView *webView = (UIWebView *)explorer.webView;
        
        self.loadingCount--;
        [self incrementProgress];
        
        NSString *readyState = [explorer stringByEvaluatingJavaScriptFromString:@"document.readyState"];
        
        BOOL interactive = [readyState isEqualToString:@"interactive"];
        if (interactive) {
            self.interactive = 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,
                                           completeRPCURLPath];
            [webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS];
        }
        
        BOOL isNotRedirect = (self.currentURL && [self.currentURL isEqual:webView.request.mainDocumentURL]) || [webView.request.URL.absoluteString hasPrefix:@"file:"];
        BOOL complete = [readyState isEqualToString:@"complete"];
        if (complete && isNotRedirect) {
            [self completeProgress];
        }
    }
}

- (void)yhExplorer_webView:(YHExplorerViewController *)explorer didFailLoadWithError:(NSError *)error
{
    
    if (![explorer.webView isKindOfClass:UIWebView.class]) {
        [self completeProgress];
        return;
    }
    UIWebView *webView = (UIWebView *)explorer.webView;
    
    if (error && error.code != NSURLErrorCancelled) {
        self.loadingCount--;
        [self incrementProgress];
    }
    
    NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
    
    BOOL interactive = [readyState isEqualToString:@"interactive"];
    if (interactive) {
        self.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, completeRPCURLPath];
        [webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS];
    }
    
    BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL];
    BOOL complete = [readyState isEqualToString:@"complete"];
    if ((complete && isNotRedirect) || (error && error.code != NSURLErrorCancelled)) {
        [self completeProgress];
    }
}

- (void)yhExplorer_webViewWebContentProcessDidTerminate:(id)webView
{
    [self completeProgress];
}

@end