iOS 10 WKWebView 新機能 リンクを3D TouchでSafari以外の独自ViewControllerを表示する


iOS 10以前はWKWebViewのリンクを3D TouchするとPeekでSafariViewが開き、PopでSafariへ移動するという動作でした。

これを移動することなく一つのアプリ内で完結したいということで、iOS 10では任意のview controllerを使うことが許されました。

サンプルでは全体httpsのサイトを使ってATS(App Transport Security)の設定を省けるようにしています。

以下サンプルコードです。

サンプル

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate {

    let webView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.backgroundColor = UIColor.white

        let web = self.webView
        web.uiDelegate = self
        web.allowsLinkPreview = true // Allow Peek & Pop 1/2
        self.view.addSubview(web)
        web.frame = self.view.bounds

        // httpsではないhttpのページを表示したい時はXcodeでATSの設定をします
        // ATSの設定方法は別途調べてください
        // be careful with ATS (App Transport Security) if you want to open a non-https page
        let url = URL(string: "https://www.amazon.co.jp/")!
        let request = URLRequest(url: url)
        web.load(request)
    }

    // Allow Peek & Pop 2/2
    func webView(_ webView: WKWebView, shouldPreviewElement elementInfo: WKPreviewElementInfo) -> Bool {
        return true
    }

    // Peek
    func webView(_ webView: WKWebView, previewingViewControllerForElement elementInfo: WKPreviewElementInfo, defaultActions previewActions: [WKPreviewActionItem]) -> UIViewController? {

        guard let url = elementInfo.linkURL else { return nil }
        print("\(type(of: self)) \(#function)  url:\(url)")

        var actions = [WKPreviewActionItem]()
        // Peekで表示しているページに対するアクションをview controllerへ渡します。
        // 表示するアクションを選択することもできます。
        // Just set all actions like
        // actions = previewActions
        // or filter any actions
        for action in previewActions {
            switch action.identifier {
            case WKPreviewActionItemIdentifierOpen:
                actions.append(action)
            case WKPreviewActionItemIdentifierAddToReadingList:
                actions.append(action)
            case WKPreviewActionItemIdentifierCopy:
                actions.append(action)
            case WKPreviewActionItemIdentifierShare:
                actions.append(action)
            default: break
            }
        }

        // アクションリストを表示したいのでUIViewControllerのサブクラスを使っています
        let vc = WebPreviewViewController(url: url, actions: actions)

        vc.preferredContentSize = vc.view.bounds.size

        return vc
    }

    // Pop
    func webView(_ webView: WKWebView, commitPreviewingViewController previewingViewController: UIViewController) {
        if previewingViewController is WebPreviewViewController {
            self.present(previewingViewController, animated: true) { }
        }
    }
}

class WebPreviewViewController: UIViewController {

    var webViewPreviewActionItems: [WKPreviewActionItem] = []
    var linkURL: URL?

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    convenience init(url: URL, actions: [WKPreviewActionItem]) {
        self.init(nibName: nil, bundle: nil)
        self.linkURL = url
        self.webViewPreviewActionItems = actions
    }

    // 受け取ったアクションのリストを表示するため
    override var previewActionItems: [UIPreviewActionItem] {
        get {
            return self.webViewPreviewActionItems
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Peek & Pop用のWKWebViewを表示
        if let url = self.linkURL {
            let web = WKWebView()
            web.frame = self.view.bounds
            self.view.addSubview(web)
            web.load(URLRequest(url: url))
        }
    }

}

参照

iOS 10 Link Preview API in WKWebView

自作ブラウザアプリ(無料) にも近日実装予定