Disable magnification gesture in WKWebView

IosWebkitIos8Wkwebview

Ios Problem Overview


I'm looking for a way to disable the "pinch to zoom" magnification gesture on the iOS implementation of WKWebView. There is a magnification BOOL property available for OS X but it doesn't seem to be available on iOS.

WKWebView.h

#if !TARGET_OS_IPHONE
/* @abstract A Boolean value indicating whether magnify gestures will
 change the web view's magnification.
 @discussion It is possible to set the magnification property even if
 allowsMagnification is set to NO.
 The default value is NO.
 */
@property (nonatomic) BOOL allowsMagnification;

I've, also, tried look at the WKWebView's gesture recognizers but that seems to be turning up an empty array. I'm assuming the actual recognizers are bured deeper in the component's structure (fairly complex, by the looks of it) and would rather not go digging for them if at all possible.

I know of possible hacks that could potentially disable the gesture from firing (selectively passing gestures to the WebView, add child view to capture pinch gesture, etc) but I've always found those introduce lag into the event and want to keep the implementation as clean/hack free as possible.

Ios Solutions


Solution 1 - Ios

You can prevent your users from zooming by setting the delegate of your WKWebKit's UIScrollView and implementing viewForZooming(in:) as in the following:

class MyClass {
    let webView = WKWebView()

    init() {
        super.init()
        webView.scrollView.delegate = self
    }

    deinit() {
        // Without this, it'll crash when your MyClass instance is deinit'd
        webView.scrollView.delegate = nil
    }
}

extension MyClass: UIScrollViewDelegate {
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return nil
    }
}

Solution 2 - Ios

I have tried setting minimumZoomScale and maximumZoomScale properties of UIScrollView to 1 or isMultipleTouchEnabled property of UIView to false or returning nil from invoking viewForZooming(in:) of UIScrollViewDelegate but none worked. In my case, after several trial and error, the following works in my case [Tested on iOS 10.3]:

class MyViewController: UIViewController {
   var webView: WKWebView?

   override viewDidLoad() {
      super.viewDidLoad()
   
      //...
      self.webView.scrollView.delegate = self
      //...
   }
}

extension MyViewController: UIScrollViewDelegate { 
   func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
      scrollView.pinchGestureRecognizer?.isEnabled = false
   }
}

Solution 3 - Ios

The below answer no longer works in iOS 10 beta.

> To improve accessibility on websites in Safari, users can now > pinch-to-zoom even when a website sets user-scalable=no in the > viewport.


WKWebView seems to respect the viewport meta tag the same way Mobile Safari does (as to be expected). So, I found injecting that tag into the DOM through javascript after a page load does the trick. I would be weary of this solution unless you know exactly what HTML is being loaded into the webview, otherwise I suspect it would have unintended consequences. In my case, I'm loading HTML strings, so I can just add it to the HTML I ship with the app.

To do it generically for any webpage:

- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    NSString *javascript = @"var meta = document.createElement('meta');meta.setAttribute('name', 'viewport');meta.setAttribute('content', 'width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no');document.getElementsByTagName('head')[0].appendChild(meta);";
    
    [webView evaluateJavaScript:javascript completionHandler:nil];
}

It might be wise to take a look at what kind of navigation has just been completed, since only a new page will need this javascript executed.

Solution 4 - Ios

Complete working code to disable zooming in WkWebView in Swift.

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
       
        let source: String = "var meta = document.createElement('meta');" +
            "meta.name = 'viewport';" +
            "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';" +
            "var head = document.getElementsByTagName('head')[0];" + "head.appendChild(meta);";
        
        let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
        webView.configuration.userContentController.addUserScript(script)
        
        view = webView
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myUrl = URL(string: "https://www.google.com")
        
        let myRequest = URLRequest(url: myUrl!)
        webView.load(myRequest)
    }
}

Solution 5 - Ios

The native solutions were not working for me, and injecting JS is not ideal. I noticed that when a zoom occurs and my delegate is called, the pinchGestureRecognizer is enabled even though I disabled it when initializing the webview. To fix this, I set it to disabled whenever a zoom starts:

extension ViewController: UIScrollViewDelegate {

    // disable zooming in webview
    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
        scrollView.pinchGestureRecognizer?.isEnabled = false
    }
}

Solution 6 - Ios

Full Swift 3 / iOS 10 version of Landschaft's answer:

import UIKit
import WebKit

class MyViewController: UIViewController, UIScrollViewDelegate {
    
    var webView = WKWebView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(webView)
        webView.scrollView.delegate = self
    }
    
    // Disable zooming in webView
    func viewForZooming(in: UIScrollView) -> UIView? {
        return nil
    }
}

Solution 7 - Ios

You can use UIScrollViewDelegate for this. First assign delegate to your webview in viewDidLoad() or any other suitable method as:

class LoginViewController: UIViewController, WKUIDelegate, UIScrollViewDelegate {

  override func viewDidLoad() {
      webView.scrollView.delegate = self 
  }

  //Add this delegate method in your view controller
  func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
      scrollView.pinchGestureRecognizer?.isEnabled = false
  }
}

Solution 8 - Ios

In case you display a local html you could just modify this html and put this meta tag to your html:

<head>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
</head>

Solution 9 - Ios

A simple way to prevent zooming

override func viewDidLoad() {
    super.viewDidLoad()
    
    webView.scrollView.delegate = self
}

func scrollViewDidZoom(_ scrollView: UIScrollView) {
        
    scrollView.setZoomScale(1.0, animated: false)
}

Solution 10 - Ios

Swift 2.0

extension WKWebView {
    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
        return nil
    }
}

Solution 11 - Ios

If it's not important for you to handle links inside html (say you want to display text only) the simplest would be to turn off user interaction webView.userInteractionEnabled = false

Solution 12 - Ios

this is how I disabled zoom for Swift3 view controller for one-webview-only app

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate, UIScrollViewDelegate {

    @IBOutlet var webView: WKWebView!

    override func loadView() {
        webView = WKWebView()
        webView.navigationDelegate = self
        view = webView
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        webView.scrollView.delegate = self
    }
    
    func viewForZooming(in: UIScrollView) -> UIView? {
        return nil;
    }    

}

Solution 13 - Ios

I don't have enough reputation to add comments to answers, but I wanted to mention that Kevin's solution (meta viewport) no longer works in iOS 10 beta. Landschaft's solution is working for me, though!

> Since the JS solution uses W3C standards, it should always be supported.

Ah, Kevin, I wish that were how these things worked.

More here: https://stackoverflow.com/a/37859168/1389714

Solution 14 - Ios

Disable double tap to zoom gesture by require failure of gesture recognizer. It will prevent the browser to take action when user double tap.

import UIKit

class DisableDoubleTapRecognizer : UITapGestureRecognizer, UIGestureRecognizerDelegate{
    override init(target: Any?, action: Selector?) {
        super.init(target: target, action: action)
    }
    
    init() {
        super.init(target:nil, action: nil)
        self.numberOfTapsRequired = 2;
        self.delegate = self;
    }
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true;
    }
}

//in your view controller
override func viewDidLoad() {
        super.viewDidLoad()
        
        webView.addGestureRecognizer(DisableDoubleTapRecognizer())
    }

Solution 15 - Ios

Here's a slightly modified version of Gulshan Kumar's answer that worked for me in iOS 12.1.2, and it also prevents zooming due to double-taps and rotation. In my example, I just inject the script directly into the web view. I adjusted the scale to 60%, and there's no need to use concatenation in building up the source string.

let source = "var meta = document.createElement('meta'); meta.name = 'viewport'; meta.content = 'width=device-width, initial-scale=0.6, maximum-scale=0.6, user-scalable=no'; var head = document.getElementsByTagName('head')[0]; head.appendChild(meta);"

let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true)

webView.configuration.userContentController.addUserScript(script)

Solution 16 - Ios

We need to change the delegate call with following signatures in XCode 8:

override func viewForZooming(in scrollView: UIScrollView) -> UIView? { return nil }

Solution 17 - Ios

The application I work on needed the view to be zoomed by Javascript. The accepted answer blocked zooming by JavaScript from inside the page too. I only needed to disable pinch gesture by the user of the appliction. The only solution I've found is to disable the gesture of the web view after the page has loaded:

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    /* disable pinch gesture recognizer to allow zooming the web view by code but prevent zooming it by the user */
    for (UIGestureRecognizer *gr in self.webView.scrollView.gestureRecognizers) {
        if ([gr isKindOfClass:[UIPinchGestureRecognizer class]]) {
            gr.enabled = NO;
        }
    }
}

Solution 18 - Ios

If it is better to disbaled the Pinch zoom when the webpage finish loading, and it can also specify which particular URL is going to be disbaled

public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
     if let url = webView.url, url.absoluteString == "***" {                       
        webView.scrollView.pinchGestureRecognizer?.isEnabled = false
     }

    print("finish")
} 

Solution 19 - Ios

For obj-c., add UIScrollViewDelegate protocol.

/* somewhere else, for example in viewDidLoad() */
_wkWeb.scrollView.delegate = self;

..

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
    scrollView.pinchGestureRecognizer.enabled = NO;
}

Solution 20 - Ios

This worked for me. https://gist.github.com/paulofierro/5b642dcde5ee9e86a130

  let source: String = "var meta = document.createElement('meta');" +
  "meta.name = 'viewport';" +
  "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';" +
  "var head = document.getElementsByTagName('head')[0];" + "head.appendChild(meta);";
  let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
  let userContentController: WKUserContentController = WKUserContentController()
  let conf = WKWebViewConfiguration()
  conf.userContentController = userContentController
  userContentController.addUserScript(script)
  let webView = WKWebView(frame: CGRect.zero, configuration: conf)

Solution 21 - Ios

If the webview is user for read-only purposes, i.e, just for displaying the web content use

webView.isUserInteractionEnabled = false

By this the zoom gesture won't work on it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKevinView Question on Stackoverflow
Solution 1 - IosLandschaftView Answer on Stackoverflow
Solution 2 - IosyohannesView Answer on Stackoverflow
Solution 3 - IosKevinView Answer on Stackoverflow
Solution 4 - IosGulshan KumarView Answer on Stackoverflow
Solution 5 - Iospulse4lifeView Answer on Stackoverflow
Solution 6 - IosSimon EpskampView Answer on Stackoverflow
Solution 7 - IosYakup AdView Answer on Stackoverflow
Solution 8 - IosLeszek SzaryView Answer on Stackoverflow
Solution 9 - IosWaqar AhmedView Answer on Stackoverflow
Solution 10 - IosquemefulView Answer on Stackoverflow
Solution 11 - IoslvpView Answer on Stackoverflow
Solution 12 - IosgodblessstrawberryView Answer on Stackoverflow
Solution 13 - IosmarkView Answer on Stackoverflow
Solution 14 - Iosuser3543806View Answer on Stackoverflow
Solution 15 - IosScott GardnerView Answer on Stackoverflow
Solution 16 - IosBriansterView Answer on Stackoverflow
Solution 17 - IosUrKView Answer on Stackoverflow
Solution 18 - IosJinView Answer on Stackoverflow
Solution 19 - IosDody Rachmat WicaksonoView Answer on Stackoverflow
Solution 20 - IosJonnyView Answer on Stackoverflow
Solution 21 - IosSameer BhideView Answer on Stackoverflow