我可以为 Safari View Controller 预加载 Web 内容吗?

Can I preload the web content for Safari View Controller?

我可以毫无问题地创建 Safari 视图控制器:

let svc = SFSafariViewController(URL: NSURL(string: remote_url)!, entersReaderIfAvailable: true)
self.presentViewController(svc, animated: true, completion: nil)

在向用户显示视图控制器之前,有什么方法可以预加载 URL?

例如,我可以先在后台预加载 URL(网页内容),当用户点击某些内容后,我可以立即将内容显示在 Safari View Controller 中。用户会感觉页面加载更快或更即时。

P.S。 Workarounds/hacks 也可以接受。例如,使用缓存或在后台启动视图控制器等。

编辑:请仅考虑 SFSafariViewController。

遗憾的是,SFSafariViewController 的当前实现不支持此行为。我鼓励向 Apple 提交雷达以增加对这种行为的支持,但就像其他人建议的那样,你最好的选择是使用 WKWebView 并在将其添加到层次结构之前开始加载。

我遇到了一个可爱的 radar from Twitter,它实际上准确地提到了您的要求。我认为您可能会发现以下请求很有用:

High Priority: - Ability to warm the SFSafariViewController before actually presenting it with a URL, URL request, HTML data or file on disk - Currently, are investing heavily into warming the shared URL cache for high priority Tweets so that if the user hits that Tweet we will open UIWebView (sadly not WKWebView) with that pre-cached web page. If we could just warm an SFSafariViewController with the desired link, this would eliminate an enormous amount of effort on our end.

您可以在他们的实现中看到他们只是使用 UIWebView 缓存响应,因为 WKWebView 似乎有点混淆了缓存语义。唯一的风险是 UIWebView 可能会被弃用,正如您在他们的 docs "In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView."

中看到的那样

所以不幸的是,似乎他们需要跳过许多障碍才能让这一切顺利进行,所以你现在最好的选择就是纠缠苹果并欺骗 Twitter 的雷达。

您可以尝试使用 http 缓存,但我认为它不会起作用,因为 Safari View Controller 作为一个单独的进程工作(可能与 Safari 相同),所以这就是它的原因,例如绕过ATS。

我能想到的唯一可行的方法是以某种方式强制用户的 Safari 加载它? openURL: 或者添加到阅读列表?这听起来不像是一个可行的解决方案。

您始终可以尝试视图控制器的自定义呈现,将其附加到视图层次结构,触发外观事件,但将其框架设置为 CGRectMake(0,0,1,1) 或将其附加到屏幕外的某个位置,然后稍等片刻用正确的框架表示它。

这是一个解决方案。 显然,如果您立即单击按钮,您将看到正在加载。 但基本上,我加载浏览器并将视图放在另一个视图后面,然后在另一个视图中放置一个按钮。

当您按下按钮时,浏览器会被带到最前面,并且已经加载。 这里唯一的问题是我没有使用任何过渡,但这至少是一种解决方案。

import UIKit
import SafariServices

class ViewController: UIViewController {
  var svc = SFSafariViewController(URL: NSURL(string: "https://microsoft.com/")!, entersReaderIfAvailable: true)
  var safariView:UIView?
  let containerView = UIView()
  let btn = UIButton()

  override func viewDidLoad() {
    super.viewDidLoad()
    //let tmpView = svc.view
    addChildViewController(svc)
    svc.didMoveToParentViewController(self)
    svc.view.frame = view.frame
    containerView.frame = view.frame
    containerView.backgroundColor = UIColor.redColor()
    safariView = svc.view
    view.addSubview(safariView!)
    view.addSubview(containerView)

    btn.setTitle("Webizer", forState: UIControlState.Normal)
    btn.titleLabel!.textColor = UIColor.blackColor()
    btn.addTarget(self, action: "buttonTouched:", forControlEvents: .TouchUpInside)
    btn.frame = CGRectMake(20, 50, 100, 100)
    containerView.addSubview(btn)

    view.sendSubviewToBack(safariView!)

    // Do any additional setup after loading the view, typically from a nib.
  }

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

  @IBAction func buttonTouched(sender: AnyObject) {
    view.bringSubviewToFront(safariView!)
    //self.presentViewController(svc, animated: true, completion: nil)
  }


}

您可以使用以下代码下载网页。并在 svc 的帮助下表示它 让data:NSData? 做 { 让 weatherData = try NSData(contentsOfURL: NSURL(string: remote_url)!, options: NSDataReadingOptions()) 数据 = 天气数据 打印(天气数据) } 抓住 { 打印(错误) }

并在需要时在 svc 中加载它

虽然在技术上可以使用上述解决方案来实现您的要求,但这可能无法通过 App Store 审核。根据 SFSafariViewController docs:

In accordance with App Store Review Guidelines, this view controller must be used to visibly present information to users; the controller may not be hidden or obscured by other views or layers. Additionally, an app may not use SFSafariViewController to track users without their knowledge and consent.