WKWebView 中的链接随机不可点击

Links in WKWebView randomly not clickable

在我用 Swift 2.1 编写的 iOS 应用程序中,我使用 WKWebView 加载 HTML 字符串,该字符串是使用 JSON 解析器从 wordpress 博客加载的.

我实现了委托方法 func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) { 并将 WKWebView 的导航委托设置为自身以处理 link 次按下。

现在,当我打开包含 WKWebView 的 ViewController 时,此委托方法被调用一次,这是您在加载 webView 时所期望的行为 - 因此委托似乎设置正确。

我现在的问题是,大多数时候 webView 包含的 link 是 不可点击的 。您通常会期望 灰色背景出现 ,当您按下 link 时,如下图所示。但大多数时候,当我按下 link 时,灰色背景不会出现,所以当我触摸时,委托方法不会被调用。这个问题肯定与 WKNavigationDelegate 的错误配置无关,因为有时 link 选择工作正常(大约 10 %)。

您知道为什么 link 有时不可点击,有时可点击(10% 的情况)吗?


当您将内容加载到 WKWebView 时,您可能会为您的网页设置错误的基础URL,这会导致无效 link,为了使其正常工作,您应该baseURL 为您的内容设置正确的 http 或 https URL,对于那些工作 link 只是因为他们的 href 中有完整的 url。

这里是 html 文本加载到 WKWebView 来说明效果:

webView.loadHTMLString(content, baseURL: NSURL(string: ""));

内容:

<html lang="en"><head><meta name="viewport" content="width=device-width, initial-scale=1"></head>
    <body>
        <h1> click testing </h1>
        <ul>
            <li><a href="http://www.w3schools.com">Visit W3Schools</a></li>
            <li><a href="/about">Un-Clickable without baseURL</a></li>
            <li><a href="about://about">Clickable without baseURL</a></li>
        </ul>
    </body>
</html>

我假设你想要的是在某个地方有一些描述或文本,并允许在文本的某些部分内制作 clickable/tapped 而你想要使用 WKWebView .

这个问题我也是用WKWebView在很久以前做的某个app中解决的,当然有多种解决方法,这只是其中一种,没有别的。

正如某些人对您所说,您可以使用 loadHTMLString 函数以 JSON 或任何您想要的方式从您的服务器加载 HTML 字符串,但是非常重要的是HTML 格式正确无误。

关于您的评论 loadHTMLString 功能的一个非常重要的一点 ...您通常会希望在您按 link 时出现灰色背景见下图 ,如果 HTML 没有某种 CSS 样式,则不会发生,因为如果没有,它具有任何 [=15] 的默认样式=].

那么让我们看下面的代码:

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

   var wkWebView: WKWebView!

   override func viewDidLoad() {
       super.viewDidLoad()

       // The part of the HTMl you want to show
       let description = "Let's start to visit the <a href=\"http://www.apple.com/\">Apple</a> website first and then if you want the <a href=\"http://www.w3schools.com\">Developer Apple</a> website."


       // The default HTML with body and styles formatted and the description inside using string interpolation.
       let htmlString = "<html><head><style type=\"text/css\">body {font-family: \"Lato-Regular\";font-size: 35px;color: #333333;margin: 0;}a {text-decoration: none; color: #999;}</style></head><body>\(description)</body></html>"

       let preferences = WKPreferences()
       preferences.javaScriptEnabled = false

       // Configuration for any preferences you want to set
       let configuration = WKWebViewConfiguration()
       configuration.preferences = preferences

       wkWebView = WKWebView(frame: CGRectMake(5, 35, self.view.bounds.width, self.view.bounds.height), configuration: configuration)

       if let theWebView = wkWebView {
          theWebView.loadHTMLString(htmlString, baseURL: NSURL())
          theWebView.navigationDelegate = self
          self.view.addSubview(theWebView)
       }
   }

   override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      // Dispose of any resources that can be recreated.
   }

   func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
      UIApplication.sharedApplication().networkActivityIndicatorVisible = true
   }


   func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
      UIApplication.sharedApplication().networkActivityIndicatorVisible = false
   }

   func webView(webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: NSError) {
     let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .Alert)
     alert.addAction(UIAlertAction(title: "Ok", style: .Default) { (UIAlertAction) -> Void in
         UIApplication.sharedApplication().networkActivityIndicatorVisible = false
     })

     presentViewController(alert, animated: true, completion: nil)
   }
}

在上面的代码中,我将变量 description 设置为一些带有可点击元素的文本,并添加了两个委托方法 didStartProvisionalNavigationdidFinishNavigation 以显示 networkActivityIndicatorVisible 在状态栏中显示点击可点击文本时页面加载的一些进度。值得注意的是,在 htmlString 变量中我设置了一些样式来显示带有某些颜色和字体的 <a>,这取决于您。

我也添加了 didFailProvisionalNavigation 功能以在某些情况下显示警报 url 无效,例如新添加的 HTTPTransportSecurityLayer 是 iOS 9只允许 https,在某些情况下你可以用它来验证 url 并知道错误的原因。

结果是正常的以下文本UIViewController:

并且当您点击任何灰色文本时,url 将加载到同一个 WKWebView 中,当然您可以将其个性化为在在浏览器中使用或其他方式,由您决定。

然后点击 Apple:

可以立即看到结果

如果您点击 Developer Apple,您可以看到正在运行的警报,因为 http 协议在 HTTPTransportSecurityLayer 在 iOS 9:

如果您需要,我已准备好上传到 Github 的项目。希望对你有帮助。

解决方案是删除 contentInset 的使用。不幸的是,WKWebView 并不是为了处理它的使用而设计的(当 contentInset 为负数时),因此我们必须通过简单地向 [=26= 添加一个空的 div 来使用变通方法]字符串,就这样:

<div style='width:100%;height:100px'></div>

如果在不重新加载整个页面的情况下更改底部插图,可以先在底部插入一个巨大的 div,然后通过添加正数 contentInset 来补偿它。这正是这个扩展的作用:

extension WKWebView {

   /// The maximum possible height of the webView bottom content inset
   @nonobjc static let maxBottomHeight: CGFloat = 20000

   /// Provides an easy way to set the real bottom inset of the webView. Due to a bug in WKWebView framework, we first insert an emtpy div with the size WKWebView.maxContentSize on the bottom of the webView and compensate it by setting a positive contentInset (should be positive, otherwise the bug will be there again).
   func set(bottomInset inset: CGFloat) {

       self.scrollView.contentInset.bottom = inset - WKWebView.maxBottomHeight
   }
}

不要忘记在 html 字符串的末尾添加 div

let htmlFooter = "<div style='width:100%;height:\(String(describing: WKWebView.maxBottomHeight))px'></div></body></html>"