2 * Copyright (C) 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "WebViewController.h"
29 #import "TabViewController.h"
30 #import <WebKit/WKNavigation.h>
31 #import <WebKit/WKNavigationDelegate.h>
32 #import <WebKit/WKWebView.h>
33 #import <WebKit/WKWebViewConfiguration.h>
35 @interface WebViewController () <WKNavigationDelegate> {
36 WKWebView *_currentWebView;
38 - (WKWebView *)createWebView;
39 - (void)removeWebView:(WKWebView *)webView;
40 - (void)setCurrentWebView:(WKWebView *)webView;
43 void* EstimatedProgressContext = &EstimatedProgressContext;
44 void* TitleContext = &TitleContext;
45 void* URLContext = &URLContext;
47 @implementation WebViewController
52 self.webViews = [[NSMutableArray alloc] initWithCapacity:1];
53 self.tabViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"idTabViewController"];
54 self.tabViewController.parent = self;
55 self.tabViewController.modalPresentationStyle = UIModalPresentationPopover;
57 [self setCurrentWebView:[self createWebView]];
61 - (void)didReceiveMemoryWarning
63 [super didReceiveMemoryWarning];
68 - (IBAction)reload:(id)sender
70 [self.currentWebView reload];
73 - (IBAction)goBack:(id)sender
75 [self.currentWebView goBack];
78 - (IBAction)goForward:(id)sender
80 [self.currentWebView goForward];
83 - (IBAction)urlFieldEditingBegan:(id)sender
85 self.urlField.selectedTextRange = [self.urlField textRangeFromPosition:self.urlField.beginningOfDocument toPosition:self.urlField.endOfDocument];
88 - (IBAction)navigateTo:(id)sender
90 [self.urlField resignFirstResponder];
91 NSString* requestedDestination = self.urlField.text;
92 if ([requestedDestination rangeOfString:@"^[\\p{Alphabetic}]+:" options:(NSRegularExpressionSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location == NSNotFound)
93 requestedDestination = [@"http://" stringByAppendingString:requestedDestination];
94 [self.currentWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:requestedDestination]]];
97 - (IBAction)showTabs:(id)sender
99 [self presentViewController:self.tabViewController animated:YES completion:^{ }];
100 self.tabViewController.popoverPresentationController.barButtonItem = self.tabButton;
103 #pragma mark Public methods
105 @dynamic currentWebView;
107 - (WKWebView *)currentWebView
109 return _currentWebView;
112 - (void)setCurrentWebView:(WKWebView *)webView
114 [_currentWebView removeObserver:self forKeyPath:@"estimatedProgress" context:EstimatedProgressContext];
115 [_currentWebView removeObserver:self forKeyPath:@"URL" context:URLContext];
116 [_currentWebView removeFromSuperview];
118 _currentWebView = webView;
120 [_currentWebView addObserver:self forKeyPath:@"estimatedProgress" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:EstimatedProgressContext];
121 [_currentWebView addObserver:self forKeyPath:@"URL" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:URLContext];
122 [self.webViewContainer addSubview:_currentWebView];
125 - (void)selectWebViewAtIndex:(NSUInteger)index
127 if (index >= self.webViews.count)
129 self.currentWebView = self.webViews[index];
132 - (void)removeWebViewAtIndex:(NSUInteger)index
134 if (index >= self.webViews.count)
136 [self removeWebView:self.webViews[index]];
141 self.currentWebView = [self createWebView];
144 #pragma mark Internal methods
146 - (WKWebView *)createWebView
148 WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
149 WKWebView *webView = [[WKWebView alloc] initWithFrame:self.webViewContainer.bounds configuration:configuration];
150 webView.navigationDelegate = self;
151 webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
152 [webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:TitleContext];
153 [self.webViews addObject:webView];
155 [self.tabViewController.tableView reloadData];
160 - (void)removeWebView:(WKWebView *)webView
162 NSUInteger index = [self.webViews indexOfObject:webView];
163 if (index != NSNotFound) {
164 [self.webViews[index] removeObserver:self forKeyPath:@"title" context:TitleContext];
165 [self.webViews removeObjectAtIndex:index];
166 } if (!self.webViews.count)
167 [self createWebView];
168 if (index >= self.webViews.count)
169 index = self.webViews.count - 1;
170 [self setCurrentWebView:self.webViews[index]];
172 [self.tabViewController.tableView reloadData];
175 #pragma mark Navigation Delegate
177 - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
179 [webView loadHTMLString:[error description] baseURL:nil];
184 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
186 if (context == EstimatedProgressContext) {
187 float value = [[change valueForKey:NSKeyValueChangeNewKey] floatValue];
188 [self.progressView setProgress:value animated:YES];
189 } else if (context == URLContext) {
190 id newURLValue = [change valueForKey:NSKeyValueChangeNewKey];
191 if ([newURLValue isKindOfClass:[NSURL class]])
192 self.urlField.text = [newURLValue absoluteString];
193 else if ([newURLValue isKindOfClass:[NSNull class]])
194 self.urlField.text = @"";
195 } else if (context == TitleContext)
196 [self.tabViewController.tableView reloadData];
198 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];