Search code examples
iosswiftuiresponderbecomefirstresponderinputaccessoryview

how canBecomeFirstResponder bring custom inputAccessoryView


I'm trying to make custom inputAccessoryView contain UITextField and docking it bottom like chat application

but if i didn't use canBecomeFirstResponder = true inputAccessoryView are hidden(?)

this is my code

class MainVC: UITableViewController {
  lazy var inputTextFieldContainer: UIView = {
    // custom inputAccessoryView
  }()

  override func viewDidLoad(){
    super.viewDidLoad()
  }

  override var inputAccessoryView: UIView? {
      get{
          return containerView
      }
  }

  override var canBecomeFirstResponder: Bool {
      get {
          return true
      }
  }
}

how canBecomeFirstResponder bring custom inputAccessoryView

i read about UIResponder and responder chain on Apple docs

but i couldn't match that concept to this issue.

They said UIResponder will handle the events and i make my MainVC become first responder by canBecomeFirstResponder = true

and inputAccessoryView are shown

but what is the exact event in this case and situation


Solution

  • Since your code inherits from UITableViewController here a complete example with it:

    Start with defining a accessory view. Since you mentioned as an example a chat app it could be a textfield and a send button:

    class SendMessageView: UIView {
    
        private let textField = UITextField()
        private let sendButton = UIButton()
    
        init() {
            super.init(frame: CGRect.zero)
            self.setup()
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) not implemented")
        }
    
        private func setup() {
            sendButton.setTitle("Send", for: UIControlState.normal)
            sendButton.setTitleColor(UIColor.blue, for: UIControlState.normal)
            self.addSubview(textField)
            self.addSubview(sendButton)
    
            self.backgroundColor = UIColor.groupTableViewBackground
            textField.backgroundColor = UIColor.white
    
            self.autoresizingMask = .flexibleHeight
            textField.translatesAutoresizingMaskIntoConstraints = false
            sendButton.translatesAutoresizingMaskIntoConstraints = false
    
            textField.topAnchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true
            textField.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
    
            sendButton.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 8).isActive = true
            sendButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
            sendButton.centerYAnchor.constraint(equalTo: textField.centerYAnchor).isActive = true
        }
    
        override var intrinsicContentSize: CGSize {
            let contentHeight = self.textField.intrinsicContentSize.height + 16
            return CGSize(width: UIScreen.main.bounds.width, height: contentHeight)
        }
    }
    

    Next you need a custom UITableView which uses our accessory view:

    class CustomTableView: UITableView {
    
        private let sendMessageView = SendMessageView()
    
        override var canBecomeFirstResponder: Bool {
            return true
        }
    
        override var inputAccessoryView: UIView? {
            return self.sendMessageView
        }
    }
    

    Finally one could define a TableViewController using this custom table view:

    class TableViewController: UITableViewController {
    
        override func loadView() {
            self.tableView = CustomTableView()
            self.view = self.tableView
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.tableView.becomeFirstResponder()
        }
    
        ...
    

    The result would look like this:

    [input accessory view[1]