blob: 5f72ad6c5f6bd0563686740d2b1c202d6ae3f15d [file] [log] [blame] [edit]
// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// swiftlint:disable function_body_length
import MaterialComponents.MaterialTextFields
final class TextFieldManualLayoutLegacySwiftExample: UIViewController {
private enum LayoutConstants {
static let largeMargin: CGFloat = 16
static let smallMargin: CGFloat = 8
static let floatingHeight: CGFloat = 84
static let defaultHeight: CGFloat = 62
static let stateWidth: CGFloat = 80
}
let scrollView = UIScrollView()
let name: MDCTextField = {
let name = MDCTextField()
name.autocapitalizationType = .words
return name
}()
let address: MDCTextField = {
let address = MDCTextField()
address.autocapitalizationType = .words
return address
}()
let city: MDCTextField = {
let city = MDCTextField()
city.autocapitalizationType = .words
return city
}()
let cityController: MDCTextInputControllerLegacyDefault
let state: MDCTextField = {
let state = MDCTextField()
state.autocapitalizationType = .allCharacters
return state
}()
let zip: MDCTextField = {
let zip = MDCTextField()
return zip
}()
let zipController: MDCTextInputControllerLegacyDefault
let phone: MDCTextField = {
let phone = MDCTextField()
return phone
}()
let stateZip = UIView()
var allTextFieldControllers = [MDCTextInputControllerLegacyDefault]()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
cityController = MDCTextInputControllerLegacyDefault(textInput: city)
zipController = MDCTextInputControllerLegacyDefault(textInput: zip)
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white:0.97, alpha: 1.0)
title = "Legacy Manual Text Fields"
setupScrollView()
setupTextFields()
updateLayout()
registerKeyboardNotifications()
addGestureRecognizer()
let styleButton = UIBarButtonItem(title: "Style",
style: .plain,
target: self,
action: #selector(buttonDidTouch(sender: )))
self.navigationItem.rightBarButtonItem = styleButton
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
scrollView.frame = view.bounds
}
func setupTextFields() {
scrollView.addSubview(name)
let nameController = MDCTextInputControllerLegacyDefault(textInput: name)
name.delegate = self
nameController.placeholderText = "Name"
allTextFieldControllers.append(nameController)
scrollView.addSubview(address)
let addressController = MDCTextInputControllerLegacyDefault(textInput: address)
address.delegate = self
addressController.placeholderText = "Address"
allTextFieldControllers.append(addressController)
scrollView.addSubview(city)
city.delegate = self
cityController.placeholderText = "City"
allTextFieldControllers.append(cityController)
scrollView.addSubview(stateZip)
stateZip.addSubview(state)
let stateController = MDCTextInputControllerLegacyDefault(textInput: state)
state.delegate = self
stateController.placeholderText = "State"
allTextFieldControllers.append(stateController)
stateZip.addSubview(zip)
zip.delegate = self
zipController.placeholderText = "Zip Code"
zipController.helperText = "XXXXX"
allTextFieldControllers.append(zipController)
scrollView.addSubview(phone)
let phoneController = MDCTextInputControllerLegacyDefault(textInput: phone)
phone.delegate = self
phoneController.placeholderText = "Phone Number"
allTextFieldControllers.append(phoneController)
var tag = 0
for controller in allTextFieldControllers {
guard let textField = controller.textInput as? MDCTextField else { continue }
textField.tag = tag
tag += 1
}
}
func setupScrollView() {
view.addSubview(scrollView)
scrollView.contentSize =
CGSize(width: scrollView.bounds.width - 2 * LayoutConstants.largeMargin,
height: 500.0)
}
func addGestureRecognizer() {
let tapRecognizer = UITapGestureRecognizer(target: self,
action: #selector(tapDidTouch(sender: )))
self.scrollView.addGestureRecognizer(tapRecognizer)
}
func updateLayout() {
let commonWidth = view.bounds.width - 2 * LayoutConstants.largeMargin
var height = LayoutConstants.floatingHeight
if let controller = allTextFieldControllers.first {
height = controller.isFloatingEnabled ?
LayoutConstants.floatingHeight : LayoutConstants.defaultHeight
}
name.frame = CGRect(x: LayoutConstants.largeMargin,
y: LayoutConstants.smallMargin,
width: commonWidth,
height: height)
address.frame = CGRect(x: LayoutConstants.largeMargin,
y: name.frame.minY + height + LayoutConstants.smallMargin,
width: commonWidth,
height: height)
city.frame = CGRect(x: LayoutConstants.largeMargin,
y: address.frame.minY + height + LayoutConstants.smallMargin,
width: commonWidth,
height: height)
stateZip.frame = CGRect(x: LayoutConstants.largeMargin,
y: city.frame.minY + height + LayoutConstants.smallMargin,
width: commonWidth,
height: height)
state.frame = CGRect(x: 0,
y: 0,
width: LayoutConstants.stateWidth,
height: height)
zip.frame = CGRect(x: LayoutConstants.stateWidth + LayoutConstants.smallMargin,
y: 0,
width: stateZip.bounds.width - LayoutConstants.stateWidth -
LayoutConstants.smallMargin,
height: height)
phone.frame = CGRect(x: LayoutConstants.largeMargin,
y: stateZip.frame.minY + height + LayoutConstants.smallMargin,
width: commonWidth,
height: height)
}
// MARK: - Actions
@objc func tapDidTouch(sender: Any) {
self.view.endEditing(true)
}
@objc func buttonDidTouch(sender: Any) {
let alert = UIAlertController(title: "Floating Enabled",
message: nil,
preferredStyle: .actionSheet)
let defaultAction = UIAlertAction(title: "Default (Yes)", style: .default) { _ in
self.allTextFieldControllers.forEach({ (controller) in
controller.isFloatingEnabled = true
})
self.updateLayout()
}
alert.addAction(defaultAction)
let floatingAction = UIAlertAction(title: "No", style: .default) { _ in
self.allTextFieldControllers.forEach({ (controller) in
controller.isFloatingEnabled = false
})
self.updateLayout()
}
alert.addAction(floatingAction)
present(alert, animated: true, completion: nil)
}
}
extension TextFieldManualLayoutLegacySwiftExample: UITextFieldDelegate {
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
guard let rawText = textField.text else {
return true
}
let fullString = NSString(string: rawText).replacingCharacters(in: range, with: string)
if textField == zip {
if let range = fullString.rangeOfCharacter(from: CharacterSet.letters),
String(fullString[range]).characterCount > 0 {
zipController.setErrorText("Error: Zip can only contain numbers",
errorAccessibilityValue: nil)
} else if fullString.characterCount > 5 {
zipController.setErrorText("Error: Zip can only contain five digits",
errorAccessibilityValue: nil)
} else {
zipController.setErrorText(nil, errorAccessibilityValue: nil)
}
} else if textField == city {
if let range = fullString.rangeOfCharacter(from: CharacterSet.decimalDigits),
String(fullString[range]).characterCount > 0 {
cityController.setErrorText("Error: City can only contain letters",
errorAccessibilityValue: nil)
} else {
cityController.setErrorText(nil, errorAccessibilityValue: nil)
}
}
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let index = textField.tag
if index + 1 < allTextFieldControllers.count,
let nextField = allTextFieldControllers[index + 1].textInput {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
}
// MARK: - Keyboard Handling
extension TextFieldManualLayoutLegacySwiftExample {
func registerKeyboardNotifications() {
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(
self,
selector: #selector(keyboardWillShow(notif:)),
name: UIResponder.keyboardWillShowNotification,
object: nil)
notificationCenter.addObserver(
self,
selector: #selector(keyboardWillShow(notif:)),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil)
notificationCenter.addObserver(
self,
selector: #selector(keyboardWillHide(notif:)),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}
@objc func keyboardWillShow(notif: Notification) {
guard let frame = notif.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
return
}
scrollView.contentInset = UIEdgeInsets(top: 0.0,
left: 0.0,
bottom: frame.height,
right: 0.0)
}
@objc func keyboardWillHide(notif: Notification) {
scrollView.contentInset = UIEdgeInsets()
}
}
// MARK: - Status Bar Style
extension TextFieldManualLayoutLegacySwiftExample {
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
// MARK: - CatalogByConvention
extension TextFieldManualLayoutLegacySwiftExample {
@objc class func catalogMetadata() -> [String: Any] {
return [
"breadcrumbs": ["Text Field", "[Legacy] Manual Layout (Swift)"],
"primaryDemo": false,
"presentable": false,
]
}
}