Disable long press on WKWebView does not work - ios

I am trying to disable long press on my WKWebView as it allows to drag and drop of urls which is unwanted on my side. Just to clarify I don't want to disable the WKActionSheet but just the drag & drop feature of the links.
I tried:
webView.allowsLinkPreview = false
and
webView.evaluateJavaScript("document.body.style.webkitTouchCallout='none';")
but these does not seem to have any effect. Anybody have an idea? My app has minimum target iOS 15.

You can disable the selection on long press by setting a property on WKPreferences to false. Configure the WKWebView like you see below
func configureWebView() {
let webConfiguration = WKWebViewConfiguration()
webConfiguration.preferences.isTextInteractionEnabled = false
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
}

Related

Swift 5 Disable redirection to other apps inside WKWebView

First of all... My other question got marked as a duplicate even though the question suggested was the exact opposite, so:
Inside my webView I do not want to be redirected to other apps. WKWebView should never redirect to other Apps. Instead it should just open the url.
This is my webView:
lazy var webView: WKWebView = {
let webConfiguration = WKWebViewConfiguration()
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.translatesAutoresizingMaskIntoConstraints = false
return webView
}()

WKWebViews (multiple) - sharing cookies, localStorage in Swift

I'm having such an irritating issue. I have a UITabBar application with 5 tab bar buttons. Each hooked up to a viewController: FirstViewController, SecondViewController etc.
In each of the 5 ViewControllers I have a WKWebView. The 5 WKWebViews display sections of an eCommerce website. For example, tab 1 shows home, tab 2 shows cart etc.
In order for the app to work, I need all 5 WKWebViews to share all cookies, localStorage, IndexDB.
Right now, I'm doing this:
FirstViewController
class FirstViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
let uniqueProcessPool = WKProcessPool()
let websiteDataStoreA = WKWebsiteDataStore.nonPersistent()
..
class YourModelObject {
static let sharedInstance = YourModelObject()
let configA = WKWebViewConfiguration()
}
override func loadView() {
let model = YourModelObject.sharedInstance
//so I believe I'm doing the right thing here setting this
model.configA.processPool = uniqueProcessPool
model.configA.websiteDataStore = websiteDataStoreA
webView = WKWebView(frame: .zero, configuration: model.configA)
webView.navigationDelegate = self
self.webView.scrollView.delegate = self
view = webView
}
Now in SecondViewController, I simply do this:
override func loadView() {
//am I doing something wrong here - like creating another instance of configA, instead of re-using - and if so, how do I re-use what was created in FirstViewController?
let model = FirstViewController.YourModelObject.sharedInstance
webView = WKWebView(frame: .zero, configuration: model.configA)
webView.navigationDelegate = self
self.webView.scrollView.delegate = self
view = webView
}
When I run the project, the cookies, localStorage, IndexDB information from WKWebView in FirstViewController is not shared with SecondViewController - even though according to me, I'm re-using the same configA for both.
I think it is best if you use SFSafariViewController for your needs.
As the documentation states:
In iOS 9 and 10, it shares cookies and other website data with Safari.
It means, that it is going to use the same cookies and data from the Safari browser, which is even better. If I am not mistaken, the user can be logged in through Safari, and when he comes to your app, he will not have to log in again.
Here is the full documentation for it:
SFSafariViewController
Update:
If you still want to do what you already started, according to this answer here in Objective-C, this is the solution in Swift:
You need a place where you would store the persistent 'process pool'. In your case, it is YourModelObject singleton
class YourModelObject {
static let sharedInstance = YourModelObject()
let processPool = WKProcessPool()
}
Use the shared processPool before initializing the webView. This is the initialization function which you would call in the loadView() for every viewController:
override func loadView() {
super.loadView() //don't forget this line
setupWebView()
}
private func setupWebView() {
let config = WKWebViewConfiguration()
config.processPool = YourModelObject.sharedInstance.processPool
self.webView = WKWebView(frame: .zero, configuration: config)
self.webView.navigationDelegate = self
self.webView.scrollView.delegate = self
self.view = self.webView
}

WKWebView stays blank on simulator (allowing arbitrary loads)

I'm trying to load a WKWebView but my simulator (iPhone 6 - iOS 10.2) keeps displaying a blank screen. I can only see horizontal and vertical scrollers as if the webview was quite wide but empty.
App Transport Security Settings > Allow Arbitrary Loads is set to YES in the info.plist file.
Here is my code
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string: "https://www.google.co.uk")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
}
I tried several pieces of code found online or even downloaded working simple WKWebView xcodeproj, but I always have the same problem.
I can load the website (https://www.google.co.uk) in my browser. Also tried with general .com extension, or local .co.uk extension.
EDIT: works perfectly fine on the device but not on the simulator
I know it's an old post but it might be helpful for others.
So it most have something to do with override loadView() This method loads or creates a view and assigns it to the view property, but somehow in Simulator it doesn't do what it should!
Try removing loadView and Add the method body inside viewDidLoad() and it should be fine.
Check if you have a proxy running. I've had Charles Proxy running in the background and that caused this issue.

How to use WKWebView in interface-builder? [duplicate]

It seems that the IB object templates in XCode 6 beta are still creating old-style objects (UIWebView for iOS and WebView for OSX). Hopefully Apple will update them for the modern WebKit, but until then, what is the best way to create WKWebViews in Interface Builder? Should I create a basic view (UIView or NSView) and assign its type to WKWebView? Most of the examples I find online add it to a container view programmatically; is that better for some reason?
You are correct - it doesn't seem to work. If you look in the headers, you'll see:
- (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;
which implies that you can't instantiate one from a nib.
You'll have to do it by hand in viewDidLoad or loadView.
As pointed out by some, as of Xcode 6.4, WKWebView is still not available on Interface Builder. However, it is very easy to add them via code.
I'm just using this in my ViewController. Skipping Interface builder
import UIKit
import WebKit
class ViewController: UIViewController {
private var webView: WKWebView?
override func loadView() {
webView = WKWebView()
//If you want to implement the delegate
//webView?.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
if let url = URL(string: "https://google.com") {
let req = URLRequest(url: url)
webView?.load(req)
}
}
}
Info.plist
add in your Info.plist transport security setting
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Xcode 9.1+
Using interface builder
You can find WKWebView element in the Object library.
Add view programmatically with Swift 5
let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
Add view programmatically with Swift 5 (full sample)
import UIKit
import WebKit
class ViewController: UIViewController {
private weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
initWebView()
webView.loadPage(address: "http://apple.com")
}
private func initWebView() {
let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
view.addSubview(webView)
self.webView = webView
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
}
}
extension WKWebView {
func loadPage(address url: URL) { load(URLRequest(url: url)) }
func loadPage(address urlString: String) {
guard let url = URL(string: urlString) else { return }
loadPage(address: url)
}
}
Here's a simple Swift 3 version based on crx_au's excellent answer.
import WebKit
class WKWebView_IBWrapper: WKWebView {
required convenience init?(coder: NSCoder) {
let config = WKWebViewConfiguration()
//config.suppressesIncrementalRendering = true //any custom config you want to add
self.init(frame: .zero, configuration: config)
self.translatesAutoresizingMaskIntoConstraints = false
}
}
Create a UIView in Interface Builder, assign your constraints, and assign it WKWebView_IBWrapper as a custom class, like so:
With Xcode 8 this is now possible, but the means of achieving it is a little hacky to say the least. But hey, a working solution is a working solution, right? Let me explain.
WKWebView's initWithCoder: is no longer annotated as "NS_UNAVAILABLE". It now looks as shown below.
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
Start by subclassing WKWebView and override initWithCoder. Instead of calling super initWithCoder, you'll need to use a different init method, such as initWithFrame:configuration:. Quick example below.
- (instancetype)initWithCoder:(NSCoder *)coder
{
// An initial frame for initialization must be set, but it will be overridden
// below by the autolayout constraints set in interface builder.
CGRect frame = [[UIScreen mainScreen] bounds];
WKWebViewConfiguration *myConfiguration = [WKWebViewConfiguration new];
// Set any configuration parameters here, e.g.
// myConfiguration.dataDetectorTypes = WKDataDetectorTypeAll;
self = [super initWithFrame:frame configuration:myConfiguration];
// Apply constraints from interface builder.
self.translatesAutoresizingMaskIntoConstraints = NO;
return self;
}
Over in your Storyboard, use a UIView and give it a custom class of your new subclass. The rest is business as usual (setting auto-layout constraints, linking the view to an outlet in a controller, etc).
Finally, WKWebView scales content differently to UIWebView. Many people are likely going to want to follow the simple advice in Suppress WKWebView from scaling content to render at same magnification as UIWebView does to make WKWebView more closely follow the UIWebView behaviour in this regard.
If you still face this issue in recent versions of Xcode, i.e. v9.2+, simply import Webkit to your ViewController:
#import <WebKit/WebKit.h>
Before the fix:
After the fix:
This is now apparently fixed in Xcode 9b4. The release notes say "WKWebView is available in the iOS object library."
I haven't looked deeper to see if it requires iOS 11 or is backward compatible yet.
You can instantiate and configure a WKWebView in IB since Xcode 9, no need to do it in code.
Note that your deployment target has to be higher than iOS 10 though or you will get a compile-time error.
In XCode Version 9.0.1 WKWebView is available on Interface Builder.
I've linked WebKit, now it's working!
Not sure if this helps but I solved the problem for me by including the WebKit framework for the target. Don't embed it just link the reference. I still use the WebView object in IB and drop it on the ViewController I'm embedding it in and I've never had a problem...
I've worked on 4 WKWebView MacOS projects now and the WebView has worked properly in each project.
This worked for me in Xcode 7.2...
First add the web view as a UIWebView outlet in the storyboard / IB. This will give you a property like this:
#property (weak, nonatomic) IBOutlet UIWebView *webView;
Then just edit your code to change it to a WKWebView.
#property (weak, nonatomic) IBOutlet WKWebView *webView;
You should also change the custom class to WKWebView in the Identity inspector.

WKWebView in Interface Builder

It seems that the IB object templates in XCode 6 beta are still creating old-style objects (UIWebView for iOS and WebView for OSX). Hopefully Apple will update them for the modern WebKit, but until then, what is the best way to create WKWebViews in Interface Builder? Should I create a basic view (UIView or NSView) and assign its type to WKWebView? Most of the examples I find online add it to a container view programmatically; is that better for some reason?
You are correct - it doesn't seem to work. If you look in the headers, you'll see:
- (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;
which implies that you can't instantiate one from a nib.
You'll have to do it by hand in viewDidLoad or loadView.
As pointed out by some, as of Xcode 6.4, WKWebView is still not available on Interface Builder. However, it is very easy to add them via code.
I'm just using this in my ViewController. Skipping Interface builder
import UIKit
import WebKit
class ViewController: UIViewController {
private var webView: WKWebView?
override func loadView() {
webView = WKWebView()
//If you want to implement the delegate
//webView?.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
if let url = URL(string: "https://google.com") {
let req = URLRequest(url: url)
webView?.load(req)
}
}
}
Info.plist
add in your Info.plist transport security setting
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Xcode 9.1+
Using interface builder
You can find WKWebView element in the Object library.
Add view programmatically with Swift 5
let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
Add view programmatically with Swift 5 (full sample)
import UIKit
import WebKit
class ViewController: UIViewController {
private weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
initWebView()
webView.loadPage(address: "http://apple.com")
}
private func initWebView() {
let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
view.addSubview(webView)
self.webView = webView
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
}
}
extension WKWebView {
func loadPage(address url: URL) { load(URLRequest(url: url)) }
func loadPage(address urlString: String) {
guard let url = URL(string: urlString) else { return }
loadPage(address: url)
}
}
Here's a simple Swift 3 version based on crx_au's excellent answer.
import WebKit
class WKWebView_IBWrapper: WKWebView {
required convenience init?(coder: NSCoder) {
let config = WKWebViewConfiguration()
//config.suppressesIncrementalRendering = true //any custom config you want to add
self.init(frame: .zero, configuration: config)
self.translatesAutoresizingMaskIntoConstraints = false
}
}
Create a UIView in Interface Builder, assign your constraints, and assign it WKWebView_IBWrapper as a custom class, like so:
With Xcode 8 this is now possible, but the means of achieving it is a little hacky to say the least. But hey, a working solution is a working solution, right? Let me explain.
WKWebView's initWithCoder: is no longer annotated as "NS_UNAVAILABLE". It now looks as shown below.
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
Start by subclassing WKWebView and override initWithCoder. Instead of calling super initWithCoder, you'll need to use a different init method, such as initWithFrame:configuration:. Quick example below.
- (instancetype)initWithCoder:(NSCoder *)coder
{
// An initial frame for initialization must be set, but it will be overridden
// below by the autolayout constraints set in interface builder.
CGRect frame = [[UIScreen mainScreen] bounds];
WKWebViewConfiguration *myConfiguration = [WKWebViewConfiguration new];
// Set any configuration parameters here, e.g.
// myConfiguration.dataDetectorTypes = WKDataDetectorTypeAll;
self = [super initWithFrame:frame configuration:myConfiguration];
// Apply constraints from interface builder.
self.translatesAutoresizingMaskIntoConstraints = NO;
return self;
}
Over in your Storyboard, use a UIView and give it a custom class of your new subclass. The rest is business as usual (setting auto-layout constraints, linking the view to an outlet in a controller, etc).
Finally, WKWebView scales content differently to UIWebView. Many people are likely going to want to follow the simple advice in Suppress WKWebView from scaling content to render at same magnification as UIWebView does to make WKWebView more closely follow the UIWebView behaviour in this regard.
If you still face this issue in recent versions of Xcode, i.e. v9.2+, simply import Webkit to your ViewController:
#import <WebKit/WebKit.h>
Before the fix:
After the fix:
This is now apparently fixed in Xcode 9b4. The release notes say "WKWebView is available in the iOS object library."
I haven't looked deeper to see if it requires iOS 11 or is backward compatible yet.
You can instantiate and configure a WKWebView in IB since Xcode 9, no need to do it in code.
Note that your deployment target has to be higher than iOS 10 though or you will get a compile-time error.
In XCode Version 9.0.1 WKWebView is available on Interface Builder.
I've linked WebKit, now it's working!
Not sure if this helps but I solved the problem for me by including the WebKit framework for the target. Don't embed it just link the reference. I still use the WebView object in IB and drop it on the ViewController I'm embedding it in and I've never had a problem...
I've worked on 4 WKWebView MacOS projects now and the WebView has worked properly in each project.
This worked for me in Xcode 7.2...
First add the web view as a UIWebView outlet in the storyboard / IB. This will give you a property like this:
#property (weak, nonatomic) IBOutlet UIWebView *webView;
Then just edit your code to change it to a WKWebView.
#property (weak, nonatomic) IBOutlet WKWebView *webView;
You should also change the custom class to WKWebView in the Identity inspector.

Resources