简体   繁体   中英

Keyboard beyond date picker

I'm trying to display a datepicker when you tap on a textfield. This all seems to be working fine but as soon as I dismiss the date picker it will show the keyboard instead.

So I basically have to dismiss it 2 times before it will actually disappear.

I've created a textfield class for the picker which looks the following

EDIT MOVED ALL THE CODE INTO THE TEXTFIELD CLASS

protocol TextFieldPickerDelegate {
    func pickerValueDidChange(date: Date, pickerMode: UIDatePickerMode, sender: TextFieldPicker)
}

public class TextFieldPicker: UITextField, UITextFieldDelegate {

    var pickerMode: UIDatePickerMode = .date

    @IBInspectable var pickerType: String? {
        willSet {
            for s in (newValue?.lowercased().getStrings())! {
                switch s {
                case "date":
                    pickerMode = .date
                case "dateandtime":
                    pickerMode = .dateAndTime
                case "countdowntimer":
                    pickerMode = .countDownTimer
                case "time":
                    pickerMode = .time
                default:
                    pickerMode = .date
                }
            }
        }
        didSet {
            setupTextField()
        }
    }

    var textFieldPickerDelegate: TextFieldPickerDelegate?

    func setupTextField() {
        createPicker()
    }

    override public func draw(_ rect: CGRect) {
        super.draw(rect)

        delegate = self
    }

    override public func caretRect(for position: UITextPosition) -> CGRect {
        return CGRect.zero
    }

    override public func shouldChangeText(in range: UITextRange, replacementText text: String) -> Bool {
        return false
    }

    public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        return false
    }

    public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        self.resignFirstResponder()
        return true
    }

    public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
        self.resignFirstResponder()
        return true
    }

    func createPicker() {
        let datePicker = UIDatePicker()
        datePicker.datePickerMode = pickerMode
        datePicker.addTarget(self, action: #selector(pickerValueChange(_:)), for: .valueChanged)

        let pickerToolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44))
        pickerToolBar.tintColor = UIColor.gray
        let doneButton = UIBarButtonItem(title: NSLocalizedString("done", comment: ""), style: .done, target: self, action: #selector(closePicker(_:)))
        let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        pickerToolBar.setItems([space, doneButton], animated: true)

        self.inputView = datePicker
        self.inputAccessoryView = pickerToolBar
    }

    func closePicker(_ sender: Any) {
        self.resignFirstResponder()
    }

    func pickerValueChange(_ sender: UIDatePicker) {
        self.textFieldPickerDelegate?.pickerValueDidChange(date: sender.date, pickerMode: sender.datePickerMode, sender: self)
    }

    public func getDateString(date: Date, format: String) -> String {
        let timeFormatter = DateFormatter()
        timeFormatter.dateFormat = format
        return timeFormatter.string(from: date)
    }
}






// =================================================== OptionSet =================================================== \\
public struct PickerModes: OptionSet {
    private enum PickerMode: Int, CustomStringConvertible {
        case Date=1
        case DateAndTime=2
        case CountDownTimer=4
        case Time=8

        public var description: String {
            var shift = 0
            while (rawValue.hashValue >> shift != 1) { shift += 1 }
            return ["date", "dateandtime", "countdowntimer", "time"][shift]
        }
    }
    public let rawValue: Int
    public init(rawValue: Int) { self.rawValue = rawValue }
    private init(_ mode: PickerMode) { self.rawValue = mode.rawValue }

    static let Date = PickerModes(PickerMode.Date)
    static let DateAndTime = PickerModes(PickerMode.DateAndTime)
    static let CountDownTimer = PickerModes(PickerMode.CountDownTimer)
    static let Time = PickerModes(PickerMode.Time)
}

extension String {
    func getStrings() -> [String] {
        var stringArray: [String] = []
        let strings = self.characters.split{$0 == ","}.map(String.init)
        for s in strings {
            let string = s.removeSpaces()
            stringArray.append(string)
        }
        return stringArray
    }

    func removeSpaces() -> String {
        if self.characters.first == " " {
            var copy = self
            copy.characters.removeFirst()
            return copy.removeSpaces()
        } else {
            return self
        }
    }
}

This class is entirely responsible for showing the date picker and dealing with displaying the correct type of date picker in case there are multiple different pickers needed in one screen.

EDIT 2
If anyone else tries my code it will work fine without any problems. The issue I was having is that I accidentally set my custom class to a UIView that contained my UITextField so when I tapped the textfield it would first trigger the keyboard for the UIView and then the UIDatePicker so when the UIDatePicker was resigned the keyboard would still be there.

I have been doing some research and it seems to me that there are much easier methods of creating a date picker inside a text field. You could just use one of them. https://blog.apoorvmote.com/change-textfield-input-to-datepicker/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM