Отключить жест увеличения в WKWebView

Я ищу способ отключить жест увеличения "щепотка для увеличения" в реализации WKWebView для iOS. Существует свойство увеличения BOOL, доступное для OS X, но оно не доступно на 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;

Я также попытался взглянуть на распознаватели жестов WKWebView, но это, кажется, приводит к появлению пустого массива. Я предполагаю, что фактические распознаватели погружены глубже в структуру компонента (довольно сложная, по внешнему виду) и не стали бы копать их, если это вообще возможно.

Мне известны возможные хаки, которые потенциально могут отключить запуск жеста (выборочная передача жестов в WebView, добавление дочернего представления для захвата жеста повышения и т. Д.), Но я всегда обнаруживал, что они вводят задержку в событие и хотят сохранить реализацию как очистить / взломать как можно скорее.

21 ответ

Решение

Вы можете запретить пользователям масштабирование, установив делегат UIScrollView вашего WKWebKit и реализовав viewForZooming(in:) как в следующем:

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
    }
}

Я пробовал настройку minimumZoomScale а также maximumZoomScale свойства UIScrollView в 1 или же isMultipleTouchEnabled собственностью UIView в false или возвращаясь nil от вызова viewForZooming(in:) из UIScrollViewDelegate но никто не работал. В моем случае, после нескольких проб и ошибок, в моем случае работает [Проверено на 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
   }
}

Приведенный ниже ответ больше не работает в iOS 10 beta.

Чтобы улучшить доступность веб-сайтов в Safari, пользователи теперь могут увеличивать масштаб, даже когда веб-сайт устанавливает пользовательский масштабируемый = нет в окне просмотра.


WKWebView, похоже, учитывает метатег viewport так же, как и Mobile Safari (как и следовало ожидать). Итак, я обнаружил, что этот тег внедряется в DOM через javascript после загрузки страницы. Я бы устал от этого решения, если вы не знаете точно, какой HTML-код загружается в веб-просмотр, иначе я подозреваю, что это будет иметь непредвиденные последствия. В моем случае я загружаю HTML-строки, поэтому я могу просто добавить их в HTML-код, который поставляется вместе с приложением.

Чтобы сделать это в общем для любой веб-страницы:

- (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, maximum-scale=1.0, user-scalable=no');document.getElementsByTagName('head')[0].appendChild(meta);";

    [webView evaluateJavaScript:javascript completionHandler:nil];
}

Возможно, было бы целесообразно взглянуть на то, какой тип навигации только что завершен, так как только на новой странице потребуется выполнение этого javascript.

Полный рабочий код для отключения масштабирования в WkWebView в Swift

импорт UIKit импорт WebKit

Класс ViewController: UIViewController, WKUIDelegate {

// @IBOutlet var eventWkWebView: WKWebView!

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()
    // Do any additional setup after loading the view, typically from a nib.
    //  view.addSubview(eventWkWebView)


    let myUrl = URL(string: "https://www.google.com")

    let myRequest = URLRequest(url: myUrl!)
    webView.load(myRequest)

}

}

Нативные решения не работали для меня, и инъекция JS не идеальна. Я заметил, что когда происходит масштабирование и вызывается мой делегат, pinchGestureRecognizer включается, хотя я отключил его при инициализации веб-просмотра. Чтобы это исправить, я устанавливаю его отключенным при каждом увеличении:

extension ViewController: UIScrollViewDelegate {

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

Полная версия ответа Landschaft от Swift 3 / iOS 10:

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;
    }
}

Вы можете использовать UIScrollViewDelegate для этого. Сначала назначьте делегата вашему веб-представлению в viewDidLoad() или любым другим подходящим способом:

class LoginViewController: UIViewController, WKUIDelegate, UIScrollViewDelegate {
  override func viewDidLoad() {
  webView.scrollView.delegate = self 
  }

 //And add this delegate method in your controller: 

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

В случае, если вы отображаете локальный HTML, вы можете просто изменить этот HTML и поместить этот метатег в свой HTML:

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

Простой способ предотвратить масштабирование

override func viewDidLoad() {
    super.viewDidLoad()

    webView.scrollView.delegate = self
}

func scrollViewDidZoom(_ scrollView: UIScrollView) {

    scrollView.setZoomScale(1.0, animated: false)
}

Swift 2.0

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

Вот как я отключил масштабирование для контроллера представления Swift3 для приложения только для одного веб-просмотра

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;
    }    

}

Если вам не важно обрабатывать ссылки внутри html (скажем, вы хотите отображать только текст), самым простым будет отключить взаимодействие с пользователемwebView.userInteractionEnabled = false

Вот слегка измененная версия ответа Gulshan Kumar, которая работала для меня в iOS 12.1.2, и она также предотвращает масштабирование из-за двойного нажатия и поворота. В моем примере я просто внедряю скрипт прямо в веб-представление. Я настроил масштаб до 60%, и нет необходимости использовать конкатенацию при построении исходной строки.

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)

Отключите двойное касание, чтобы увеличить жест, если требуется сбой распознавателя жестов. Это предотвратит действие браузера, когда пользователь дважды нажмет.

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())
    }

У меня недостаточно репутации, чтобы добавлять комментарии к ответам, но я хотел бы упомянуть, что решение Кевина (meta viewport) больше не работает в бета-версии iOS 10. Однако решение Landschaft работает на меня!

Поскольку решение JS использует стандарты W3C, оно всегда должно поддерживаться.

Ах, Кевин, я бы хотел, чтобы все это работало.

Больше здесь: /questions/36270519/otklyuchit-masshtabirovanie-okna-prosmotra-ios-10-safari/36270532#36270532

Нам нужно изменить вызов делегата с помощью следующих подписей в XCode 8:

переопределить func viewForZooming(в scrollView: UIScrollView) -> UIView? {вернуть ноль}

Если лучше отключить масштабирование Pinch по окончании загрузки веб-страницы, а также можно указать, какой конкретный URL будет отключен

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

    print("finish")
} 

Для obj-c. добавьте UIScrollViewDelegateпротокол.

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

..

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

Для приложения, над которым я работаю, Javascript необходимо было увеличить масштаб представления. Принятый ответ также заблокировал масштабирование с помощью JavaScript изнутри страницы. Мне нужно было только отключить жест щипка пользователем приложения. Единственное решение, которое я нашел, это отключить жест веб-просмотра после загрузки страницы:

- (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;
        }
    }
}

Это сработало для меня. 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)

Если веб-просмотр является пользователем только для чтения, то есть только для отображения веб-контента, используйте

webView.isUserInteractionEnabled = false

При этом жест масштабирования не сработает.

Другие вопросы по тегам