From 2ef6968d004ed1c5dae4b8c3983ae45b933e034d Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Sun, 23 Jul 2023 12:34:49 +0300 Subject: [PATCH] New keyboard type for entering VIN --- .../xcdebugger/Breakpoints_v2.xcbkptlist | 15 ++ AutoCat/ACUIKit/Extensions/UIStackView.swift | 17 ++ AutoCat/Controllers/NewNumberController.swift | 6 + AutoCat/Views/PNKeyboard.swift | 147 ++++++++++++++---- AutoCatCore/Utils/Constants.swift | 4 + 5 files changed, 161 insertions(+), 28 deletions(-) diff --git a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 4b18da9..130aa24 100644 --- a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -72,6 +72,21 @@ endingLineNumber = "37" offsetFromSymbolStart = "256"> + + diff --git a/AutoCat/ACUIKit/Extensions/UIStackView.swift b/AutoCat/ACUIKit/Extensions/UIStackView.swift index 47a3b30..8412321 100644 --- a/AutoCat/ACUIKit/Extensions/UIStackView.swift +++ b/AutoCat/ACUIKit/Extensions/UIStackView.swift @@ -40,4 +40,21 @@ extension UIStackView { self.distribution = distribution return self } + + func removeAllArrangedSubviews() { + arrangedSubviews.forEach { + self.removeArrangedSubview($0) + NSLayoutConstraint.deactivate($0.constraints) + $0.removeFromSuperview() + } + } + + func addArrangedSubviews(_ views: [UIView]) { + views.forEach { addArrangedSubview($0) } + } + + func setArrangedSubviews(_ views: [UIView]) { + removeAllArrangedSubviews() + addArrangedSubviews(views) + } } diff --git a/AutoCat/Controllers/NewNumberController.swift b/AutoCat/Controllers/NewNumberController.swift index 53800d5..2d8d158 100644 --- a/AutoCat/Controllers/NewNumberController.swift +++ b/AutoCat/Controllers/NewNumberController.swift @@ -39,6 +39,7 @@ class NewNumberController: UIViewController { private lazy var vinField: UITextField = { let view = UITextField() view.translatesAutoresizingMaskIntoConstraints = false + view.borderStyle = .roundedRect view.isHidden = true return view }() @@ -48,6 +49,7 @@ class NewNumberController: UIViewController { button.isEnabled = false button.contentEdgeInsets = .init(top: 0, left: 8, bottom: 0, right: 8) button.accessibilityIdentifier = "checkButton" + button.setContentHuggingPriority(.defaultHigh, for: .horizontal) return button }() @@ -131,9 +133,13 @@ class NewNumberController: UIViewController { case .plateNumber: plateView.isHidden = false vinField.isHidden = true + keyboardView.target = plateView + keyboardView.type = .plateNumber case .vin: plateView.isHidden = true vinField.isHidden = false + keyboardView.target = vinField + keyboardView.type = .vin } } } diff --git a/AutoCat/Views/PNKeyboard.swift b/AutoCat/Views/PNKeyboard.swift index f98f830..a77f876 100644 --- a/AutoCat/Views/PNKeyboard.swift +++ b/AutoCat/Views/PNKeyboard.swift @@ -15,28 +15,57 @@ enum PNButtonType { case done } +enum PNKeyboardType { + + case plateNumber + case vin +} + class PNButton: UIButton { private(set) var type: PNButtonType + private var keyboardType: PNKeyboardType = .plateNumber private var rectLayer = CAShapeLayer() private var bgColor = UIColor(named: "KeyBackground") weak var delegate: PNButtonDelegate? - init(letter: Character) { + var letterFont: UIFont? { + + switch keyboardType { + case .plateNumber: + return UIFont(name: "RoadNumbers", size: 36) + case .vin: + return .systemFont(ofSize: 24) + } + } + + var digitFont: UIFont? { + + switch keyboardType { + case .plateNumber: + return UIFont(name: "RoadNumbersCyr-Regular", size: 30) + case .vin: + return .systemFont(ofSize: 24) + } + } + + init(letter: Character, keyboardType: PNKeyboardType = .plateNumber) { self.type = .symbol(String(letter)) + self.keyboardType = keyboardType super.init(frame: .zero) self.setup() - self.titleLabel?.font = UIFont(name: "RoadNumbers", size: 36) + self.titleLabel?.font = letterFont let title = String(Constants.pnLettersMap[letter] ?? letter) self.setTitle(title, for: .normal) self.setTitleColor(.label, for: .normal) } - init(digit: Int) { + init(digit: Int, keyboardType: PNKeyboardType = .plateNumber) { self.type = .symbol(String(digit)) + self.keyboardType = keyboardType super.init(frame: .zero) self.setup() - self.titleLabel?.font = UIFont(name: "RoadNumbersCyr-Regular", size: 30) + self.titleLabel?.font = digitFont let character = Character(String(digit)) let title = String(Constants.pnLettersMap[character] ?? character) self.setTitle(title, for: .normal) @@ -98,20 +127,52 @@ class PNButton: UIButton { } class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate { - private weak var target: UIKeyInput? + public weak var target: UIKeyInput? weak var delegate: PNKeyboardDelegate? private let insets: UIEdgeInsets - init(target: UIKeyInput, insets: UIEdgeInsets = .zero) { + public var type: PNKeyboardType { + didSet { + setupButtons() + } + } + + var letters: [Character] { + + switch type { + case .plateNumber: + return Array(Constants.pnLettersMap.keys) + case .vin: + return Constants.vinLetters + } + } + + private var lettersWidthConstraint: NSLayoutConstraint? + private var digitsWidthConstraint: NSLayoutConstraint? + + var mainStack: UIStackView = .horizontal([]) + //.distribution(.fillProportionally) + .spacing(16) + + init(target: UIKeyInput, type: PNKeyboardType = .plateNumber, insets: UIEdgeInsets = .zero) { self.target = target self.insets = insets + self.type = type super.init(frame: .zero) self.autoresizingMask = [.flexibleWidth, .flexibleHeight] let blurEffectView = UIVisualEffectView(effect: UIBlurEffect()) blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] self.addSubview(blurEffectView) + self.addSubview(mainStack) + + NSLayoutConstraint.activate([ + mainStack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: insets.left), + mainStack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -insets.right), + mainStack.topAnchor.constraint(equalTo: self.topAnchor, constant: insets.top), + mainStack.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -insets.bottom) + ]) self.setupButtons() } @@ -120,14 +181,53 @@ class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate { fatalError("init(coder:) has not been implemented") } + override func layoutSubviews() { + super.layoutSubviews() + + let lettersMult: CGFloat = type == .plateNumber ? 0.5 : 2.0/3.0 + let digitsMult: CGFloat = type == .plateNumber ? 0.5 : 1.0/3.0 + let availableWidth = frame.width - mainStack.spacing + + let newLettersWidth = availableWidth*lettersMult + let newDigitsWidth = availableWidth*digitsMult + + let needChangeConstraints = lettersWidthConstraint?.constant != newLettersWidth + || digitsWidthConstraint?.constant != newDigitsWidth + + if needChangeConstraints { + lettersWidthConstraint?.constant = newLettersWidth + digitsWidthConstraint?.constant = newDigitsWidth + } + } + + func letterRows(from buttons: [PNButton]) -> [UIView] { + + switch type { + case .plateNumber: + return [ + createLetterStack([buttons[0], buttons[1], buttons[2]]), + createLetterStack([buttons[3], buttons[4], buttons[5]]), + createLetterStack([buttons[6], buttons[7], buttons[8]]), + createLetterStack([buttons[9], buttons[10], buttons[11]]) + ] + case .vin: + return [ + createLetterStack([buttons[0], buttons[1], buttons[2], buttons[3], buttons[4], buttons[5]]), + createLetterStack([buttons[6], buttons[7], buttons[8], buttons[9], buttons[10], buttons[11]]), + createLetterStack([buttons[12], buttons[13], buttons[14], buttons[15], buttons[16], buttons[17]]), + createLetterStack([buttons[18], buttons[19], buttons[20], buttons[21], buttons[22]]) + ] + } + } + func setupButtons() { - let letters: [PNButton] = Constants.pnLettersMap.keys.sorted().map { letter in - let button = PNButton(letter: letter) + let letterButtons: [PNButton] = letters.sorted().map { letter in + let button = PNButton(letter: letter, keyboardType: type) button.delegate = self return button } let digits: [PNButton] = [1,2,3,4,5,6,7,8,9,0].map { digit in - let button = PNButton(digit: digit) + let button = PNButton(digit: digit, keyboardType: type) button.delegate = self return button } @@ -139,14 +239,8 @@ class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate { done.setBacgroundColor(color: .systemBlue) done.tintColor = .white - let letterRows = [ - self.createLetterStack([letters[0], letters[1], letters[2]]), - self.createLetterStack([letters[3], letters[4], letters[5]]), - self.createLetterStack([letters[6], letters[7], letters[8]]), - self.createLetterStack([letters[9], letters[10], letters[11]]) - ] - - let lettersStack = UIStackView(arrangedSubviews: letterRows) + let rows = letterRows(from: letterButtons) + let lettersStack = UIStackView(arrangedSubviews: rows) lettersStack.axis = .vertical lettersStack.distribution = .fillEqually //lettersStack.spacing = 8 @@ -163,18 +257,15 @@ class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate { digitsStack.distribution = .fillEqually //digitsStack.spacing = 8 - let mainStack = UIStackView(arrangedSubviews: [lettersStack, digitsStack]) - mainStack.spacing = 16 - mainStack.distribution = .fillEqually - mainStack.translatesAutoresizingMaskIntoConstraints = false - self.addSubview(mainStack) + mainStack.setArrangedSubviews([lettersStack, digitsStack]) - NSLayoutConstraint.activate([ - mainStack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: insets.left), - mainStack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -insets.right), - mainStack.topAnchor.constraint(equalTo: self.topAnchor, constant: insets.top), - mainStack.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -insets.bottom) - ]) + lettersWidthConstraint = lettersStack.widthAnchor.constraint(equalToConstant: 0) + digitsWidthConstraint = digitsStack.widthAnchor.constraint(equalToConstant: 0) + + lettersWidthConstraint?.priority = .defaultLow + digitsWidthConstraint?.priority = .defaultLow + + NSLayoutConstraint.activate([lettersWidthConstraint, digitsWidthConstraint].compactMap { $0 }) } func createLetterStack(_ views: [UIView]) -> UIStackView { diff --git a/AutoCatCore/Utils/Constants.swift b/AutoCatCore/Utils/Constants.swift index 7b58439..491d5ad 100644 --- a/AutoCatCore/Utils/Constants.swift +++ b/AutoCatCore/Utils/Constants.swift @@ -15,6 +15,10 @@ public enum Constants { "А": "A", "В": "B", "Е": "E", "К": "K", "М": "M", "Н": "H", "О": "O", "Р": "P", "С": "C", "Т": "T", "У": "Y", "Х": "X" ] + public static let vinLetters: [Character] = [ + "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" + ] + public static let googleAuthURL = "https://accounts.google.com/o/oauth2/v2/auth" public static let googleTokenURL = "https://oauth2.googleapis.com/token" public static let googleRedirectURL = "com.googleusercontent.apps.994679674451-k7clunkk4nicl6iuajdtc5u7hvustbdb:/oauth2callback"