[browser] move handling a keyboard state to view model

pull/6612/head
Krypto Pank 2 years ago
parent 67b04ed108
commit 007d0a6dd4
  1. 42
      AlphaWallet/Browser/ViewControllers/BrowserViewController.swift
  2. 51
      AlphaWallet/Browser/ViewModel/BrowserViewModel.swift

@ -74,9 +74,6 @@ final class BrowserViewController: UIViewController {
]) ])
view.backgroundColor = Configuration.Color.Semantic.defaultViewBackground view.backgroundColor = Configuration.Color.Semantic.defaultViewBackground
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
bind(viewModel: viewModel) bind(viewModel: viewModel)
} }
@ -108,38 +105,25 @@ final class BrowserViewController: UIViewController {
output.recordUrl output.recordUrl
.sink { [weak self] _ in self?.recordURL() } .sink { [weak self] _ in self?.recordURL() }
.store(in: &cancellable) .store(in: &cancellable)
output.keyboardAction
.sink { [weak self] state in
guard let strongSelf = self else { return }
switch state {
case .hideKeyboard:
strongSelf.webView.scrollView.contentInset.bottom = 0
//Must exit editing more explicitly (and update the nav bar buttons) because tapping on the web view can hide keyboard
strongSelf.delegate?.dismissKeyboard(in: strongSelf)
case .adjustBottomInset(let height):
strongSelf.webView.scrollView.contentInset.bottom = height
}
}.store(in: &cancellable)
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc private func keyboardWillShow(notification: NSNotification) {
if let keyboardEndFrame = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let _ = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue {
webView.scrollView.contentInset.bottom = keyboardEndFrame.size.height
}
}
@objc private func keyboardWillHide(notification: NSNotification) {
//If there's a external keyboard (or on simulator with software keyboard disabled):
// When text input starts. beginRect: size.height=0 endRect: size.height ~54. origin.y remains at ~812 (out of the screen)
// When text input ends. beginRect: size.height ~54 endRect: size.height = 0. origin.y remains at 812 (out of the screen)
//Note the above. keyboardWillHide() is called for both when input starts and ends for external keyboard. Probably because the keyboard is hidden in both cases
guard let beginRect = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue, let endRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
let isExternalKeyboard = beginRect.origin == endRect.origin && (beginRect.size.height == 0 || endRect.size.height == 0)
let isEnteringEditModeWithExternalKeyboard: Bool
if isExternalKeyboard {
isEnteringEditModeWithExternalKeyboard = beginRect.size.height == 0 && endRect.size.height > 0
} else {
isEnteringEditModeWithExternalKeyboard = false
}
if !isExternalKeyboard || !isEnteringEditModeWithExternalKeyboard {
webView.scrollView.contentInset.bottom = 0
//Must exit editing more explicitly (and update the nav bar buttons) because tapping on the web view can hide keyboard
delegate?.dismissKeyboard(in: self)
}
}
private func injectUserAgent() { private func injectUserAgent() {
webView.evaluateJavaScript("navigator.userAgent") { [weak self] result, _ in webView.evaluateJavaScript("navigator.userAgent") { [weak self] result, _ in
guard let strongSelf = self, let currentUserAgent = result as? String else { return } guard let strongSelf = self, let currentUserAgent = result as? String else { return }

@ -23,6 +23,7 @@ struct BrowserViewModelOutput {
let universalLink: AnyPublisher<URL, Never> let universalLink: AnyPublisher<URL, Never>
let recordUrl: AnyPublisher<Void, Never> let recordUrl: AnyPublisher<Void, Never>
let dappAction: AnyPublisher<(action: DappAction, callbackId: Int), Never> let dappAction: AnyPublisher<(action: DappAction, callbackId: Int), Never>
let keyboardAction: AnyPublisher<BrowserViewModel.KeyboardAction, Never>
} }
class BrowserViewModel: NSObject { class BrowserViewModel: NSObject {
@ -34,6 +35,16 @@ class BrowserViewModel: NSObject {
private let universalLinkSubject = PassthroughSubject<URL, Never>() private let universalLinkSubject = PassthroughSubject<URL, Never>()
private let dappActionSubject = PassthroughSubject<(action: DappAction, callbackId: Int), Never>() private let dappActionSubject = PassthroughSubject<(action: DappAction, callbackId: Int), Never>()
private var cancellable = Set<AnyCancellable>() private var cancellable = Set<AnyCancellable>()
private var keyboardStatePublisher: AnyPublisher<KeyboardChecker.KeyboardState, Never> {
let keyboardNotifications: [NSNotification.Name] = [
UIResponder.keyboardWillShowNotification,
UIResponder.keyboardWillHideNotification,
]
return Publishers.MergeMany(keyboardNotifications.map { NotificationCenter.default.publisher(for: $0) })
.map { KeyboardChecker.KeyboardState(with: $0) }
.eraseToAnyPublisher()
}
lazy var config: WKWebViewConfiguration = { lazy var config: WKWebViewConfiguration = {
let config = WKWebViewConfiguration.make(forType: .dappBrowser(server), address: wallet.address, messageHandler: ScriptMessageProxy(delegate: self)) let config = WKWebViewConfiguration.make(forType: .dappBrowser(server), address: wallet.address, messageHandler: ScriptMessageProxy(delegate: self))
@ -55,11 +66,15 @@ class BrowserViewModel: NSObject {
let progress = input.progress let progress = input.progress
.map { BrowserViewModel.ProgressBarState(value: Float($0), isHidden: $0 == 1) } .map { BrowserViewModel.ProgressBarState(value: Float($0), isHidden: $0 == 1) }
let keyboardAction = keyboardStatePublisher
.compactMap { [weak self] in self?.handle(keyboardState: $0) }
return .init( return .init(
progressBarState: progress.eraseToAnyPublisher(), progressBarState: progress.eraseToAnyPublisher(),
universalLink: universalLinkSubject.eraseToAnyPublisher(), universalLink: universalLinkSubject.eraseToAnyPublisher(),
recordUrl: recordUrlSubject.eraseToAnyPublisher(), recordUrl: recordUrlSubject.eraseToAnyPublisher(),
dappAction: dappActionSubject.eraseToAnyPublisher()) dappAction: dappActionSubject.eraseToAnyPublisher(),
keyboardAction: keyboardAction.eraseToAnyPublisher())
} }
private func handle(decidePolicy: BrowserViewModel.DecidePolicy) { private func handle(decidePolicy: BrowserViewModel.DecidePolicy) {
@ -91,6 +106,35 @@ class BrowserViewModel: NSObject {
decidePolicy.decisionHandler(.allow) decidePolicy.decisionHandler(.allow)
} }
private func handle(keyboardState: KeyboardChecker.KeyboardState) -> BrowserViewModel.KeyboardAction? {
switch keyboardState.state {
case .willShow:
return .adjustBottomInset(height: keyboardState.endFrame.size.height)
case .willHide:
//If there's a external keyboard (or on simulator with software keyboard disabled):
// When text input starts. beginRect: size.height=0 endRect: size.height ~54. origin.y remains at ~812 (out of the screen)
// When text input ends. beginRect: size.height ~54 endRect: size.height = 0. origin.y remains at 812 (out of the screen)
//Note the above. keyboardWillHide() is called for both when input starts and ends for external keyboard. Probably because the keyboard is hidden in both cases
let beginRect = keyboardState.beginFrame
let endRect = keyboardState.endFrame
let isExternalKeyboard = beginRect.origin == endRect.origin && (beginRect.size.height == 0 || endRect.size.height == 0)
let isEnteringEditModeWithExternalKeyboard: Bool
if isExternalKeyboard {
isEnteringEditModeWithExternalKeyboard = beginRect.size.height == 0 && endRect.size.height > 0
} else {
isEnteringEditModeWithExternalKeyboard = false
}
if !isExternalKeyboard || !isEnteringEditModeWithExternalKeyboard {
//Must exit editing more explicitly (and update the nav bar buttons) because tapping on the web view can hide keyboard
return .hideKeyboard
}
return nil
case .frameChange, .didHide, .didShow:
return nil
}
}
} }
extension BrowserViewModel: WKScriptMessageHandler { extension BrowserViewModel: WKScriptMessageHandler {
@ -111,6 +155,11 @@ extension BrowserViewModel: WKScriptMessageHandler {
extension BrowserViewModel { extension BrowserViewModel {
enum KeyboardAction {
case hideKeyboard
case adjustBottomInset(height: CGFloat)
}
struct Keys { struct Keys {
static let developerExtrasEnabled = "developerExtrasEnabled" static let developerExtrasEnabled = "developerExtrasEnabled"
static let ClientName = "AlphaWallet" static let ClientName = "AlphaWallet"

Loading…
Cancel
Save