How to Migrate to WKWebView? - uiwebview

I'm trying to understand how to make use of the new WKWebView in iOS8, can't find much information. I've read:
http://developer.telerik.com/featured/why-ios-8s-wkwebview-is-a-big-deal-for-hybrid-development/
http://nshipster.com/wkwebkit/
But how does this affect existing apps? Will an ordinary UiWebView get the speedup from the nitro java script engine or do we need to make changes? How do we deal with backwards compatibility?
All the code and examples I can find are using swift, will this be mandatory?
Thankful for any help on this matter!

UIWebView will still continue to work with existing apps. WKWebView is available starting from iOS8, only WKWebView has a Nitro JavaScript engine.
To take advantage of this faster JavaScript engine in older apps you have to make code changes to use WKWebView instead of UIWebView. For iOS7 and older, you have to continue to use UIWebView, so you may have to check for iOS8 and then apply WKWebView methods / delegate methods and fallback to UIWebView methods for iOS7 and older. Also there is no Interface Builder component for WKWebView (yet), so you have to programmatically implement WKWebView.
You can implement WKWebView in Objective-C, here is simple example to initiate a WKWebView:
WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:theConfiguration];
webView.navigationDelegate = self;
NSURL *nsurl=[NSURL URLWithString:#"http://www.apple.com"];
NSURLRequest *nsrequest=[NSURLRequest requestWithURL:nsurl];
[webView loadRequest:nsrequest];
[self.view addSubview:webView];
WKWebView rendering performance is noticeable in WebGL games and something that runs complex JavaScript algorithms, if you are using webview to load a simple html or website, you can continue to use UIWebView.
Here is a test app that can used to open any website using either UIWebView or WKWebView and you can compare performance, and then decide on upgrading your app to use WKWebView:
https://itunes.apple.com/app/id928647773?mt=8&at=10ltWQ

Here is how I transitioned from UIWebView to WKWebView.
Note: There is no property like UIWebView that you can drag onto your
storyboard, you have to do it programatically.
Make sure you import WebKit/WebKit.h into your header file.
This is my header file:
#import <WebKit/WebKit.h>
#interface ViewController : UIViewController
#property(strong,nonatomic) WKWebView *webView;
#property (strong, nonatomic) NSString *productURL;
#end
Here is my implementation file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.productURL = #"http://www.URL YOU WANT TO VIEW GOES HERE";
NSURL *url = [NSURL URLWithString:self.productURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
_webView = [[WKWebView alloc] initWithFrame:self.view.frame];
[_webView loadRequest:request];
_webView.frame = CGRectMake(self.view.frame.origin.x,self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
[self.view addSubview:_webView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

Step : 1 Import webkit in ViewController.swift
import WebKit
Step : 2 Declare variable of webView.
var webView : WKWebView!
Step : 3 Adding Delegate of WKNavigationDelegate
class ViewController: UIViewController , WKNavigationDelegate{
Step : 4 Adding code in ViewDidLoad.
let myBlog = "https://iosdevcenters.blogspot.com/"
let url = NSURL(string: myBlog)
let request = NSURLRequest(URL: url!)
// init and load request in webview.
webView = WKWebView(frame: self.view.frame)
webView.navigationDelegate = self
webView.loadRequest(request)
self.view.addSubview(webView)
self.view.sendSubviewToBack(webView)
Step : 5 Edit the info.plist adding
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>google.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>

Swift is not a requirement, everything works fine with Objective-C. UIWebView will continue to be supported, so there is no rush to migrate if you want to take your time. However, it will not get the javascript and scrolling performance enhancements of WKWebView.
For backwards compatibility, I have two properties for my view controller: a UIWebView and a WKWebView. I use the WKWebview only if the class exists:
if ([WKWebView class]) {
// do new webview stuff
} else {
// do old webview stuff
}
Whereas I used to have a UIWebViewDelegate, I also made it a WKNavigationDelegate and created the necessary methods.

WkWebView is much faster and reliable than UIWebview according to the Apple docs.
Here, I posted my WkWebViewController.
import UIKit
import WebKit
class WebPageViewController: UIViewController,UINavigationControllerDelegate,UINavigationBarDelegate,WKNavigationDelegate{
var webView: WKWebView?
var webUrl="http://www.nike.com"
override func viewWillAppear(animated: Bool){
super.viewWillAppear(true)
navigationController!.navigationBar.hidden = false
}
override func viewDidLoad()
{
/* Create our preferences on how the web page should be loaded */
let preferences = WKPreferences()
preferences.javaScriptEnabled = false
/* Create a configuration for our preferences */
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
/* Now instantiate the web view */
webView = WKWebView(frame: view.bounds, configuration: configuration)
if let theWebView = webView{
/* Load a web page into our web view */
let url = NSURL(string: self.webUrl)
let urlRequest = NSURLRequest(URL: url!)
theWebView.loadRequest(urlRequest)
theWebView.navigationDelegate = self
view.addSubview(theWebView)
}
}
/* Start the network activity indicator when the web view is loading */
func webView(webView: WKWebView,didStartProvisionalNavigation navigation: WKNavigation){
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
/* Stop the network activity indicator when the loading finishes */
func webView(webView: WKWebView,didFinishNavigation navigation: WKNavigation){
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
func webView(webView: WKWebView,
decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse,decisionHandler: ((WKNavigationResponsePolicy) -> Void)){
//print(navigationResponse.response.MIMEType)
decisionHandler(.Allow)
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
}
}

WKWebView using Swift in iOS 8..
The whole ViewController.swift file now looks like this:
import UIKit
import WebKit
class ViewController: UIViewController {
#IBOutlet var containerView : UIView! = nil
var webView: WKWebView?
override func loadView() {
super.loadView()
self.webView = WKWebView()
self.view = self.webView!
}
override func viewDidLoad() {
super.viewDidLoad()
var url = NSURL(string:"http://www.kinderas.com/")
var req = NSURLRequest(URL:url)
self.webView!.loadRequest(req)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Use some design patterns, you can mix UIWebView and WKWebView.
The key point is to design a unique browser interface.
But you should pay more attention to your app's current functionality,
for example: if your app using NSURLProtocol to enhance network ability, using WKWebView you have no chance to do the same thing. Because NSURLProtocol only effects the current process, and WKWebView using muliti-process architecture, the networking staff is in a seperate process.

You have to use WKWebView, which is available as of iOS8 in Framework 'WebKit' to get the speedup.
If you need backwards compatibility, you have to use UIWebView for iOS7 and older.
I set up a little code to provide the UIViewController frame for the new WKWebView. It can be installed via cocoapods. Have a look here:
STKWebKitViewController on github

Swift 4
let webView = WKWebView() // Set Frame as per requirment, I am leaving it for you
let url = URL(string: "http://www.google.com")!
webView.load(URLRequest(url: url))
view.addSubview(webView)

Related

Changing RootView controller Xamarin IOS app

My Xamarin IOS project uses a Storyboard. Its a tabbarcontroller app, I'd like to modify the number of tabs in my root UITabBarController.
You can't add or remove tabs if you crate the tabbarcontroller from a Storyboard. I'd like to replace the root view controller with one that is not created from the Storyboard. I'd still like the Storyboard for some of my other classes.
Instructions for creating an empty Xamarin project or removing the Storyboard from an Xamarin project using Visual Studio on the Mac do not work.
I think the new SceneDelegate removes the ability to set a root view controller in AppDelegate.
Thanks,
Gerry
Actually , you don't need to delete the Storyboard . If you want to set the RootViewController in AppDelegate , check the following code.
in Appdelegate
the following code will work before iOS 13.0
public bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
this.Window = new UIWindow(UIScreen.MainScreen.Bounds);
var MainViewController = new MyViewController();
this.Window.RootViewController = MainViewController;
this.Window.MakeKeyAndVisible();
return true;
}
And after iOS 13.0 , we should call the similar code in SceneDelegate , so add the following code to SceneDelegate at same time .
public void WillConnect (UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
this.Window = new UIWindow(new UIWindowScene(session,connectionOptions));
var MainViewController = new MyViewController();
this.Window.RootViewController = MainViewController;
this.Window.MakeKeyAndVisible();
// This delegate does not imply the connecting scene or session are new (see UIApplicationDelegate `GetConfiguration` instead).
}
In addition , if you want to change the RootViewController in run time (for example when click a button) .
We used Animation to make the process smooth
var MainController = new UITabBarController();
CATransition transition = CATransition.CreateAnimation();
transition.Duration = 0.3;
transition.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);
UIApplication.SharedApplication.KeyWindow.RootViewController = MainController;
UIApplication.SharedApplication.KeyWindow.Layer.AddAnimation(transition, "Animation");

Simple Swift WebView not working (Xcode 6 Beta 3)

I was writing a simple WebView in Swift, but everytime I try to launch it in the iOS Simulator I get these errors. What is going wrong?
import UIKit
class ViewController: UIViewController {
#IBOutlet var webview: UIWebView
var urlpath = "http://www.google.de"
func loadAddressURL(){
let requesturl = NSURL(string: urlpath)
let request = NSURLRequest(URL: requesturl)
webview.loadRequest(request)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadAddressURL()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Error:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
self uiwebview.ViewController 0x7987fc70 0x7987fc70
request NSURLRequest * 0x78ebfc40 0x78ebfc40
requesturl NSURL * "" 0x78ec0040
You simply haven't connected your UIWebView to the webview class property
Open the assistant editor, show your xib or storyboard at left, your view controller source file at right, click on the circle at the left of the webview property and drag into the UIWebView control. Once the connection is established, run the app and it should work now
I guess you get a white page because you test on the simulator. If you test on a real device you should be fine.
You need to put "!" after 'UIWebView' and unwrap "requesturl" to get your String value otherwise it's been an optional and you get error.
import UIKit
class WebViewController: UIViewController {
#IBOutlet var webview: UIWebView!
var urlpath: String = "http://www.google.de"
func loadAddressURL(){
let requesturl = NSURL(string: urlpath)
let request = NSURLRequest(URL: requesturl!)
webview.loadRequest(request)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadAddressURL()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Playing YouTube video in iPhone only app - loss of controls

The code below is used to put a small WebView on a View so that the user can tap it and the video opens in full screen mode and plays. All that works, but after 4 seconds of play the controls disappear and will not reappear (tapping, rotating...). Once the video finishes, the controls reappear and the 'Done' button becomes available. However once the WebView is disposed of and a new view loaded, that new view is unresponsive for up to 6 minutes.
[Preserve (AllMembers=true)]
public class YouTubeViewer : UIWebView
{
public static AppDelegate appDelegate = (AppDelegate) UIApplication.SharedApplication.Delegate;
public YouTubeViewer(string url, RectangleF frame)
{
Log.WriteLog("loading YouTubeView");
appDelegate.firstViewing = true;
this.UserInteractionEnabled = true;
this.BackgroundColor = UIColor.Clear;
this.Frame = frame;
string youTubeVideoHTML = #"<object width=""{1}"" height=""{2}""><param name=""movie""
value=""{0}""></param><embed
src=""{0}"" type=""application/x-shockwave-flash""
width=""{1}"" height=""{2}""</embed></object>";
string html = string.Format(youTubeVideoHTML, url, frame.Size.Width, frame.Size.Height);
this.LoadHtmlString(html, null);
}
}
Here is how the WebView is disposed of:
public void RemoveWebView(UIWebView inView)
{
try
{
Log.WriteLog("RemoveWebView");
NSUrlCache.SharedCache.RemoveAllCachedResponses();
NSUrlCache.SharedCache.DiskCapacity = 0;
NSUrlCache.SharedCache.MemoryCapacity = 0;
inView.LoadHtmlString("",null);
inView.EvaluateJavascript("var body=document.getElementsByTagName('body')[0];body.style.backgroundColor=(body.style.backgroundColor=='')?'white':'';");
inView.EvaluateJavascript("document.open();document.close()");
inView.StopLoading();
inView.Delegate = null;
inView.RemoveFromSuperview();
inView.Dispose();
}
catch(Exception ex)
{
Log.LogError("RemoveWebView",ex);
}
}
Thanks,
Rick
I talked to Xamarin and they suggested removing the override for orientation management in the AppDelegate.
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
{ /*... code ...*/ }
After I removed this override my application worked as expected when loading YouTube videos.
This resolved the issue for me. You can still control supported orientations via individual ViewController overrides and globally via the Info.plist file.
https://github.com/nishanil/YouTubePlayeriOS
Hope this sample helps you. It worked well for me.

Manually add more tabs to UITabController with Storyboard and Monotouch

I am currently using the new iOS 5 Storyboard approach to creating my Tabbed Application with Monotouch. I have developed two of my tab views in Xcode with Storyboard and linked them appropriately to the Tab Bar Controller. I also want to develop (in Xcode) a third tab view that would be shared among two additional tabs. I want to reuse the same layout, but display different data depending on which tab is selected (think something like a "Popular" and a "Recent" that would have the same layout but different data).
To do this, I figured I could add the tab manually twice after the Storyboard-driven tabs are added. How do I do this with the Storyboard approach? I'm not sure where in the code to do this since the loading of the Storyboard seems pretty transparent (i.e. no code in AppDelegate that I see). Or, is there another (easier/better) way to share a view between two tabs using the Storyboard approach?
I don't know Monotouch, but here's how I did it in Objective-c. I didn't find anything about this topic, so if something is wrong, people please comment :) By the way, I'm using ARC, so I don't manually manage memory! What I needed to achieve was like you, having a tab bar, loading the same viewController, but loading different data for each tab.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *root = (UITabBarController*)self.window.rootViewController;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
TeamViewController *home = [[mainStoryboard instantiateViewControllerWithIdentifier:#"Team"] initHome];
TeamViewController *visitor = [[mainStoryboard instantiateViewControllerWithIdentifier:#"Team"] initVisitor];
[root setViewControllers:[NSArray arrayWithObjects:home, visitor, nil] animated:NO];
UITabBar *tabs = root.tabBar;
UITabBarItem *homeTab = [tabs.items objectAtIndex:0];
UITabBarItem *visitorTab = [tabs.items objectAtIndex:1];
homeTab.title = #"Home team";
visitorTab.title = #"Visitor team";
return YES;
}
You can see I call initHome and initVisitor when I load my two TeamViewController, here is the code about it.
TeamViewController.h
#interface TeamViewController : UIViewController
{
enum
{
HOME,
VISITOR
};
int team;
}
TeamViewController.m
- (id)initHome
{
team = HOME;
return self;
}
- (id)initVisitor
{
team = VISITOR;
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if(team == HOME)
{
label.text = #"home data";
}
else if(team == VISITOR)
{
label.text = #"visitor data";
}
}
I don't know how well you can translate that to your project, but I hope you get the big picture of it :)
If you need to read a bit about how to access the first view controller using the storyboard: http://developer.apple.com/library/ios/#releasenotes/Miscellaneous/RN-AdoptingStoryboards/_index.html#//apple_ref/doc/uid/TP40011297
There is a section called "Accessing the First View Controller"

UIWebView display problem

I followed this tutorial http://bytedissident.theconspiracy5.com/2010/11/24/uiwebview-tutorial/ and when I run the simulation in Xcode, all that is displayed is a blank white screen. I'm wondering what could be wrong. Is there some sort of code connection to the internet that i'm missing? I really don't know what could be wrong.
WebPageViewController.m
#import "WebPageViewController.h"
#implementation WebPageViewController
#synthesize wView;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//a URL string
NSString *urlAddress = #"http://www.nd.edu";
//create URL object from string
NSURL *url = [NSURL URLWithString:urlAddress];
//URL Request Object created from your URL object
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//Load the request in the UIWebView
[wView loadRequest:requestObj];
//scale page to the device - can also be done in IB if preferred.
//wView.scalesPageToFit = YES;
}
Did you connect the .xib to the WebPageViewController class you created? You can check by selecting the .xib in the editor, and use identity inspector on 'File's Owner' in the Class field under Custom Class

Resources