有没有办法将我所有的视图控制器日期选择器逻辑放入一个单独的类中,以保持我的代码井井有条?

时间:2019-04-07 00:09:00

标签: ios swift uidatepicker

我正在设置一个非常简单的iPhone应用程序,该应用程序具有多个文本字段,使用日期选择器控件输入日期和时间字符串。

问题是,尽管一切运行正常,但我发现我在多个字段中复制了很多代码,这感觉像是不好的做法。

我想要的是一种将这段代码压缩成一个单独的类,并在需要时从任何地方调用它的方法。


下面是我的视图控制器中的一个示例,该示例工作得很好,但必须对多个字段重复很多次:

@IBOutlet weak var dateField: UITextField!

@objc func dateFieldModified(sender:UIDatePicker) {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MM/dd/yyyy"
    dateField.text = dateFormatter.string(from: sender.date)
}

@IBAction func dateFieldModify(_ sender: UITextField) {
    let datePicker = UIDatePicker()
    datePicker.datePickerMode = UIDatePicker.Mode.date
    datePicker.addTarget(self, action: #selector(self.dateFieldModified(sender:)), for: UIControl.Event.valueChanged)
    dateField.inputView = datePicker
}

总而言之,当用户输入文本字段“ dateField”时,将弹出一个日期选择器,并允许用户滚动和选择一个日期。当用户选择日期时,文本字段将设置为反映所选日期。

我想在视图控制器中执行的操作是这样的:

@IBOutlet weak var dateField: UITextField!

@IBAction func dateFieldModify(_ sender: UITextField) {
    let datePicker = DatePicker(field:dateField, mode:"date", format: "MM/dd/yyyy")
    datePicker.loadDatePicker()
}

并有一个单独定义的类,它可能看起来像这样:

class DatePicker {

    var field:UITextField
    var mode:String
    var format:String

    init(field:UITextField, mode:String, format:String) {
        self.field = field
        self.mode = mode
        self.format = format
    }

    @objc func dateFieldModified(sender:UIDatePicker) {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = format
        field.text = dateFormatter.string(from: sender.date)
    }

    func loadDatePicker() {
        let datePicker = UIDatePicker()
        datePicker.datePickerMode = UIDatePicker.Mode.date
        datePicker.addTarget(self, action: #selector(self.dateFieldModified(sender:)), for: UIControl.Event.valueChanged)
        field.inputView = datePicker
    }

}

实现上面我写的新的DatePicker类,几乎所有东西都起作用:当我单击“ dateField”文本字段时,日期选择器打开,并且我可以滚动并选择一个日期。但是,选择日期后... dateField仍然为空。

我通常是Swift和OOP的新手,所以我确定我缺少代码或重要概念。

2 个答案:

答案 0 :(得分:2)

管理两个单独的类的实例(UITextField和您的DatePicker)有点烦人,并且很容易导致引用周期。

如何定义自己的TextField类型?

类似这样的东西:

class DatePickerTextField: UITextField {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setUp()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUp()
    }

    private func setUp() {
        self.addTarget(self, action: #selector(dateFieldModify(_:)), for: .editingDidBegin)
    }

    @objc func dateFieldModified(_ sender: UIDatePicker) {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MM/dd/yyyy"
        self.text = dateFormatter.string(from: sender.date)
    }

    @IBAction func dateFieldModify(_ sender: UITextField) {
        let datePicker = UIDatePicker()
        datePicker.datePickerMode = .date
        datePicker.addTarget(self, action: #selector(self.dateFieldModified), for: .valueChanged)
        self.inputView = datePicker
    }

}

我认为您可以自己改善这一点。

答案 1 :(得分:1)

是的,您可以(可能应该)将此逻辑封装在单独的类中。但是就像OOPer所说的那样,您可能想要为此定义一个using (SqlConnection con = new SqlConnection(cs)) { using (SqlCommand cmd = new SqlCommand("spAddEmployee", con)) { string name = txtEmployeeName.Text; string gender = ddlGender.SelectedValue; int salary = Convert.ToInt32(txtSalary.Text); SqlParameter op = new SqlParameter("@EmployeeId", SqlDbType.Int); op.Direction = System.Data.ParameterDirection.Output; cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@Name", SqlDbType.VarChar).Value = name; //add for your other variables here cmd.Parameters.Add(op); con.Open(); cmd.ExecuteNonQuery(); } } 子类。

我还会提出其他一些建议:

  1. 我建议您为该文本字段提供一个UITextField属性,以便您可以设置和检索与该文本字段关联的date。就像Date一样,视图控制器应该通过UIDatePicker属性与此新控件进行交互,而不必处理日期格式化程序本身。

  2. 当使用日期选择器作为输入源时,我认为添加带有“完成”按钮的工具栏是很好的,这样用户就可以消除输入源(就像您可以弹出键盘一样)

  3. 我不建议将datedateFormat一起使用,而建议使用DateFormatterdateStyle。您始终希望您的用户界面显示本地化的日期字符串。

  4. 您可能应该处理可能已连接到设备的硬件键盘(对于以iPad为目标的情况尤其重要)。因此,您可能希望日期文本字段直接在文本字段中了解字符串的修改以及从日期选择器中选择日期。

  5. 您可能希望使用自己的文本字段协议来告知UI日期值的更改。

因此,我可能会建议:

timeStyle

然后您可以在IB中指定基类/// Date text field delegate protocol @objc protocol DateTextFieldDelegate { @objc optional func dateTextField(_ dateTextField: DateTextField, didChangeDate: Date?) @objc optional func didTapDone(dateTextField: DateTextField) } /// Date text field /// /// Used for entry of dates in UITextField, replacing keyboard with date picker. class DateTextField: UITextField { /// `DateTextField` delegate /// /// You don't need to supply a delegate, but if you do, this will tell you as /// the user changes the date. weak var dateTextFieldDelegate: DateTextFieldDelegate? /// Default date /// /// If `nil`, uses today's date var defaultDate: Date? /// Date formatter for date /// /// Feel free to change `dateStyle` and `timeStyle` to suit the needs of your app. let formatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .none return formatter }() /// Date /// /// The user's selected date. var date: Date? { didSet { dateTextFieldDelegate?.dateTextField?(self, didChangeDate: date) if !isManuallyEditing { text = date.map { formatter.string(from: $0) } } datePicker.date = date ?? defaultDate ?? Date() } } var dateTextFieldButtonType: DateTextFieldButtonType = .done { didSet { doneButton?.title = dateTextFieldButtonType.buttonText } } /// The date picker. lazy var datePicker: UIDatePicker = { let picker = UIDatePicker() picker.datePickerMode = .date return picker }() // MARK: - Private properties /// Private reference for "Done" button private var doneButton: UIBarButtonItem! /// Private flag is the user is manually changing the date. private var isManuallyEditing = false // MARK: - Initialization override init(frame: CGRect = .zero) { super.init(frame: frame) configure() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) configure() } } // MARK: - Private utility methods private extension DateTextField { func configure() { inputView = datePicker datePicker.addTarget(self, action: #selector(datePickerModified(_:)), for: .valueChanged) let toolBar = UIToolbar() toolBar.barStyle = .default toolBar.isTranslucent = true let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) doneButton = UIBarButtonItem(title: dateTextFieldButtonType.buttonText, style: .done, target: self, action: #selector(didTapDone(_:))) clearButtonMode = .whileEditing toolBar.setItems([space, doneButton], animated: false) toolBar.isUserInteractionEnabled = true toolBar.sizeToFit() inputAccessoryView = toolBar addTarget(self, action: #selector(textFieldModified(_:)), for: .editingChanged) } } // MARK: - Actions extension DateTextField { @objc func didTapDone(_ sender: Any) { if dateTextFieldButtonType == .select { date = datePicker.date } resignFirstResponder() dateTextFieldDelegate?.didTapDone?(dateTextField: self) } @objc func textFieldModified(_ textField: UITextField) { isManuallyEditing = true date = text.flatMap { formatter.date(from: $0) } isManuallyEditing = false } @objc func datePickerModified(_ datePicker: UIDatePicker) { date = datePicker.date } } // MARK: - Enumerations extension DateTextField { enum DateTextFieldButtonType { case select case done case next } } extension DateTextField.DateTextFieldButtonType { var buttonText: String { switch self { case .select: return NSLocalizedString("Select", comment: "DateTextFieldButtonType") case .done: return NSLocalizedString("Done", comment: "DateTextFieldButtonType") case .next: return NSLocalizedString("Next", comment: "DateTextFieldButtonType") } } } 而不是DateTextField,就可以了。

或者,如果要实现委托协议,则可以执行以下操作:

UITextField

或者如果您想要日期和时间:

class ViewController: UIViewController {

    @IBOutlet weak var textField: DateTextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        textField.dateTextFieldDelegate = self
    }

}

extension ViewController: DateTextFieldDelegate {
    func didTapDone(dateTextField: DateTextField) {
        print("keyboard dismissed")
    }

    func dateTextField(_ dateTextField: DateTextField, didChangeDate date: Date?) {
        print(date ?? "No date specified")
    }
}

很明显,您可以根据需要修改此行为,但这可以说明问题。

但是您是正确的,应该使用日期选择器从您的视图控制器中提取此文本字段,并将其放在自己的类中。并根据您认为最适合您的应用程序的行为来定制此子类。