iOS
UITextField
Swift

チャットアプリでよくある画面下部のテキストフィールドを実現する

実現するもの

LINEのチャット画面のように、画面下部に設置されたテキストフィールドをタップすると、
出てきたキーボードの上部にスライドするようにする。

コツ

通常はUITextFieldへinputAccessoryViewを設定しますが、その場合だとViewController上のビューにもともとおいてあるUITextFieldをタップして、それとは別のUITextFieldが含まれるUIViewがキーボード上部に表示されるという感じになりそうです。
そのためここではUIResponderのinputAccessoryView: UIView?をオーバーライドすることにより同一インスタンスを返すようにしています。

 注意事項

self.bottomView = ChatRoomInputView(frame:
                       CGRect(x: 0, y: self.view.frame.height-50, width: self.view.frame.width, height: 50))
self.bottomView.chatTextField.inputAccessoryView =
                       ChatRoomInputView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50))
self.view.addSubview(bottomView)

としても同じような画面を表示することができますが、インスタンスが違うため2つのTextFieldで入力した内容が一致しません。

コード

ChatRoomViewController.swift
class ChatRoomViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    var bottomView: ChatRoomInputView!

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

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //画面下部とキーボード上部に同じビューを返す
    override var inputAccessoryView: UIView? {
        return bottomView
    }

    // trueを返すことで override var inputAccessoryView: UIView? が呼ばれるっぽい
    override var canBecomeFirstResponder: Bool {
        return true
    }
}

extension ChatRoomViewController {
    func setupUI() {
        tableView.keyboardDismissMode = .interactive //TableViewを下方向にスワイプした時のみキーボードを閉じる

        // frameを指定しないとだめ
        self.bottomView = ChatRoomInputView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50))

        //self.view.addSubview(bottomView) //やったらだめ!!

    }
}

ChatRoomInputView.swift
import UIKit

class ChatRoomInputView: UIView {

    @IBOutlet weak var chatTextField: UITextField!
    @IBOutlet weak var sendButton: UIButton!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setFromXib()
    }

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

    func setFromXib() {
        let nib = UINib.init(nibName: "ChatRoomInputView", bundle: nil)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
        self.addSubview(view)
    }
}

参考にさせていただいたサイト

https://qiita.com/ykpaco_404wm/items/659b0070bb9dbfdf1373
https://stackoverflow.com/questions/19764293/inputaccessoryview-docked-at-bottom