はじめに
初めまして!
関西を拠点に活動している学生団体watnowの、アドベントカレンダー最終日を担当させていただきますアントニーと申します!
今回はチャット機能実装などの際に必要不可欠となる、メッセージ入力画面/機能を実装する簡単な方法を紹介させていただきます。
InputAccessoryViewとは
UITextFieldやUITextViewを使用する際に出てくるキーボードにViewをアタッチするために使用します。
簡単に言うと、チャットで文章を送信するときの機能を簡単に作成できるんだなって思ってくれたら良いと思います。
今回実装したいこと
こちらが今回の完成形です。
文字を入力し、送信ボタンを押すとLabelに入力した文字が表示されます。
実装
まずはじめに、Xibファイルを作成します。
ViewのsizeはFreeformにしておきましょう。
上記で作成したXibファイルに紐付けるswiftファイルを作成します。
画面作成
以下のようにUITextViewとUIButtonを配置します。
※オートレイアウトの設定を忘れないでください。
コード説明
ここから、コードの説明をしていきます。
全体のコードは最後に記載してますので、お急ぎの方はそちらをご参照ください。
まず、作成したswiftファイル(ここでは、"IndicateAccessoryView.swift"と名付けました。)に、以下のようにクラスを定義します。
import UIKit
class IndicateAccessoryView: UIView {
@IBOutlet var inputTextView: UITextView!
@IBOutlet var sendButton: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
TextViewの高さを可変にする
override init(frame: CGRect) {
super.init(frame: frame)
autoresizingMask = .flexibleHeight
}
override var intrinsicContentSize: CGSize {
return .zero
}
Delegateを作成する
protocol IndicateAccessoryViewDelegate: class {
func tappedSendButton(text: String)
}
XibファイルをIndidateInputAccessoryViewに表示する
以下のコードでXibファイルをViewに表示する処理を行なっています。
private func nibInit() {
let nib = UINib(nibName: "IndicateAccessoryView", bundle: nil)
guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return }
view.frame = self.bounds
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.addSubview(view)
}
IndicateAccessoryViewの全文
import UIKit
protocol IndicateAccessoryViewDelegate: class {
func tappedSendButton(text: String)
}
class IndicateAccessoryView: UIView {
@IBOutlet var inputTextView: UITextView!
@IBOutlet var sendButton: UIButton!
var delegate: IndicateAccessoryViewDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
nibInit()
autoresizingMask = .flexibleHeight
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@IBAction func tappedSendButton() {
guard let text = inputTextView.text else { return }
delegate?.tappedSendButton(text: text) //textViewの情報を渡す準備
}
//chatTextViewの高さを可変にする(initでautoresizingMaskの設定必要)
override var intrinsicContentSize: CGSize {
return .zero
}
//XibファイルをChatInputAccessoryViewに表示するためのメソッド
private func nibInit() {
let nib = UINib(nibName: "IndicateAccessoryView", bundle: nil)
guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return }
view.frame = self.bounds
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.addSubview(view)
}
}
紐付け
Xibファイルとswiftファイルの紐付けですが、Viewではなく、PlaceholdersのFile's Ownerに紐づけるように注意してください。
MainStoryboardの設定
以下のように、送信したボタンを表示するラベルを配置しました。
ViewController.swiftのコード説明
InputAccessoryViewの配置
以下のコードで配置します。
IndicateAccessoryView.swiftで作成しているので、その設定も行います。
private lazy var indicateAccessoryView: IndicateAccessoryView = {
let view = IndicateAccessoryView()
view.frame = .init(x: 0, y: 0, width: view.frame.width, height: 150)
view.delegate = self
return view
}()
override var inputAccessoryView: UIView? {
get {
return indicateAccessoryView
}
}
override var canBecomeFirstResponder: Bool {
return true
}
送信ボタンがタップされた際、入力された文字を作成したラベルに表示するようにします。
extension ViewController: IndicateAccessoryViewDelegate {
func tappedSendButton(text: String) {
sendLabel.text = text
}
}
コード全文
import UIKit
class ViewController: UIViewController {
@IBOutlet var sendLabel: UILabel!
private lazy var indicateAccessoryView: IndicateAccessoryView = {
let view = IndicateAccessoryView()
view.frame = .init(x: 0, y: 0, width: view.frame.width, height: 150)
view.delegate = self
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
}
override var inputAccessoryView: UIView? {
get {
return indicateAccessoryView
}
}
override var canBecomeFirstResponder: Bool {
return true
}
}
extension ViewController: IndicateAccessoryViewDelegate {
func tappedSendButton(text: String) {
sendLabel.text = text
}
}
終わりに
いかがでしたでしょうか。
文字入力する際に、キーボードも出てきてくれるし、消える時も一緒に消えてくれるし、すごく便利ではないでしょうか。
今回の実装では簡単化のために、cornerRadiusや送信後にtextViewのクリアなどは行いませんでしたが、これでかなり本格的なチャット機能を実装できるのではないでしょうか。