@@ -10,235 +10,63 @@ import ValidatorUI
1010// MARK: - ViewController
1111
1212final class ViewController : UIViewController {
13- // MARK: - Properties
13+ private let examples : [ ExampleItem ] = [
14+ ExampleItem ( title: " UITextField Example " , controller: LoginTextFieldExampleViewController ( ) ) ,
15+ ExampleItem ( title: " UITextView Example " , controller: FeedbackTextViewExampleViewController ( ) ) ,
16+ ]
1417
15- // UI
16- private let scrollView : UIScrollView = {
17- let scrollView = UIScrollView ( )
18- scrollView. translatesAutoresizingMaskIntoConstraints = false
19- return scrollView
20- } ( )
21-
22- private let contentView : UIView = {
23- let view = UIView ( )
24- view. translatesAutoresizingMaskIntoConstraints = false
25- return view
26- } ( )
27-
28- private lazy var firstNameTextField : UITextField = {
29- let textField = makeField ( " First Name " )
30- textField. validationRules = [
31- LengthValidationRule ( min: 2 , max: 50 , error: " First name should be 2–50 characters long " ) ,
32- ]
33- return textField
34- } ( )
35-
36- private lazy var lastNameTextField : UITextField = {
37- let textField = makeField ( " Last Name " )
38- textField. validationRules = [
39- LengthValidationRule ( min: 2 , max: 50 , error: " Last name should be 2–50 characters long " ) ,
40- ]
41- return textField
42- } ( )
43-
44- private lazy var emailTextField : UITextField = {
45- let textField = makeField ( " Email " )
46- textField. keyboardType = . emailAddress
47- textField. validationRules = [
48- EmailValidationRule ( error: " Please enter a valid email address " ) ,
49- ]
50- return textField
51- } ( )
52-
53- private lazy var submitButton : UIButton = {
54- let button = UIButton ( type: . system)
55- button. setTitle ( " Sign Up " , for: . normal)
56- button. layer. cornerRadius = 10
57- button. backgroundColor = . systemGray4
58- button. setTitleColor ( . white, for: . normal)
59- button. translatesAutoresizingMaskIntoConstraints = false
60- button. addTarget ( self , action: #selector( submit) , for: . touchUpInside)
61- return button
62- } ( )
63-
64- private let stackView : UIStackView = {
65- let stackView = UIStackView ( )
66- stackView. axis = . vertical
67- stackView. spacing = 16
68- stackView. translatesAutoresizingMaskIntoConstraints = false
69- return stackView
70- } ( )
71-
72- // Private properties
73- private var isValid : Bool {
74- [ firstNameTextField, lastNameTextField, emailTextField]
75- . allSatisfy { $0. validationResult == . valid }
76- }
77-
78- // MARK: - Lifecycle
18+ private let tableView = UITableView ( frame: . zero, style: . insetGrouped)
7919
8020 override func viewDidLoad( ) {
8121 super. viewDidLoad ( )
22+ title = " Examples "
8223 view. backgroundColor = . systemBackground
83- configure ( )
84- }
8524
86- override func viewWillAppear( _ animated: Bool ) {
87- super. viewWillAppear ( animated)
88- registerKeyboardNotifications ( )
25+ setupTableView ( )
8926 }
9027
91- override func viewWillDisappear( _ animated: Bool ) {
92- super. viewWillDisappear ( animated)
93- unregisterKeyboardNotifications ( )
94- }
28+ private func setupTableView( ) {
29+ tableView. dataSource = self
30+ tableView. delegate = self
31+ tableView. register ( UITableViewCell . self, forCellReuseIdentifier: " cell " )
32+ tableView. translatesAutoresizingMaskIntoConstraints = false
9533
96- // MARK: - UI Setup
97-
98- private func configure( ) {
99- for item in [ firstNameTextField, lastNameTextField, emailTextField] {
100- item. validationHandler = { [ weak self] _ in
101- guard let self else { return }
102- updateSubmitButtonState ( )
103- }
104- }
105-
106- [ firstNameTextField, lastNameTextField, emailTextField, submitButton]
107- . forEach ( stackView. addArrangedSubview)
108-
109- view. addSubview ( scrollView)
110- scrollView. addSubview ( contentView)
111- contentView. addSubview ( stackView)
34+ view. addSubview ( tableView)
11235
11336 NSLayoutConstraint . activate ( [
114- scrollView. leadingAnchor. constraint ( equalTo: view. leadingAnchor) ,
115- scrollView. trailingAnchor. constraint ( equalTo: view. trailingAnchor) ,
116- scrollView. topAnchor. constraint ( equalTo: view. topAnchor) ,
117- scrollView. bottomAnchor. constraint ( equalTo: view. bottomAnchor) ,
118-
119- contentView. leadingAnchor. constraint ( equalTo: scrollView. leadingAnchor) ,
120- contentView. trailingAnchor. constraint ( equalTo: scrollView. trailingAnchor) ,
121- contentView. topAnchor. constraint ( equalTo: scrollView. topAnchor) ,
122- contentView. bottomAnchor. constraint ( equalTo: scrollView. bottomAnchor) ,
123- contentView. widthAnchor. constraint ( equalTo: scrollView. widthAnchor) ,
124- contentView. heightAnchor. constraint ( equalTo: scrollView. heightAnchor) ,
125-
126- stackView. centerYAnchor. constraint ( equalTo: contentView. centerYAnchor) ,
127- stackView. leadingAnchor. constraint ( equalTo: contentView. leadingAnchor, constant: 24 ) ,
128- stackView. trailingAnchor. constraint ( equalTo: contentView. trailingAnchor, constant: - 24 ) ,
129-
130- submitButton. heightAnchor. constraint ( equalToConstant: 48 ) ,
37+ tableView. topAnchor. constraint ( equalTo: view. topAnchor) ,
38+ tableView. leadingAnchor. constraint ( equalTo: view. leadingAnchor) ,
39+ tableView. trailingAnchor. constraint ( equalTo: view. trailingAnchor) ,
40+ tableView. bottomAnchor. constraint ( equalTo: view. bottomAnchor) ,
13141 ] )
13242 }
43+ }
13344
134- private func updateSubmitButtonState( ) {
135- submitButton. isEnabled = isValid
136- UIView . animate ( withDuration: 0.25 ) {
137- self . submitButton. backgroundColor = self . isValid ? . systemBlue : . systemGray4
138- }
139- }
140-
141- // MARK: Private
142-
143- private func makeField( _ placeholder: String ) -> UITextField {
144- let textField = UITextField ( )
145- textField. placeholder = placeholder
146-
147- textField. layer. cornerRadius = 10
148- textField. layer. borderWidth = 1
149- textField. layer. borderColor = UIColor . systemGray4. cgColor
150- textField. backgroundColor = UIColor . secondarySystemBackground
151- textField. textColor = . label
152-
153- textField. leftView = UIView ( frame: CGRect ( x: 0 , y: 0 , width: 12 , height: 0 ) )
154- textField. leftViewMode = . always
155-
156- textField. heightAnchor. constraint ( equalToConstant: 56.0 ) . isActive = true
157- textField. translatesAutoresizingMaskIntoConstraints = false
158- textField. validateOnInputChange ( isEnabled: true )
159-
160- return textField
161- }
162-
163- // MARK: Notifications
164-
165- private func registerKeyboardNotifications( ) {
166- NotificationCenter . default. addObserver (
167- self ,
168- selector: #selector( keyboardWillShow ( _: ) ) ,
169- name: UIResponder . keyboardWillShowNotification,
170- object: nil
171- )
172-
173- NotificationCenter . default. addObserver (
174- self ,
175- selector: #selector( keyboardWillHide ( _: ) ) ,
176- name: UIResponder . keyboardWillHideNotification,
177- object: nil
178- )
179- }
180-
181- private func unregisterKeyboardNotifications( ) {
182- NotificationCenter . default. removeObserver ( self )
183- }
184-
185- // MARK: - Actions
186-
187- @objc
188- private func submit( ) {
189- guard isValid else {
190- let alert = UIAlertController (
191- title: " The form is invalid " ,
192- message: " Please check the highlighted fields and correct the entered information. " ,
193- preferredStyle: . alert
194- )
195- alert. addAction ( UIAlertAction ( title: " Got it " , style: . default) )
196-
197- present ( alert, animated: true )
198- return
199- }
200-
201- let alert = UIAlertController (
202- title: " Success 🎉 " ,
203- message: " Your account has been successfully created. " ,
204- preferredStyle: . alert
205- )
206- alert. addAction ( UIAlertAction ( title: " OK " , style: . default) )
45+ // MARK: UITableViewDataSource, UITableViewDelegate
20746
208- present ( alert, animated: true )
47+ extension ViewController : UITableViewDataSource , UITableViewDelegate {
48+ func tableView( _: UITableView , numberOfRowsInSection _: Int ) -> Int {
49+ examples. count
20950 }
21051
211- @objc
212- private func keyboardWillShow( _ notification: Notification ) {
213- guard
214- let userInfo = notification. userInfo,
215- let frameValue = userInfo [ UIResponder . keyboardFrameEndUserInfoKey] as? NSValue
216- else { return }
217-
218- let keyboardFrame = frameValue. cgRectValue
219- let bottomInset = keyboardFrame. height - view. safeAreaInsets. bottom
220-
221- scrollView. contentInset. bottom = bottomInset
222-
223- if let firstResponder = view. findFirstResponder ( ) ,
224- !scrollView. frame. contains ( firstResponder. frame)
225- {
226- scrollView. scrollRectToVisible ( firstResponder. frame, animated: true )
227- }
52+ func tableView( _ tableView: UITableView , cellForRowAt indexPath: IndexPath ) -> UITableViewCell {
53+ let cell = tableView. dequeueReusableCell ( withIdentifier: " cell " , for: indexPath)
54+ let example = examples [ indexPath. row]
55+ cell. textLabel? . text = example. title
56+ cell. accessoryType = . disclosureIndicator
57+ return cell
22858 }
22959
230- @objc
231- private func keyboardWillHide( _: Notification ) {
232- scrollView. contentInset. bottom = . zero
60+ func tableView( _ tableView: UITableView , didSelectRowAt indexPath: IndexPath ) {
61+ tableView. deselectRow ( at: indexPath, animated: true )
62+ let example = examples [ indexPath. row]
63+ navigationController? . pushViewController ( example. controller, animated: true )
23364 }
23465}
23566
236- extension UIView {
237- func findFirstResponder( ) -> UIView ? {
238- if isFirstResponder { return self }
239- for sub in subviews {
240- if let found = sub. findFirstResponder ( ) { return found }
241- }
242- return nil
243- }
67+ // MARK: - ExampleItem
68+
69+ struct ExampleItem {
70+ let title : String
71+ let controller : UIViewController
24472}
0 commit comments