summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN/View controllers/Settings/SettingsInputCell.swift
blob: dc5645290a8ac8eaec7296ec869ab97019498e9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//
//  SettingsInputCell.swift
//  MullvadVPN
//
//  Created by Jon Petersson on 2023-05-05.
//  Copyright © 2023 Mullvad VPN AB. All rights reserved.
//

import UIKit

class SettingsInputCell: SelectableSettingsCell {
    let textField = CustomTextField(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 30)))
    var toolbarDoneButton = UIBarButtonItem()

    var isValidInput: Bool {
        didSet {
            updateTextFieldInputValidity()
        }
    }

    var inputDidChange: ((String) -> Void)?
    var inputWasConfirmed: ((String) -> Void)?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        isValidInput = true

        super.init(style: style, reuseIdentifier: reuseIdentifier)

        toolbarDoneButton = UIBarButtonItem(
            title: NSLocalizedString(
                "INPUT_CELL_TOOLBAR_BUTTON_DONE",
                tableName: "VPNSettings",
                value: "Done",
                comment: ""
            ),
            style: .done,
            target: self,
            action: #selector(confirmInput)
        )

        accessoryView = textField

        setUpTextField()
        setUpTextFieldToolbar()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func prepareForReuse() {
        super.prepareForReuse()

        reset()
    }

    func reset() {
        textField.text = nil
        UITextField.SearchTextFieldAppearance.inactive.apply(to: textField)
    }

    func setInput(_ text: String) {
        textField.text = text
        textFieldDidChange(textField)
    }

    @objc func confirmInput() {
        _ = textFieldShouldReturn(textField)
    }

    @objc private func textFieldDidChange(_ textField: UITextField) {
        if let text = textField.text {
            inputDidChange?(text)
            toolbarDoneButton.isEnabled = isValidInput
        }
    }

    private func setUpTextField() {
        textField.borderStyle = .none
        textField.layer.cornerRadius = 4
        textField.font = .preferredFont(forTextStyle: .body)
        textField.textAlignment = .right
        textField.delegate = self
        textField.keyboardType = .numberPad
        textField.returnKeyType = .done
        textField.textMargins = UIMetrics.SettingsCell.inputCellTextFieldLayoutMargins
        textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)

        UITextField.SearchTextFieldAppearance.inactive.apply(to: textField)
    }

    private func setUpTextFieldToolbar() {
        let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44))
        toolbar.items = [
            UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil),
            toolbarDoneButton,
        ]

        toolbar.sizeToFit()

        textField.inputAccessoryView = toolbar
    }

    private func updateTextFieldInputValidity() {
        if isValidInput {
            textField.textColor = textField.isEditing ? .SearchTextField.textColor : .SearchTextField.inactiveTextColor
        } else {
            textField.textColor = UIColor.TextField.invalidInputTextColor
        }
    }
}

extension SettingsInputCell: UITextFieldDelegate {
    func textFieldDidBeginEditing(_ textField: UITextField) {
        inputDidChange?(textField.text ?? "")
        toolbarDoneButton.isEnabled = isValidInput

        UITextField.SearchTextFieldAppearance.active.apply(to: textField)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        guard isValidInput else { return false }

        inputWasConfirmed?(textField.text ?? "")
        textField.resignFirstResponder()

        return true
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        UITextField.SearchTextFieldAppearance.inactive.apply(to: textField)
    }
}