2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Swift】チャットアプリでよくみるキーボードの動きに追随するTextFieldとButtonの実装

Last updated at Posted at 2021-01-22

チャットアプリでよくみるキーボードの動きに追随するTextFieldとButtoonを実装してみました。

開発環境

Xcode 12.3
Swift 5

デモ

こんなやつです。

イメージ.GIF

コード

InputAccesoryViewController.swift
import UIKit

class InputAccesoryViewController: UIViewController {
    
    let outputTableView = UITableView()
    
    //プロパティにアクセスできるようにグローバル定数として定義
    let inputTextField: UITextField = {
        let textField = UITextField()
        textField.placeholder = "Aa"
        textField.backgroundColor = .systemGray6
        textField.borderStyle = .roundedRect
        return textField
    }()

    override var canBecomeFirstResponder: Bool { return true }

    var inputContainerView: UIView!
    
    //inputAccesoryViewの中身を作って返り値に入れます。
    override var inputAccessoryView: UIView? {

        if inputContainerView == nil {

            //容器となるContainerViewを作成
            inputContainerView = InputContainerView()
            inputContainerView.backgroundColor = .systemYellow
            
            //ContainerViewに定義しておいたTextFieldを追加
            inputContainerView.addSubview(inputTextField)

            //ContainerViewの高さがキーボードを閉じた時と開いた時で自動でリサイズする
            inputContainerView.autoresizingMask = .flexibleHeight
            
            //送信ボタンの追加
            let sendButton = UIButton(type: .system)
            sendButton.setImage(UIImage(systemName: "paperplane"), for: .normal)
            
            //Button Actionを追加
            sendButton.addTarget(self, action: #selector(didTapSend), for: .touchUpInside)
            
            //ContainerViewにSendButtonを追加
            inputContainerView.addSubview(sendButton)
            
            //それぞれの部品に制約を設定します
            sendButton.anchor(
                right: inputContainerView.trailingAnchor,
                paddingRight: 8,
                width: 50,
                height: 50)
            
            inputTextField.anchor(
                top: inputContainerView.topAnchor,
                left: inputContainerView.leadingAnchor,
                bottom: inputContainerView.layoutMarginsGuide.bottomAnchor,
                right: sendButton.leadingAnchor,
                paddingTop: 8,
                paddingLeft: 8,
                paddingBottom: 8,
                paddingRight: 8)
        }
        //作成したContainerViewを返り値に入れる
        return inputContainerView
    }
    
    var messages = [String()]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        inputTextField.delegate = self
        outputTableView.dataSource = self
        
        outputTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        
        //これでTableViewのスクロールに合わせてキーボードが閉じれるようになる
        outputTableView.keyboardDismissMode = .interactive
        view = outputTableView
    }
    
    @objc func didTapSend() {
        
        if let message = inputTextField.text, inputTextField.text != "" {
            messages.append(message)
            inputTextField.text = ""
            outputTableView.reloadData()
        }
    }
}

extension InputAccesoryViewController: UITextFieldDelegate {
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        didTapSend()
        textField.resignFirstResponder()
        return true
    }
}

extension InputAccesoryViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return messages.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "cell")
        cell.textLabel?.text = messages[indexPath.row]
        return cell
    }
}

InputContainerView.swift

InputContainerView.swift
import UIKit

class InputContainerView: UIView {
    
    //これは、inputAccesoryViewが自動レイアウト制約から適切なサイズになるために必要です
    override var intrinsicContentSize: CGSize {
        return CGSize.zero
    }
}

UIView Extension

制約を容易にする為にメソッドを作成しました。

UIView+.swift
import UIKit

extension UIView {
    
    func anchor(top: NSLayoutYAxisAnchor? = nil,
                left: NSLayoutXAxisAnchor? = nil,
                bottom: NSLayoutYAxisAnchor? = nil,
                right: NSLayoutXAxisAnchor? = nil,
                paddingTop: CGFloat = 0,
                paddingLeft: CGFloat = 0,
                paddingBottom: CGFloat = 0,
                paddingRight: CGFloat = 0,
                width: CGFloat? = nil,
                height: CGFloat? = nil) {
        
        translatesAutoresizingMaskIntoConstraints = false
        
        if let top = top {
            topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
        }
        
        if let left = left {
            leadingAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
        }
        
        if let bottom = bottom {
            bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
        }
        
        if let right = right {
            trailingAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
        }
        
        if let width = width {
            widthAnchor.constraint(equalToConstant: width).isActive = true
        }
        
        if let height = height {
            heightAnchor.constraint(equalToConstant: height).isActive = true
        }
    }
}

苦戦したところ

SafeAreaの部分に作成したContainerViewが被って表示され、意図した結果がなかなか出なかった。

textFeild.bottomAnchor
inputContainerView.layoutMarginsGuide.bottomAnchorに指定するのがミソでした。

参考

こちらの記事を参考にし、解決することが出来ました。
iPhone X how to handle View Controller inputAccessoryView


何か間違いやより良い方法がありましたら優しく教えていただけると幸いです🙇‍♂️
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?