Files
AFFiNE-Mirror/packages/frontend/apps/ios/App/App/AffineViewController.swift
T
keepClamDown a77d89bb1a fix(editor): edgeless can't slider with finger (#15091)
fix bug edgeless can't slider with finger 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added mobile immersive edgeless mode with dynamic chrome auto-hide and
tap-gesture controls.
  * Added a mobile zoom ruler UI for edgeless.
* **Bug Fixes**
* Improved iOS rendering/zoom by applying low-zoom survival behavior,
gesture-aware refresh deferral, and effective-DPR canvas scaling.
* Fixed iOS webview zoom/bounce and process-termination reload behavior.
  * Improved placeholder styling with theme-aware colors.
* **Chores**
  * Updated local ignore rules and iOS app build/version configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DarkSky <darksky2048@gmail.com>
2026-06-16 21:19:31 +08:00

130 lines
4.3 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Capacitor
import Intelligents
import UIKit
import WebKit
class AFFiNEViewController: CAPBridgeViewController, UIScrollViewDelegate {
var intelligentsButton: IntelligentsButton?
override func viewDidLoad() {
super.viewDidLoad()
webView?.allowsBackForwardNavigationGestures = false
navigationController?.navigationBar.isHidden = true
extendedLayoutIncludesOpaqueBars = false
edgesForExtendedLayout = []
// Disable WKWebView scrollView zoom/bounce to prevent conflict with edgeless canvas gestures
webView?.scrollView.minimumZoomScale = 1.0
webView?.scrollView.maximumZoomScale = 1.0
webView?.scrollView.bouncesZoom = false
webView?.scrollView.bounces = false
webView?.scrollView.pinchGestureRecognizer?.isEnabled = false
webView?.scrollView.delegate = self
// Inject viewport meta to prevent WKWebView smart zoom
let viewportScript = """
(function() {
function setViewport() {
var meta = document.querySelector('meta[name="viewport"]');
if (!meta) {
meta = document.createElement('meta');
meta.name = 'viewport';
(document.head || document.documentElement).appendChild(meta);
}
meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover';
}
if (document.head) {
setViewport();
} else {
document.addEventListener('DOMContentLoaded', setViewport);
}
})();
"""
webView?.configuration.userContentController.addUserScript(
WKUserScript(source: viewportScript, injectionTime: .atDocumentStart, forMainFrameOnly: true)
)
let intelligentsButton = installIntelligentsButton()
intelligentsButton.delegate = self
self.intelligentsButton = intelligentsButton
dismissIntelligentsButton()
}
override func capacitorDidLoad() {
let plugins: [CAPPlugin] = [
AuthPlugin(),
CookiePlugin(),
HashcashPlugin(),
NavigationGesturePlugin(),
NbStorePlugin(),
PayWallPlugin(associatedController: self),
PreviewPlugin(),
]
plugins.forEach { bridge?.registerPluginInstance($0) }
}
private var intelligentsButtonTimer: Timer?
private var isCheckingIntelligentEligibility = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
IntelligentContext.shared.webView = webView
navigationController?.setNavigationBarHidden(false, animated: animated)
let timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { [weak self] _ in
self?.checkEligibilityOfIntelligent()
}
intelligentsButtonTimer = timer
RunLoop.main.add(timer, forMode: .common)
}
private func checkEligibilityOfIntelligent() {
guard !isCheckingIntelligentEligibility else { return }
assert(intelligentsButton != nil)
guard intelligentsButton?.isHidden ?? false else { return }
isCheckingIntelligentEligibility = true
IntelligentContext.shared.webView = webView
IntelligentContext.shared.preparePresent { [self] result in
DispatchQueue.main.async {
defer { self.isCheckingIntelligentEligibility = false }
switch result {
case .failure: break
case .success:
self.presentIntelligentsButton()
}
}
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
intelligentsButtonTimer?.invalidate()
}
// MARK: - UIScrollViewDelegate
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return nil
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
scrollView.zoomScale = 1.0
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset != .zero {
scrollView.contentOffset = .zero
}
}
// MARK: - Web Content Process Crash Recovery
// NOTE: Capacitor's CAPBridgeViewController owns the WKWebView
// navigationDelegate (it assigns its own WebViewDelegationHandler), so this
// override is NOT called in practice Capacitor's handler logs
// " WebView process terminated" and reloads instead. Kept as defensive
// fallback, matching the prior baseline behavior.
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
webView.reload()
}
}