Вы можете дать ячейке собственную inputView
- вам нужно override
, и для ее реализации требуется всего несколько шагов.
Вот простой пример (собран очень быстро, поэтому не учитывайте этот код «готов к производству»):
Постукивание по строке приведет к ячейке becomeFirstResponder
. Его inputView
представляет собой пользовательское представление с UIDatePicker
в качестве подпредставления.
Когда вы выбираете новую дату / время в средстве выбора, оно обновляет эту ячейку (и источник данных поддержки).
Нажатие на текущую ячейку приведет к resignFirstResponder
, что приведет к отклонению DatePickerKeyboard
.
Вот код. Ячейка основана на коде (не является прототипом раскадровки) и не использует IBOutlet
s или IBAction
s ... просто добавьте UITableViewController
и присвойте ее InputViewTableViewController
:
// protocol so we can send back the newly selected date
@objc protocol MyDatePickerProtocol {
@objc func updateDate(_ newDate: Date)
}
// basic UIView with UIDatePicker added as subview
class DatePickKeyboard: UIView {
var theDatePicker: UIDatePicker = UIDatePicker()
weak var delegate: MyDatePickerProtocol?
init(delegate: MyDatePickerProtocol) {
self.delegate = delegate
super.init(frame: .zero)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// UIDatePicker target
extension DatePickKeyboard {
@objc func dpChanged(_ sender: Any?) -> Void {
if let dp = sender as? UIDatePicker {
// tell the delegat we have a new date
delegate?.updateDate(dp.date)
}
}
}
// MARK: - Private initial configuration methods
private extension DatePickKeyboard {
func configure() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
theDatePicker.addTarget(self, action: #selector(dpChanged(_:)), for: .valueChanged)
addSubview(theDatePicker)
theDatePicker.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
theDatePicker.centerXAnchor.constraint(equalTo: centerXAnchor),
theDatePicker.centerYAnchor.constraint(equalTo: centerYAnchor),
theDatePicker.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0),
theDatePicker.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1.0),
])
}
}
// table view cell with a single UILabel
// enable canBecomeFirstResponder and use
// DatePickerKeyboard view instead of default keyboard
class InputViewCell: UITableViewCell, MyDatePickerProtocol {
var theLabel: UILabel = {
let v = UILabel()
return v
}()
var myInputView: UIView?
var myCallback: ((Date)->())?
var myDate: Date = Date()
var theDate: Date {
get {
return self.myDate
}
set {
self.myDate = newValue
let df = DateFormatter()
df.dateFormat = "MMM d, h:mm a"
let s = df.string(from: self.myDate)
theLabel.text = s
}
}
override var canBecomeFirstResponder: Bool { return true }
override var inputView: UIView {
get {
return self.myInputView!
}
set {
self.myInputView = newValue
}
}
@discardableResult
override func becomeFirstResponder() -> Bool {
let becameFirstResponder = super.becomeFirstResponder()
if let dpv = self.inputView as? DatePickKeyboard {
dpv.theDatePicker.date = self.myDate
}
updateUI()
return becameFirstResponder
}
@discardableResult
override func resignFirstResponder() -> Bool {
let resignedFirstResponder = super.resignFirstResponder()
updateUI()
return resignedFirstResponder
}
func updateUI() -> Void {
// change the appearance if desired
backgroundColor = isFirstResponder ? .yellow : .clear
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
theLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(theLabel)
let v = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: v.topAnchor),
theLabel.bottomAnchor.constraint(equalTo: v.bottomAnchor),
theLabel.leadingAnchor.constraint(equalTo: v.leadingAnchor),
theLabel.trailingAnchor.constraint(equalTo: v.trailingAnchor),
])
inputView = DatePickKeyboard(delegate: self)
}
@objc func updateDate(_ newDate: Date) -> Void {
self.theDate = newDate
myCallback?(newDate)
}
}
// must conform to UIKeyInput, even if we're not using the standard funcs
extension InputViewCell: UIKeyInput {
var hasText: Bool { return false }
func insertText(_ text: String) { }
func deleteBackward() { }
}
// simple table view controller
class InputViewTableViewController: UITableViewController {
var theData: [Date] = [Date]()
override func viewDidLoad() {
super.viewDidLoad()
// generate some date data to work with - 25 dates incrementing by 2 days
var d = Calendar.current.date(byAdding: .day, value: -50, to: Date())
for _ in 1...25 {
theData.append(d!)
d = Calendar.current.date(byAdding: .day, value: 2, to: d!)
}
// register our custom cell
tableView.register(InputViewCell.self, forCellReuseIdentifier: "InputViewCell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return theData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "InputViewCell", for: indexPath) as! InputViewCell
c.theDate = theData[indexPath.row]
c.myCallback = { d in
self.theData[indexPath.row] = d
}
c.selectionStyle = .none
return c
}
// on row tap, either become or resign as first responder
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let c = tableView.cellForRow(at: indexPath) as? InputViewCell {
if c.isFirstResponder {
c.resignFirstResponder()
} else {
c.becomeFirstResponder()
}
}
tableView.deselectRow(at: indexPath, animated: false)
}
}