You can give a cell its own inputView -- you have to override it, and it just takes a few steps to implement.
Here is a simple example (put together very quickly, so don't consider this "production ready" code):

Tapping on a row will cause the cell to becomeFirstResponder. Its inputView is a custom view with a UIDatePicker as a subview.
As you select a new date / time in the picker, it will update that cell (and the backing data source).
Tapping on the current cell will cause it to resignFirstResponder which will dismiss the DatePickerKeyboard.
Here is the code. The cell is code-based (not a Storyboard Prototype), and uses no IBOutlets or IBActions... just add a UITableViewController and assign it to 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)
}
}