Dactyloidae/mobile/ios/Client/Frontend/AuthenticationManager/PagingPasscodeViewController.swift
2026-06-26 21:04:09 -07:00

89 lines
3.1 KiB
Swift

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
private let PaneSwipeDuration: TimeInterval = 0.3
/// Base class for implementing a Passcode configuration screen with multiple 'panes'.
class PagingPasscodeViewController: BasePasscodeViewController {
fileprivate lazy var pager: UIScrollView = {
let scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.isUserInteractionEnabled = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
return scrollView
}()
var panes = [PasscodePane]()
var currentPaneIndex: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(pager)
panes.forEach { pager.addSubview($0) }
pager.snp.makeConstraints { make in
make.bottom.left.right.equalTo(self.view)
make.top.equalTo(self.topLayoutGuide.snp.bottom)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
panes.enumerated().forEach { index, pane in
pane.frame = CGRect(origin: CGPoint(x: CGFloat(index) * pager.frame.width, y: 0), size: pager.frame.size)
}
pager.contentSize = CGSize(width: CGFloat(panes.count) * pager.frame.width, height: pager.frame.height)
scrollToPaneAtIndex(currentPaneIndex)
if self.authenticationInfo?.isLocked() ?? false {
return
}
panes[currentPaneIndex].codeInputView.becomeFirstResponder()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.view.endEditing(true)
}
}
extension PagingPasscodeViewController {
@discardableResult func scrollToNextAndSelect() -> PasscodePane {
scrollToNextPane()
panes[currentPaneIndex].codeInputView.becomeFirstResponder()
return panes[currentPaneIndex]
}
@discardableResult func scrollToPreviousAndSelect() -> PasscodePane {
scrollToPreviousPane()
panes[currentPaneIndex].codeInputView.becomeFirstResponder()
return panes[currentPaneIndex]
}
func resetAllInputFields() {
panes.forEach { $0.codeInputView.resetCode() }
}
func scrollToNextPane() {
guard (currentPaneIndex + 1) < panes.count else {
return
}
currentPaneIndex += 1
scrollToPaneAtIndex(currentPaneIndex)
}
func scrollToPreviousPane() {
guard (currentPaneIndex - 1) >= 0 else {
return
}
currentPaneIndex -= 1
scrollToPaneAtIndex(currentPaneIndex)
}
func scrollToPaneAtIndex(_ index: Int) {
UIView.animate(withDuration: PaneSwipeDuration, delay: 0, options: UIViewAnimationOptions(), animations: {
self.pager.contentOffset = CGPoint(x: CGFloat(self.currentPaneIndex) * self.pager.frame.width, y: 0)
}, completion: nil)
}
}