【问题标题】:How to disable iOS 11 and iOS 12 Drag & Drop in WKWebView?如何在 WKWebView 中禁用 iOS 11 和 iOS 12 拖放?
【发布时间】:2018-04-19 00:54:03
【问题描述】:

在 iOS 11 和 12 上长按 WKWebView 中的图像或链接会启动拖放会话(用户可以拖动图像或链接)。我怎样才能禁用它?

【问题讨论】:

    标签: ios drag-and-drop ios11 wkwebview


    【解决方案1】:

    我确实找到了solution that involves method swizzling,但也可以在 WKWebView 中禁用拖放而无需任何调整。

    注意:请参阅下面的 iOS 12.2+ 特别说明

    WKContentViewWKWebViewWKScrollView 的私有子视图 — 具有 interactions 属性,就像 iOS 11+ 中的任何其他 UIView 一样。 interactions 属性包含 UIDragInteractionUIDropInteraction。只需在UIDragInteraction 上将enabled 设置为false 即可。

    我们不希望访问任何私有 API 并使代码尽可能可靠。

    假设您的WKWebView 被称为webView

    if (@available(iOS 11.0, *)) {        
        // Step 1: Find the WKScrollView - it's a subclass of UIScrollView
        UIView *webScrollView = nil;
    
        for (UIView *subview in webView.subviews) {
            if ([subview isKindOfClass:[UIScrollView class]]) {
                webScrollView = subview;
                break;
            }
        }
    
        if (webScrollView) {
            // Step 2: Find the WKContentView
            UIView *contentView = nil;
    
            // We don't want to trigger any private API usage warnings, so instead of checking
            // for the subview's type, we simply look for the one that has two "interactions" (drag and drop)
            for (UIView *subview in webScrollView.subviews) {
                if ([subview.interactions count] > 1) {
                    contentView = subview;
                    break;
                }
            }
    
            if (contentView) {
                // Step 3: Find and disable the drag interaction
                for (id<UIInteraction> interaction in contentView.interactions) {
                    if ([interaction isKindOfClass:[UIDragInteraction class]]) {
                        ((UIDragInteraction *) interaction).enabled = NO;
                        break;
                    }
                }
            }
        }
    }
    

    就是这样!

    iOS 12.2+ 特别说明

    以上代码在 iOS 12.2 上仍然有效,但调用它很重要何时。在 iOS 12.1 及更低版本上,您可以在创建 WKWebView 后立即调用此代码。这已经不可能了。 WKContentViewinteractions 数组在首次创建时为空。仅在将WKWebView 添加到附加到UIWindow 的视图层次结构后才会填充它 - 仅将其添加到尚不属于可见视图层次结构的超级视图是不够的。在视图控制器中,viewDidAppear 很可能是一个安全的调用它的地方。

    我是怎么发现的?

    这是输出:

    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 50.1  
      * frame #0: 0x00000001115b726c WebKit`-[WKContentView(WKInteraction) setupDataInteractionDelegates]  
        frame #1: 0x00000001115a8852 WebKit`-[WKContentView(WKInteraction) setupInteraction] + 1026  
        frame #2: 0x00000001115a5155 WebKit`-[WKContentView didMoveToWindow] + 79 
    

    很明显,UIDragInteraction 的创建和添加是由视图移动到(被添加到)窗口触发的。

    【讨论】:

      【解决方案2】:

      这很好用! 感谢@basha 的快速版本。

      我也做了同样的事情,但使用了一些 compactMaps 来减少 if 语句的深度和警卫以摆脱强制展开。

      private func disableDragAndDropInteraction() {
          var webScrollView: UIView? = nil
          var contentView: UIView? = nil
      
          if #available(iOS 11.0, *) {
              guard let noDragWebView = webView else { return }
              webScrollView = noDragWebView.subviews.compactMap { $0 as? UIScrollView }.first
              contentView = webScrollView?.subviews.first(where: { $0.interactions.count > 1 })
              guard let dragInteraction = (contentView?.interactions.compactMap { $0 as? UIDragInteraction }.first) else { return }
              contentView?.removeInteraction(dragInteraction)
          }
      }
      

      【讨论】:

      • 不使用first(where: ...) 而不是compactMap(...).first 有什么特别的原因吗?我想知道,因为你用它来寻找contentView
      • 没有特别的原因,first(where:) 也可以解决问题。
      • UITextView 有类似的解决方案吗?
      【解决方案3】:

      基于 Johannes FahrenKrug 的帖子,进行了一些更改。

      private func disableDragAndDropInteraction() {
          var webScrollView: UIView? = nil
          var contentView: UIView? = nil
      
          if #available(iOS 11.0, *) {
              if (webView != nil) {
                  for subView in webView!.subviews {
                      if (subView is UIScrollView) {
                          webScrollView = subView
                          break
                      }
                  }
      
                  if (webScrollView != nil) {
                      for subView in webScrollView!.subviews {
                          if subView.interactions.count > 1 {
                              contentView = subView
                              break
                          }
                      }
      
                      if (contentView != nil) {
                          for interaction in contentView!.interactions {
                              if interaction is UIDragInteraction {
                                  contentView!.removeInteraction(interaction)
                              }
                          }
                      }
                  }
              } else {
                  // Fallback on earlier versions
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        imga 元素上使用CSS 属性webkit-touch-callout。还可以通过更改 HTML 或使用 -[WKWebView evaluateJavaScript(...)] 注入将其 draggable 属性设置为 false

        img, a {
            -webkit-touch-callout: none;
        }
        
        <img draggable="false">
        

        避免所有脆弱的子视图潜水和操纵。

        (将my answer 概括为类似的Disable link drag on WKWebView。)

        【讨论】:

          猜你喜欢
          • 2018-02-06
          • 2018-05-14
          • 1970-01-01
          • 2017-04-21
          • 2018-03-05
          • 2018-02-14
          • 2018-05-09
          • 1970-01-01
          • 2019-04-10
          相关资源
          最近更新 更多