如何使用 UIDatePicker 输入管理带有两个 UITextField 的 firstResponder
How to manage firstResponder with two UITextFields using UIDatePicker input
我有一个 ViewController
,其中两个 UITextFields
使用 UIDatePicker(s)
作为输入。一个是 "From" 日期,另一个是 "To" 日期,所以这似乎是一个相当常见的场景。
问题是,当一个字段的 DatePicker 处于活动状态时,用户可能会点击屏幕上的另一个字段。活动的 DatePicker 仍然与第一个字段相关联,因此 UI 会让用户感到困惑,因为光标在另一个字段中闪烁,但输入不在那里。
我认为正确的 UI 解决方案是防止用户在关闭 DatePicker 之前点击任何其他字段。但是我无法找到一种方法来检测 firstResponder 正在更改或暂时禁用对其他字段的编辑。
这是我当前的代码,它没有做我需要它做的事情:
class AddTripLocationVC: UIViewController, UITextFieldDelegate {
@IBOutlet private weak var tripLocFromDateInput: UITextField!
@IBOutlet private weak var tripLocToDateInput: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//set up datepickers for text field inputs
createDatePicker(forDateField: tripLocFromDateInput)
createDatePicker(forDateField: tripLocToDateInput)
}
//automatically update the label fields when the date vairables are updated by the UIDatePicker
var locFromDate: Date? {
didSet {
guard locFromDate != nil else { return }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy"
tripLocFromDateInput.text = dateFormatter.string(from: locFromDate!)
}
}
var locToDate: Date? {
didSet {
guard locToDate != nil else { return }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy"
tripLocFromDateInput.text = dateFormatter.string(from: locToDate!)
//Comment after solution found...The problem was the line above
//It should be:
tripLocToDateInput.text = dateFormatter.string(from: locToDate!)
}
}
func createDatePicker(forDateField dateField: UITextField) {
let datePickerView = UIDatePicker()
datePickerView.datePickerMode = .date
dateField.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(handleDatePicker(sender:)), for: .valueChanged)
let doneButton = UIBarButtonItem.init(title: "Done", style: .done, target: self, action: #selector(self.datePickerDone))
let toolBar = UIToolbar.init(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 44))
toolBar.setItems([UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil), doneButton],
animated: true)
dateField.inputAccessoryView = toolBar
}
@objc
func handleDatePicker(sender: UIDatePicker) {
if tripLocFromDateInput.isFirstResponder {
locFromDate = sender.date
} else {
if tripLocToDateInput.isFirstResponder {
locToDate = sender.date
} else {
print("Error: Can't find first responder field.")
}
}
}
@objc
func datePickerDone(dateField: UITextView) {
if tripLocFromDateInput.isFirstResponder {
tripLocFromDateInput.resignFirstResponder()
} else {
if tripLocToDateInput.isFirstResponder {
tripLocToDateInput.resignFirstResponder()
} else {
print("Error: Can't find first responder field.")
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
textField.becomeFirstResponder()
}
}
对于 iOS 中的此类 'From' 和 'To' 日期字段,UI 是否有 'standard' 方法?
您可以注册一个 UITextField 委托,其中包含您特别感兴趣的两个方法,特别是:
textFieldDidBeginEditing(:)
textFieldDidEndEditing(:)
要获取更多信息,请访问:https://developer.apple.com/documentation/uikit/uitextfielddelegate
请注意,您还可以在任何 UITextField 上使用 becomeFirstResponder()
来激活它。激活的那个应该有闪烁的光标。
编辑:根据您更新的代码:
//
// ViewController.swift
// datepickers
//
// Created by Dave Poirier on 2019-06-13.
//
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet private weak var tripLocFromDateInput: UITextField!
@IBOutlet private weak var tripLocToDateInput: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//set up datepickers for text field inputs
createDatePicker(forDateField: tripLocFromDateInput)
createDatePicker(forDateField: tripLocToDateInput)
//set text field delegates to be notified when focus changes
tripLocFromDateInput.delegate = self
tripLocToDateInput.delegate = self
}
func createDatePicker(forDateField dateField: UITextField) {
let datePickerView = UIDatePicker()
datePickerView.datePickerMode = .date
dateField.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(handleDatePicker(sender:)), for: .valueChanged)
let doneButton = UIBarButtonItem.init(title: "Done", style: .done, target: self, action: #selector(self.datePickerDone))
let toolBar = UIToolbar.init(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 44))
toolBar.setItems([UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil), doneButton],
animated: true)
dateField.inputAccessoryView = toolBar
}
@objc
func handleDatePicker(sender: UIDatePicker) {
if tripLocFromDateInput.isFirstResponder {
let locFromDate = sender.date
tripLocFromDateInput.text = "FROM: \(locFromDate)"
} else {
if tripLocToDateInput.isFirstResponder {
let locToDate = sender.date
tripLocToDateInput.text = "TO: \(locToDate)"
} else {
print("Error: Can't find first responder field.")
}
}
}
@objc
func datePickerDone(dateField: UITextView) {
if tripLocFromDateInput.isFirstResponder {
tripLocFromDateInput.resignFirstResponder()
} else {
if tripLocToDateInput.isFirstResponder {
tripLocToDateInput.resignFirstResponder()
} else {
print("Error: Can't find first responder field.")
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
self.view.endEditing(true)
}
}
}
上面的代码在一个全新的项目中为我工作,创建了两个输入字段并连接了 IBOutlets
我有一个 ViewController
,其中两个 UITextFields
使用 UIDatePicker(s)
作为输入。一个是 "From" 日期,另一个是 "To" 日期,所以这似乎是一个相当常见的场景。
问题是,当一个字段的 DatePicker 处于活动状态时,用户可能会点击屏幕上的另一个字段。活动的 DatePicker 仍然与第一个字段相关联,因此 UI 会让用户感到困惑,因为光标在另一个字段中闪烁,但输入不在那里。
我认为正确的 UI 解决方案是防止用户在关闭 DatePicker 之前点击任何其他字段。但是我无法找到一种方法来检测 firstResponder 正在更改或暂时禁用对其他字段的编辑。
这是我当前的代码,它没有做我需要它做的事情:
class AddTripLocationVC: UIViewController, UITextFieldDelegate {
@IBOutlet private weak var tripLocFromDateInput: UITextField!
@IBOutlet private weak var tripLocToDateInput: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//set up datepickers for text field inputs
createDatePicker(forDateField: tripLocFromDateInput)
createDatePicker(forDateField: tripLocToDateInput)
}
//automatically update the label fields when the date vairables are updated by the UIDatePicker
var locFromDate: Date? {
didSet {
guard locFromDate != nil else { return }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy"
tripLocFromDateInput.text = dateFormatter.string(from: locFromDate!)
}
}
var locToDate: Date? {
didSet {
guard locToDate != nil else { return }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy"
tripLocFromDateInput.text = dateFormatter.string(from: locToDate!)
//Comment after solution found...The problem was the line above
//It should be:
tripLocToDateInput.text = dateFormatter.string(from: locToDate!)
}
}
func createDatePicker(forDateField dateField: UITextField) {
let datePickerView = UIDatePicker()
datePickerView.datePickerMode = .date
dateField.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(handleDatePicker(sender:)), for: .valueChanged)
let doneButton = UIBarButtonItem.init(title: "Done", style: .done, target: self, action: #selector(self.datePickerDone))
let toolBar = UIToolbar.init(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 44))
toolBar.setItems([UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil), doneButton],
animated: true)
dateField.inputAccessoryView = toolBar
}
@objc
func handleDatePicker(sender: UIDatePicker) {
if tripLocFromDateInput.isFirstResponder {
locFromDate = sender.date
} else {
if tripLocToDateInput.isFirstResponder {
locToDate = sender.date
} else {
print("Error: Can't find first responder field.")
}
}
}
@objc
func datePickerDone(dateField: UITextView) {
if tripLocFromDateInput.isFirstResponder {
tripLocFromDateInput.resignFirstResponder()
} else {
if tripLocToDateInput.isFirstResponder {
tripLocToDateInput.resignFirstResponder()
} else {
print("Error: Can't find first responder field.")
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
textField.becomeFirstResponder()
}
}
对于 iOS 中的此类 'From' 和 'To' 日期字段,UI 是否有 'standard' 方法?
您可以注册一个 UITextField 委托,其中包含您特别感兴趣的两个方法,特别是:
textFieldDidBeginEditing(:)
textFieldDidEndEditing(:)
要获取更多信息,请访问:https://developer.apple.com/documentation/uikit/uitextfielddelegate
请注意,您还可以在任何 UITextField 上使用 becomeFirstResponder()
来激活它。激活的那个应该有闪烁的光标。
编辑:根据您更新的代码:
//
// ViewController.swift
// datepickers
//
// Created by Dave Poirier on 2019-06-13.
//
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet private weak var tripLocFromDateInput: UITextField!
@IBOutlet private weak var tripLocToDateInput: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//set up datepickers for text field inputs
createDatePicker(forDateField: tripLocFromDateInput)
createDatePicker(forDateField: tripLocToDateInput)
//set text field delegates to be notified when focus changes
tripLocFromDateInput.delegate = self
tripLocToDateInput.delegate = self
}
func createDatePicker(forDateField dateField: UITextField) {
let datePickerView = UIDatePicker()
datePickerView.datePickerMode = .date
dateField.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(handleDatePicker(sender:)), for: .valueChanged)
let doneButton = UIBarButtonItem.init(title: "Done", style: .done, target: self, action: #selector(self.datePickerDone))
let toolBar = UIToolbar.init(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 44))
toolBar.setItems([UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil), doneButton],
animated: true)
dateField.inputAccessoryView = toolBar
}
@objc
func handleDatePicker(sender: UIDatePicker) {
if tripLocFromDateInput.isFirstResponder {
let locFromDate = sender.date
tripLocFromDateInput.text = "FROM: \(locFromDate)"
} else {
if tripLocToDateInput.isFirstResponder {
let locToDate = sender.date
tripLocToDateInput.text = "TO: \(locToDate)"
} else {
print("Error: Can't find first responder field.")
}
}
}
@objc
func datePickerDone(dateField: UITextView) {
if tripLocFromDateInput.isFirstResponder {
tripLocFromDateInput.resignFirstResponder()
} else {
if tripLocToDateInput.isFirstResponder {
tripLocToDateInput.resignFirstResponder()
} else {
print("Error: Can't find first responder field.")
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
self.view.endEditing(true)
}
}
}
上面的代码在一个全新的项目中为我工作,创建了两个输入字段并连接了 IBOutlets