LoginViewController.swift 11.4 KB
Newer Older
domenicw's avatar
domenicw committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
//  LoginViewController.swift
//  Amiv
//
//  Created by Domenic Wüthrich on 18.09.18.
//  Copyright © 2018 Amiv an der ETH. All rights reserved.
//

import Foundation
import UIKit

public class LoginViewController: UIViewController {
    
    // MARK: - Variables
    
domenicw's avatar
domenicw committed
16
17
    public var delegate: LoginViewControllerDelegate?
    
domenicw's avatar
domenicw committed
18
    private let infoLabelText: String
domenicw's avatar
domenicw committed
19
    
domenicw's avatar
domenicw committed
20
21
22
23
    // MARK: - View variables
    
    public private(set) var titleLabel: UILabel!
    
domenicw's avatar
domenicw committed
24
25
    public private(set) var infoLabel: UILabel!
    
domenicw's avatar
domenicw committed
26
    public private(set) var button: UIButton!
domenicw's avatar
domenicw committed
27
    
domenicw's avatar
domenicw committed
28
    public private(set) var smallButton: UIButton!
domenicw's avatar
domenicw committed
29
    
domenicw's avatar
domenicw committed
30
31
32
33
34
35
    public private(set) var usernameTextField: UITextField!
    
    public private(set) var passwordTextField: UITextField!
    
    // MARK: - Initializers
    
domenicw's avatar
domenicw committed
36
37
    public init(model: LoginModel) {
        self.infoLabelText = model.infoText
domenicw's avatar
domenicw committed
38
39
40
        super.init(nibName: nil, bundle: nil)
        
        // View Creation
domenicw's avatar
domenicw committed
41
        self.titleLabel = self.createTitleLabel(model.title)
domenicw's avatar
domenicw committed
42
43
44
45
46
        self.view.addSubview(self.titleLabel)
        
        self.infoLabel = self.createInfoTextLabel()
        self.view.addSubview(self.infoLabel)
        
domenicw's avatar
domenicw committed
47
48
        self.button = self.createButton(model.buttonTitle)
        self.view.addSubview(self.button)
domenicw's avatar
domenicw committed
49
        
domenicw's avatar
domenicw committed
50
51
        self.smallButton = self.createSmallButton(model.smallButtonTitle)
        self.view.addSubview(self.smallButton)
domenicw's avatar
domenicw committed
52
        
domenicw's avatar
domenicw committed
53
54
55
56
57
58
59
60
        self.usernameTextField = self.createUsernameTextField()
        self.view.addSubview(self.usernameTextField)
        
        self.passwordTextField = self.createPasswordTextField()
        self.view.addSubview(self.passwordTextField)
        
        // View Constraints
        self.applyTitleLabelConstraints()
domenicw's avatar
domenicw committed
61
        self.applyInfoLabelConstraints()
domenicw's avatar
domenicw committed
62
63
        self.applyUsernameTextFieldConstraints()
        self.applyPasswordTextFieldConstraints()
domenicw's avatar
domenicw committed
64
65
        self.applyButtonConstraints()
        self.applySmallButtonConstraints()
domenicw's avatar
domenicw committed
66
67
68
69
70
71
72
73
74
75
        
        // Tap view to hide keyboard
        let recognizer = UITapGestureRecognizer(target: self, action: #selector(self.hideKeyboard))
        self.view.addGestureRecognizer(recognizer)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
domenicw's avatar
domenicw committed
76
77
78
79
80
81
    // MARK: - Deinitializers
    
    deinit {
        self.hideKeyboard()
    }
    
domenicw's avatar
domenicw committed
82
83
    // MARK: - View Creation
    
domenicw's avatar
domenicw committed
84
    private func createTitleLabel(_ title: String) -> UILabel {
domenicw's avatar
domenicw committed
85
        let label = UILabel()
domenicw's avatar
domenicw committed
86
        label.text = title
domenicw's avatar
domenicw committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
        label.textColor = .amivRed
        label.numberOfLines = 2
        label.textAlignment = .center
        label.font = UIFont.preferredFont(forTextStyle: .largeTitle).bold()
        label.translatesAutoresizingMaskIntoConstraints = false
        
        return label
    }
    
    private func applyTitleLabelConstraints() {
        NSLayoutConstraint(item: self.titleLabel, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .topMargin, multiplier: 1, constant: 30).isActive = true
        NSLayoutConstraint(item: self.titleLabel, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
        NSLayoutConstraint(item: self.titleLabel, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
    }
    
domenicw's avatar
domenicw committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    private func createInfoTextLabel() -> UILabel {
        let label = UILabel()
        label.text = self.infoLabelText
        label.numberOfLines = 0
        label.textAlignment = .center
        label.font = UIFont.preferredFont(forTextStyle: .body)
        label.translatesAutoresizingMaskIntoConstraints = false
        
        return label
    }
    
    private func applyInfoLabelConstraints() {
        NSLayoutConstraint(item: self.infoLabel, attribute: .top, relatedBy: .equal, toItem: self.titleLabel, attribute: .bottom, multiplier: 1, constant: 20).isActive = true
        NSLayoutConstraint(item: self.infoLabel, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
        NSLayoutConstraint(item: self.infoLabel, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
    }
    
domenicw's avatar
domenicw committed
119
    private func createButton(_ title: String) -> UIButton {
domenicw's avatar
domenicw committed
120
        let button = UIButton()
domenicw's avatar
domenicw committed
121
        button.setTitle(title, for: .normal)
domenicw's avatar
domenicw committed
122
        button.setTitleColor(.white, for: .normal)
123
        button.setBackgroundColor(.amivRed, for: .normal)
domenicw's avatar
domenicw committed
124
        button.layer.cornerRadius = 12
125
        button.layer.masksToBounds = true
domenicw's avatar
domenicw committed
126
127
128
129
130
131
        button.addTarget(self, action: #selector(self.login), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        
        return button
    }
    
domenicw's avatar
domenicw committed
132
133
134
    private func applyButtonConstraints() {
        NSLayoutConstraint(item: self.button, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
        NSLayoutConstraint(item: self.button, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
domenicw's avatar
domenicw committed
135
        NSLayoutConstraint(item: self.button, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottomMargin, multiplier: 1, constant: -20).isActive = true
domenicw's avatar
domenicw committed
136
        NSLayoutConstraint(item: self.button, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50).isActive = true
domenicw's avatar
domenicw committed
137
138
    }
    
domenicw's avatar
domenicw committed
139
    private func createSmallButton(_ title: String) -> UIButton {
domenicw's avatar
domenicw committed
140
        let button = UIButton()
domenicw's avatar
domenicw committed
141
        button.setTitle(title, for: .normal)
domenicw's avatar
domenicw committed
142
143
        button.setTitleColor(.amivRed, for: .normal)
        button.setTitleColor(.lightGray, for: .highlighted)
domenicw's avatar
domenicw committed
144
        button.addTarget(self, action: #selector(self.smallButtonAction), for: .touchUpInside)
domenicw's avatar
domenicw committed
145
146
147
148
149
        button.translatesAutoresizingMaskIntoConstraints = false
        
        return button
    }
    
domenicw's avatar
domenicw committed
150
151
152
153
154
    private func applySmallButtonConstraints() {
        NSLayoutConstraint(item: self.smallButton, attribute: .top, relatedBy: .greaterThanOrEqual, toItem: self.passwordTextField, attribute: .bottom, multiplier: 1, constant: 30).isActive = true
        NSLayoutConstraint(item: self.smallButton, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
        NSLayoutConstraint(item: self.smallButton, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
        NSLayoutConstraint(item: self.smallButton, attribute: .bottom, relatedBy: .equal, toItem: self.button, attribute: .top, multiplier: 1, constant: -15).isActive = true
domenicw's avatar
domenicw committed
155
156
157
158
159
160
    }
    
    private func createUsernameTextField() -> UITextField {
        let field = UITextField()
        field.tintColor = .amivRed
        field.placeholder = "Username"
domenicw's avatar
domenicw committed
161
        field.textContentType = .username
domenicw's avatar
domenicw committed
162
        field.autocapitalizationType = .none
domenicw's avatar
domenicw committed
163
164
        field.returnKeyType = .continue
        field.borderStyle = .roundedRect
domenicw's avatar
domenicw committed
165
        field.addTarget(self, action: #selector(self.checkLoginButtonEnabling), for: .editingChanged)
domenicw's avatar
domenicw committed
166
        field.addTarget(self, action: #selector(self.fillPassword), for: .primaryActionTriggered)
domenicw's avatar
domenicw committed
167
        field.translatesAutoresizingMaskIntoConstraints = false
domenicw's avatar
domenicw committed
168
169
170
171
172
        
        return field
    }
    
    private func applyUsernameTextFieldConstraints() {
domenicw's avatar
domenicw committed
173
        NSLayoutConstraint(item: self.usernameTextField, attribute: .top, relatedBy: .equal, toItem: self.infoLabel, attribute: .bottom, multiplier: 1, constant: 30).isActive = true
domenicw's avatar
domenicw committed
174
175
176
177
178
179
180
181
        NSLayoutConstraint(item: self.usernameTextField, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
        NSLayoutConstraint(item: self.usernameTextField, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
    }
    
    private func createPasswordTextField() -> UITextField {
        let field = UITextField()
        field.tintColor = .amivRed
        field.placeholder = "Password"
domenicw's avatar
domenicw committed
182
        field.textContentType = .password
domenicw's avatar
domenicw committed
183
        field.returnKeyType = .go
domenicw's avatar
domenicw committed
184
        field.autocapitalizationType = .none
domenicw's avatar
domenicw committed
185
        field.isSecureTextEntry = true
domenicw's avatar
domenicw committed
186
        field.borderStyle = .roundedRect
domenicw's avatar
domenicw committed
187
        field.addTarget(self, action: #selector(self.checkLoginButtonEnabling), for: .editingChanged)
domenicw's avatar
domenicw committed
188
        field.addTarget(self, action: #selector(self.login), for: .primaryActionTriggered)
domenicw's avatar
domenicw committed
189
        field.translatesAutoresizingMaskIntoConstraints = false
domenicw's avatar
domenicw committed
190
191
192
193
194
        
        return field
    }
    
    private func applyPasswordTextFieldConstraints() {
domenicw's avatar
domenicw committed
195
        NSLayoutConstraint(item: self.passwordTextField, attribute: .top, relatedBy: .equal, toItem: self.usernameTextField, attribute: .bottom, multiplier: 1, constant: 20).isActive = true
domenicw's avatar
domenicw committed
196
197
198
199
200
201
202
203
204
205
        NSLayoutConstraint(item: self.passwordTextField, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
        NSLayoutConstraint(item: self.passwordTextField, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
    }
    
    // MARK: - View Setup
    
    public override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.backgroundColor = .white
domenicw's avatar
domenicw committed
206
        self.modalPresentationStyle = .formSheet
domenicw's avatar
domenicw committed
207
208
    }
    
domenicw's avatar
domenicw committed
209
210
211
212
    public override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        self.navigationController?.navigationBar.isHidden = true
domenicw's avatar
domenicw committed
213
214
215
216
217
218
219
        self.checkLoginButtonEnabling()
    }
    
    public override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        self.hideKeyboard()
domenicw's avatar
domenicw committed
220
221
    }
    
domenicw's avatar
domenicw committed
222
223
224
225
    // MARK: - View Interaction
    
    @objc private func login() {
        self.hideKeyboard()
domenicw's avatar
domenicw committed
226
227
228
229
230
231
232
233
234
        
        if let username = self.usernameTextField.text, let password = self.passwordTextField.text {
            self.delegate?.login(username: username, password: password)
        } else {
            self.infoLabel.text = "Oops... This should not happen."
            self.infoLabel.textColor = .red
        }
    }
    
domenicw's avatar
domenicw committed
235
    @objc private func smallButtonAction() {
domenicw's avatar
domenicw committed
236
        self.hideKeyboard()
domenicw's avatar
domenicw committed
237
        self.delegate?.smallButtonTapped()
domenicw's avatar
domenicw committed
238
239
240
241
242
243
244
245
246
247
248
    }
    
    @objc private func fillPassword() {
        self.passwordTextField.becomeFirstResponder()
    }
    
    @objc private func hideKeyboard() {
        self.usernameTextField.resignFirstResponder()
        self.passwordTextField.resignFirstResponder()
    }
    
domenicw's avatar
domenicw committed
249
250
251
252
253
254
255
256
    public func loginFailed(with error: String) {
        self.infoLabel.text = error
        self.infoLabel.textColor = .red
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            self.infoLabel.textColor = .black
            self.infoLabel.text = self.infoLabelText
        }
domenicw's avatar
domenicw committed
257
258
    }
    
domenicw's avatar
domenicw committed
259
260
261
262
263
264
265
266
267
268
    @objc private func checkLoginButtonEnabling() {
        if self.usernameTextField.text == "" || self.passwordTextField.text == "" {
            self.button.isEnabled = false
            self.button.setBackgroundColor(UIColor.amivRed.withAlphaComponent(0.9), for: .normal)
        } else {
            self.button.isEnabled = true
            self.button.setBackgroundColor(UIColor.amivRed.withAlphaComponent(1), for: .normal)
        }
    }
    
domenicw's avatar
domenicw committed
269
}