Skip to content

Commit 77b6f51

Browse files
committed
Merge pull request #74 from TimOliver/feature-load-progress
Refined the loading/progress tracking system
2 parents 75cf608 + a547eba commit 77b6f51

18 files changed

Lines changed: 516 additions & 359 deletions

Example/TOViewController.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
101101
NSURL *url = nil;
102102

103103
#ifdef TO_ONEPASSWORD_EXAMPLE
104-
url = [NSURL URLWithString:@"http://dropbox.com/login"];
104+
url = [NSURL URLWithString:@"https://accounts.google.com/login"];
105105
#else
106106
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
107107
url = [NSURL URLWithString:@"www.apple.com/ipad"];

TOWebViewController+1Password/TOWebViewController+1Password.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//
22
// TOWebViewController+1Password.h
33
//
4-
// Copyright 2013-2015 Timothy Oliver. All rights reserved.
4+
// Copyright 2013-2016 Timothy Oliver. All rights reserved.
55
//
66
// Permission is hereby granted, free of charge, to any person obtaining a copy
77
// of this software and associated documentation files (the "Software"), to

TOWebViewController+1Password/TOWebViewController+1Password.m

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//
22
// TOWebViewController+1Password.m
33
//
4-
// Copyright 2013-2015 Timothy Oliver. All rights reserved.
4+
// Copyright 2013-2016 Timothy Oliver. All rights reserved.
55
//
66
// Permission is hereby granted, free of charge, to any person obtaining a copy
77
// of this software and associated documentation files (the "Software"), to
@@ -35,9 +35,12 @@ - (void)setShowOnePasswordButton:(BOOL)showOnePasswordButton
3535
if (self.showOnePasswordButton == showOnePasswordButton)
3636
return;
3737

38+
#if TARGET_IPHONE_SIMULATOR
39+
#else
3840
//Don't bother trying if 1Password isn't on the system
3941
if ([[OnePasswordExtension sharedExtension] isAppExtensionAvailable] == NO)
4042
return;
43+
#endif
4144

4245
objc_setAssociatedObject(self, &onePasswordExtensionEnabledKey, @(showOnePasswordButton), OBJC_ASSOCIATION_ASSIGN);
4346

TOWebViewController.podspec

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ Pod::Spec.new do |s|
77
s.author = 'Tim Oliver'
88
s.source = { :git => 'https://github.com/TimOliver/TOWebViewController.git', :tag => s.version.to_s }
99
s.platform = :ios, '5.0'
10-
10+
s.frameworks = 'QuartzCore', 'CoreGraphics'
11+
s.weak_frameworks = 'Twitter', 'MessageUI'
1112
s.source_files = 'TOWebViewController/**/*.{h,m}'
1213
s.resource_bundles = {'TOWebViewControllerLocalizable' => 'TOWebViewController/**/*.lproj'}
1314
s.requires_arc = true
@@ -20,5 +21,6 @@ Pod::Spec.new do |s|
2021
spec.subspec '1Password' do |op|
2122
op.dependency '1PasswordExtension'
2223
op.source_files = 'TOWebViewController+1Password/*.{h,m}'
24+
op.frameworks = 'MobileCoreServices'
2325
end
2426
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// NJKWebViewProgress.h
3+
//
4+
// Created by Satoshi Aasano on 4/20/13.
5+
// Copyright (c) 2013 Satoshi Asano. All rights reserved.
6+
//
7+
8+
#import <Foundation/Foundation.h>
9+
#import <UIKit/UIKit.h>
10+
11+
#undef njk_weak
12+
#if __has_feature(objc_arc_weak)
13+
#define njk_weak weak
14+
#else
15+
#define njk_weak unsafe_unretained
16+
#endif
17+
18+
extern const float NJKInitialProgressValue;
19+
extern const float NJKInteractiveProgressValue;
20+
extern const float NJKFinalProgressValue;
21+
22+
typedef void (^NJKWebViewProgressBlock)(float progress);
23+
@protocol NJKWebViewProgressDelegate;
24+
@interface NJKWebViewProgress : NSObject<UIWebViewDelegate>
25+
@property (nonatomic, njk_weak) id<NJKWebViewProgressDelegate>progressDelegate;
26+
@property (nonatomic, njk_weak) id<UIWebViewDelegate>webViewProxyDelegate;
27+
@property (nonatomic, copy) NJKWebViewProgressBlock progressBlock;
28+
@property (nonatomic, readonly) float progress; // 0.0..1.0
29+
30+
- (void)reset;
31+
@end
32+
33+
@protocol NJKWebViewProgressDelegate <NSObject>
34+
- (void)webViewProgress:(NJKWebViewProgress *)webViewProgress updateProgress:(float)progress;
35+
@end
36+
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
//
2+
// NJKWebViewProgress.m
3+
//
4+
// Created by Satoshi Aasano on 4/20/13.
5+
// Copyright (c) 2013 Satoshi Asano. All rights reserved.
6+
//
7+
8+
#import "NJKWebViewProgress.h"
9+
10+
NSString *completeRPCURLPath = @"/njkwebviewprogressproxy/complete";
11+
12+
const float NJKInitialProgressValue = 0.1f;
13+
const float NJKInteractiveProgressValue = 0.5f;
14+
const float NJKFinalProgressValue = 0.9f;
15+
16+
@implementation NJKWebViewProgress
17+
{
18+
NSUInteger _loadingCount;
19+
NSUInteger _maxLoadCount;
20+
NSURL *_currentURL;
21+
BOOL _interactive;
22+
}
23+
24+
- (id)init
25+
{
26+
self = [super init];
27+
if (self) {
28+
_maxLoadCount = _loadingCount = 0;
29+
_interactive = NO;
30+
}
31+
return self;
32+
}
33+
34+
- (void)startProgress
35+
{
36+
if (_progress < NJKInitialProgressValue) {
37+
[self setProgress:NJKInitialProgressValue];
38+
}
39+
}
40+
41+
- (void)incrementProgress
42+
{
43+
float progress = self.progress;
44+
float maxProgress = _interactive ? NJKFinalProgressValue : NJKInteractiveProgressValue;
45+
float remainPercent = (float)_loadingCount / (float)_maxLoadCount;
46+
float increment = (maxProgress - progress) * remainPercent;
47+
progress += increment;
48+
progress = fmin(progress, maxProgress);
49+
[self setProgress:progress];
50+
}
51+
52+
- (void)completeProgress
53+
{
54+
[self setProgress:1.0];
55+
}
56+
57+
- (void)setProgress:(float)progress
58+
{
59+
// progress should be incremental only
60+
if (progress > _progress || progress == 0) {
61+
_progress = progress;
62+
if ([_progressDelegate respondsToSelector:@selector(webViewProgress:updateProgress:)]) {
63+
[_progressDelegate webViewProgress:self updateProgress:progress];
64+
}
65+
if (_progressBlock) {
66+
_progressBlock(progress);
67+
}
68+
}
69+
}
70+
71+
- (void)reset
72+
{
73+
_maxLoadCount = _loadingCount = 0;
74+
_interactive = NO;
75+
[self setProgress:0.0];
76+
}
77+
78+
#pragma mark -
79+
#pragma mark UIWebViewDelegate
80+
81+
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
82+
{
83+
if ([request.URL.path isEqualToString:completeRPCURLPath]) {
84+
[self completeProgress];
85+
return NO;
86+
}
87+
88+
BOOL ret = YES;
89+
if ([_webViewProxyDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
90+
ret = [_webViewProxyDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
91+
}
92+
93+
BOOL isFragmentJump = NO;
94+
if (request.URL.fragment) {
95+
NSString *nonFragmentURL = [request.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:request.URL.fragment] withString:@""];
96+
isFragmentJump = [nonFragmentURL isEqualToString:webView.request.URL.absoluteString];
97+
}
98+
99+
BOOL isTopLevelNavigation = [request.mainDocumentURL isEqual:request.URL];
100+
101+
BOOL isHTTPOrLocalFile = [request.URL.scheme isEqualToString:@"http"] || [request.URL.scheme isEqualToString:@"https"] || [request.URL.scheme isEqualToString:@"file"];
102+
if (ret && !isFragmentJump && isHTTPOrLocalFile && isTopLevelNavigation) {
103+
_currentURL = request.URL;
104+
[self reset];
105+
}
106+
return ret;
107+
}
108+
109+
- (void)webViewDidStartLoad:(UIWebView *)webView
110+
{
111+
if ([_webViewProxyDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
112+
[_webViewProxyDelegate webViewDidStartLoad:webView];
113+
}
114+
115+
_loadingCount++;
116+
_maxLoadCount = fmax(_maxLoadCount, _loadingCount);
117+
118+
[self startProgress];
119+
}
120+
121+
- (void)webViewDidFinishLoad:(UIWebView *)webView
122+
{
123+
if ([_webViewProxyDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
124+
[_webViewProxyDelegate webViewDidFinishLoad:webView];
125+
}
126+
127+
_loadingCount--;
128+
[self incrementProgress];
129+
130+
NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
131+
132+
BOOL interactive = [readyState isEqualToString:@"interactive"];
133+
if (interactive) {
134+
_interactive = YES;
135+
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];
136+
[webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS];
137+
}
138+
139+
BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL];
140+
BOOL complete = [readyState isEqualToString:@"complete"];
141+
if (complete && isNotRedirect) {
142+
[self completeProgress];
143+
}
144+
}
145+
146+
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
147+
{
148+
if ([_webViewProxyDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
149+
[_webViewProxyDelegate webView:webView didFailLoadWithError:error];
150+
}
151+
152+
_loadingCount--;
153+
[self incrementProgress];
154+
155+
NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
156+
157+
BOOL interactive = [readyState isEqualToString:@"interactive"];
158+
if (interactive) {
159+
_interactive = YES;
160+
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];
161+
[webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS];
162+
}
163+
164+
BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL];
165+
BOOL complete = [readyState isEqualToString:@"complete"];
166+
if ((complete && isNotRedirect) || error) {
167+
[self completeProgress];
168+
}
169+
}
170+
171+
#pragma mark -
172+
#pragma mark Method Forwarding
173+
// for future UIWebViewDelegate impl
174+
175+
- (BOOL)respondsToSelector:(SEL)aSelector
176+
{
177+
if ( [super respondsToSelector:aSelector] )
178+
return YES;
179+
180+
if ([self.webViewProxyDelegate respondsToSelector:aSelector])
181+
return YES;
182+
183+
return NO;
184+
}
185+
186+
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
187+
{
188+
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
189+
if(!signature) {
190+
if([_webViewProxyDelegate respondsToSelector:selector]) {
191+
return [(NSObject *)_webViewProxyDelegate methodSignatureForSelector:selector];
192+
}
193+
}
194+
return signature;
195+
}
196+
197+
- (void)forwardInvocation:(NSInvocation*)invocation
198+
{
199+
if ([_webViewProxyDelegate respondsToSelector:[invocation selector]]) {
200+
[invocation invokeWithTarget:_webViewProxyDelegate];
201+
}
202+
}
203+
204+
@end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// NJKWebViewProgressView.h
3+
// iOS 7 Style WebView Progress Bar
4+
//
5+
// Created by Satoshi Aasano on 11/16/13.
6+
// Copyright (c) 2013 Satoshi Asano. All rights reserved.
7+
//
8+
9+
#import <UIKit/UIKit.h>
10+
11+
@interface NJKWebViewProgressView : UIView
12+
@property (nonatomic) float progress;
13+
14+
@property (nonatomic) UIView *progressBarView;
15+
@property (nonatomic) NSTimeInterval barAnimationDuration; // default 0.1
16+
@property (nonatomic) NSTimeInterval fadeAnimationDuration; // default 0.27
17+
@property (nonatomic) NSTimeInterval fadeOutDelay; // default 0.1
18+
19+
- (void)setProgress:(float)progress animated:(BOOL)animated;
20+
21+
@end
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// NJKWebViewProgressView.m
3+
//
4+
// Created by Satoshi Aasanoon 11/16/13.
5+
// Copyright (c) 2013 Satoshi Asano. All rights reserved.
6+
//
7+
8+
#import "NJKWebViewProgressView.h"
9+
10+
@implementation NJKWebViewProgressView
11+
12+
- (id)initWithFrame:(CGRect)frame
13+
{
14+
self = [super initWithFrame:frame];
15+
if (self) {
16+
[self configureViews];
17+
}
18+
return self;
19+
}
20+
21+
- (void)awakeFromNib
22+
{
23+
[super awakeFromNib];
24+
[self configureViews];
25+
}
26+
27+
-(void)configureViews
28+
{
29+
self.userInteractionEnabled = NO;
30+
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
31+
_progressBarView = [[UIView alloc] initWithFrame:self.bounds];
32+
_progressBarView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
33+
UIColor *tintColor = [UIColor colorWithRed:22.f / 255.f green:126.f / 255.f blue:251.f / 255.f alpha:1.0]; // iOS7 Safari bar color
34+
if ([UIApplication.sharedApplication.delegate.window respondsToSelector:@selector(setTintColor:)] && UIApplication.sharedApplication.delegate.window.tintColor) {
35+
tintColor = UIApplication.sharedApplication.delegate.window.tintColor;
36+
}
37+
_progressBarView.backgroundColor = tintColor;
38+
[self addSubview:_progressBarView];
39+
40+
_barAnimationDuration = 0.27f;
41+
_fadeAnimationDuration = 0.27f;
42+
_fadeOutDelay = 0.1f;
43+
}
44+
45+
-(void)setProgress:(float)progress
46+
{
47+
[self setProgress:progress animated:NO];
48+
}
49+
50+
- (void)setProgress:(float)progress animated:(BOOL)animated
51+
{
52+
BOOL isGrowing = progress > 0.0;
53+
[UIView animateWithDuration:(isGrowing && animated) ? _barAnimationDuration : 0.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
54+
CGRect frame = _progressBarView.frame;
55+
frame.size.width = progress * self.bounds.size.width;
56+
_progressBarView.frame = frame;
57+
} completion:nil];
58+
59+
if (progress >= 1.0) {
60+
[UIView animateWithDuration:animated ? _fadeAnimationDuration : 0.0 delay:_fadeOutDelay options:UIViewAnimationOptionCurveEaseInOut animations:^{
61+
_progressBarView.alpha = 0.0;
62+
} completion:^(BOOL completed){
63+
CGRect frame = _progressBarView.frame;
64+
frame.size.width = 0;
65+
_progressBarView.frame = frame;
66+
}];
67+
}
68+
else {
69+
[UIView animateWithDuration:animated ? _fadeAnimationDuration : 0.0 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
70+
_progressBarView.alpha = 1.0;
71+
} completion:nil];
72+
}
73+
}
74+
75+
@end

0 commit comments

Comments
 (0)