Merge pull request #924 from correia/correia/white-flash-on-load
Fix flash on white when loading detail view controller in dark mode
This commit is contained in:
commit
8335a5cb48
|
@ -237,6 +237,9 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(accountDidDownloadArticles(_:)), name: .AccountDidDownloadArticles, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(accountDidDownloadArticles(_:)), name: .AccountDidDownloadArticles, object: nil)
|
||||||
|
|
||||||
|
// Force lazy initialization of the web view provider so that it can warm up the queue of prepared web views
|
||||||
|
let _ = DetailViewControllerWebViewProvider.shared
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() -> UIViewController {
|
func start() -> UIViewController {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14854.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14806.4"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -15,22 +15,13 @@
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<wkWebView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t8d-md-Yhc">
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DNb-lt-KzC">
|
||||||
<rect key="frame" x="0.0" y="44" width="414" height="769"/>
|
<rect key="frame" x="0.0" y="44" width="414" height="769"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<wkWebViewConfiguration key="configuration">
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
|
</view>
|
||||||
<wkPreferences key="preferences"/>
|
|
||||||
</wkWebViewConfiguration>
|
|
||||||
</wkWebView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="t8d-md-Yhc" firstAttribute="top" secondItem="VUw-jc-0yf" secondAttribute="top" id="0aK-ew-1HG"/>
|
|
||||||
<constraint firstItem="VUw-jc-0yf" firstAttribute="trailing" secondItem="t8d-md-Yhc" secondAttribute="trailing" id="31v-r8-kzh"/>
|
|
||||||
<constraint firstItem="VUw-jc-0yf" firstAttribute="bottom" secondItem="t8d-md-Yhc" secondAttribute="bottom" id="kK6-eC-XwD"/>
|
|
||||||
<constraint firstItem="t8d-md-Yhc" firstAttribute="leading" secondItem="VUw-jc-0yf" secondAttribute="leading" id="nDG-aV-vqc"/>
|
|
||||||
</constraints>
|
|
||||||
<viewLayoutGuide key="safeArea" id="VUw-jc-0yf"/>
|
<viewLayoutGuide key="safeArea" id="VUw-jc-0yf"/>
|
||||||
</view>
|
</view>
|
||||||
<toolbarItems>
|
<toolbarItems>
|
||||||
|
@ -112,7 +103,7 @@
|
||||||
<outlet property="prevArticleBarButtonItem" destination="v4j-fq-23N" id="Gny-Oh-cQa"/>
|
<outlet property="prevArticleBarButtonItem" destination="v4j-fq-23N" id="Gny-Oh-cQa"/>
|
||||||
<outlet property="readBarButtonItem" destination="hy0-LS-MzE" id="BzM-x9-tuj"/>
|
<outlet property="readBarButtonItem" destination="hy0-LS-MzE" id="BzM-x9-tuj"/>
|
||||||
<outlet property="starBarButtonItem" destination="wU4-eH-wC9" id="Z8Q-Lt-dKk"/>
|
<outlet property="starBarButtonItem" destination="wU4-eH-wC9" id="Z8Q-Lt-dKk"/>
|
||||||
<outlet property="webView" destination="t8d-md-Yhc" id="Iqg-bg-wds"/>
|
<outlet property="webViewContainer" destination="DNb-lt-KzC" id="Fc1-Ae-pWK"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="FJe-Yq-33r" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="FJe-Yq-33r" sceneMemberID="firstResponder"/>
|
||||||
|
|
|
@ -14,23 +14,42 @@ import SafariServices
|
||||||
|
|
||||||
class DetailViewController: UIViewController {
|
class DetailViewController: UIViewController {
|
||||||
|
|
||||||
@IBOutlet weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
||||||
@IBOutlet weak var prevArticleBarButtonItem: UIBarButtonItem!
|
@IBOutlet private weak var prevArticleBarButtonItem: UIBarButtonItem!
|
||||||
@IBOutlet weak var nextArticleBarButtonItem: UIBarButtonItem!
|
@IBOutlet private weak var nextArticleBarButtonItem: UIBarButtonItem!
|
||||||
@IBOutlet weak var readBarButtonItem: UIBarButtonItem!
|
@IBOutlet private weak var readBarButtonItem: UIBarButtonItem!
|
||||||
@IBOutlet weak var starBarButtonItem: UIBarButtonItem!
|
@IBOutlet private weak var starBarButtonItem: UIBarButtonItem!
|
||||||
@IBOutlet weak var actionBarButtonItem: UIBarButtonItem!
|
@IBOutlet private weak var actionBarButtonItem: UIBarButtonItem!
|
||||||
@IBOutlet weak var browserBarButtonItem: UIBarButtonItem!
|
@IBOutlet private weak var browserBarButtonItem: UIBarButtonItem!
|
||||||
@IBOutlet weak var webView: WKWebView!
|
@IBOutlet private weak var webViewContainer: UIView!
|
||||||
|
private var webView: WKWebView!
|
||||||
|
|
||||||
weak var coordinator: AppCoordinator!
|
weak var coordinator: AppCoordinator!
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
webView.removeFromSuperview()
|
||||||
|
DetailViewControllerWebViewProvider.shared.enqueueWebView(webView)
|
||||||
|
webView = nil
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
webView.isHidden = true
|
|
||||||
|
webView = DetailViewControllerWebViewProvider.shared.dequeueWebView()
|
||||||
|
webView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
webView.navigationDelegate = self
|
webView.navigationDelegate = self
|
||||||
|
|
||||||
|
webViewContainer.addSubview(webView)
|
||||||
|
|
||||||
|
let constraints: [NSLayoutConstraint] = [
|
||||||
|
webView.leadingAnchor.constraint(equalTo: webViewContainer.safeAreaLayoutGuide.leadingAnchor),
|
||||||
|
webView.trailingAnchor.constraint(equalTo: webViewContainer.safeAreaLayoutGuide.trailingAnchor),
|
||||||
|
webView.topAnchor.constraint(equalTo: webViewContainer.safeAreaLayoutGuide.topAnchor),
|
||||||
|
webView.bottomAnchor.constraint(equalTo: webViewContainer.safeAreaLayoutGuide.bottomAnchor),
|
||||||
|
]
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate(constraints)
|
||||||
|
|
||||||
markAsRead()
|
markAsRead()
|
||||||
updateUI()
|
updateUI()
|
||||||
reloadHTML()
|
reloadHTML()
|
||||||
|
@ -213,16 +232,6 @@ extension DetailViewController: WKNavigationDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
||||||
// We initially hide the webview and only show it after it has loaded to avoid the
|
|
||||||
// white flashing that WKWebView does when it loads. This is especially noticable
|
|
||||||
// in dark mode.
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
|
||||||
webView.isHidden = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension DetailViewController {
|
private extension DetailViewController {
|
||||||
|
@ -234,3 +243,48 @@ private extension DetailViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// WKWebView has an awful behavior of a flash to white on first load when in dark mode.
|
||||||
|
/// Keep a queue of WebViews where we've already done a trivial load so that by the time we need them in the UI, they're past the flash-to-shite part of their lifecycle.
|
||||||
|
class DetailViewControllerWebViewProvider {
|
||||||
|
static var shared = DetailViewControllerWebViewProvider()
|
||||||
|
|
||||||
|
func dequeueWebView() -> WKWebView {
|
||||||
|
if let webView = queue.popLast() {
|
||||||
|
replenishQueueIfNeeded()
|
||||||
|
return webView
|
||||||
|
}
|
||||||
|
|
||||||
|
assertionFailure("Creating WKWebView in \(#function); queue has run dry.")
|
||||||
|
let webView = WKWebView(frame: .zero)
|
||||||
|
return webView
|
||||||
|
}
|
||||||
|
|
||||||
|
func enqueueWebView(_ webView: WKWebView) {
|
||||||
|
webView.uiDelegate = nil
|
||||||
|
webView.navigationDelegate = nil
|
||||||
|
|
||||||
|
let html = ArticleRenderer.noSelectionHTML(style: .defaultStyle)
|
||||||
|
webView.loadHTMLString(html, baseURL: nil)
|
||||||
|
|
||||||
|
queue.insert(webView, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let minimumQueueDepth = 3
|
||||||
|
private var queue: [WKWebView] = []
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
replenishQueueIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func replenishQueueIfNeeded() {
|
||||||
|
while queue.count < minimumQueueDepth {
|
||||||
|
let webView = WKWebView(frame: .zero)
|
||||||
|
enqueueWebView(webView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue