Swift LINEぽいメッセージアプリの入力UIをつくったのでメモ
はじめに
かなり前にこちらでメッセージを表示する部分の吹き出しの作り方をメモした。
http://kimagureneet.hatenablog.com/entry/2015/09/19/005407
今回はメッセージ入力部分のUIの作り込みの方法をメモ。こんな感じのものを作りたかった。
やりたいことは
・入力フォームを画面下に固定
・フォーカスを当てたらキーボードの上に入力フォームを移動
・複数行にも対応したいのでUITextFieldでなくUITextViewを使用
上記を満たすためにやらなくてはならないこと(上と重複するけど)
・キーボードを開いたとき
入力フォームの位置をキーボードの上に移動
UITextViewの高さを複数行見えるように変更
・キーボードを閉じたとき
入力フォームの位置を画面下に移動
UITextViewの高さを1行見えるように変更
ざっくりこんな感じ。あとはキーボードの上に完了ボタンを追加したり細々とした内容。
実装
import UIKit class ViewController: UIViewController, UITextViewDelegate { let MARGIN_MSG: CGFloat = 1 let HEIGHT_BOX: CGFloat = 50 let WIDTH_SUBMIT: CGFloat = 70 let MIN_HEIGHT_MSG: CGFloat = 50 let MAX_HEIGHT_MSG: CGFloat = 100 // メッセージを表示する領域 var tableView: UITableView! // メッセージBOX(入力フォームと送信ボタン) var box: UIView! var msg: UITextView! var submit: UIButton! // テーブルビューのサイズ情報 var sizeTable: CGSize! // メッセージBOXのフレーム情報(キーボードが閉じられた時の情報) var frBox: CGRect! // キーボードのフレーム情報 var frKeyboard: CGRect! override func viewDidLoad() { super.viewDidLoad() setView() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(ViewController.handleKeyboardWillShowNotification(_:)), name: .UIKeyboardWillShow, object: nil) notificationCenter.addObserver(self, selector: #selector(ViewController.handleKeyboardWillHideNotification(_:)), name: .UIKeyboardWillHide, object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: self.view.window) NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidHide, object: self.view.window) } // キーボードが表示されるときに行う処理 @objc func handleKeyboardWillShowNotification(_ notification: Notification) { let userInfo = notification.userInfo! frKeyboard = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue drawBox() } // キーボードが非表示になるときに行う処理 @objc func handleKeyboardWillHideNotification(_ notification: Notification) { box.frame = frBox tableView.frame.size = sizeTable } // メッセージBOX描画処理 func drawBox() { let statusBarHeight = UIApplication.shared.statusBarFrame.height let bound: CGSize = UIScreen.main.bounds.size var height = msg.sizeThatFits(CGSize(width: msg.frame.size.width, height: CGFloat.greatestFiniteMagnitude)).height if height < MIN_HEIGHT_MSG { height = MIN_HEIGHT_MSG } else if MAX_HEIGHT_MSG < height { height = MAX_HEIGHT_MSG } // 入力フォームの位置調整 msg.frame = CGRect(x: msg.frame.origin.x, y: msg.frame.origin.y, width: msg.frame.width, height: height) box.frame = CGRect(x: box.frame.origin.x, y: bound.height - frKeyboard.size.height - box.frame.height, width: msg.frame.width, height: height) // テーブルビューの高さ調整 tableView.frame.size = CGSize(width: tableView.frame.width, height: bound.height - (statusBarHeight + frKeyboard.height + height)) } func textViewDidChange(_ textView: UITextView) { drawBox() } func setView() { let statusBarHeight = UIApplication.shared.statusBarFrame.height let widthMax = view.frame.width let heightMax = view.frame.height // メッセージを表示する領域 tableView = UITableView(frame: CGRect(x:0, y:statusBarHeight, width:widthMax, height:heightMax - (statusBarHeight + HEIGHT_BOX))) tableView.separatorStyle = .none view.addSubview(tableView) // 入力フォームと送信ボタンを表示する領域 box = UIView(frame: CGRect(x: 0, y: tableView.frame.origin.y + tableView.frame.height, width: widthMax, height: HEIGHT_BOX)) view.addSubview(box) // 送信ボタン submit = UIButton(frame: CGRect(x: widthMax-(WIDTH_SUBMIT+MARGIN_MSG), y: MARGIN_MSG, width: WIDTH_SUBMIT, height: HEIGHT_BOX-MARGIN_MSG)) submit.setTitle("送信", for: UIControlState.normal) submit.setTitleColor(UIColor.white, for: UIControlState.normal) submit.backgroundColor = UIColor.black submit.layer.cornerRadius = 2.0 submit.layer.borderColor = UIColor.lightGray.cgColor submit.layer.borderWidth = 3 box.addSubview(submit) // 入力フォーム msg = UITextView(frame: CGRect(x: MARGIN_MSG, y: MARGIN_MSG, width: widthMax-(MARGIN_MSG*3+WIDTH_SUBMIT), height: HEIGHT_BOX-MARGIN_MSG)) msg.font = UIFont.systemFont(ofSize: 18) msg.layer.borderColor = UIColor.lightGray.cgColor msg.layer.borderWidth = 3 msg.delegate = self box.addSubview(msg) // キーボード完了ボタン let keyboard = UIView(frame: CGRect(x: 0, y: 0, width: widthMax, height: 40)) keyboard.backgroundColor = UIColor.lightGray let button = UIButton(frame: CGRect(x: widthMax - 50, y: 5, width: 50, height: 30)) button.setTitle("完了", for: .normal) button.layer.cornerRadius = 2.0 keyboard.addSubview(button) msg.inputAccessoryView = keyboard button.addTarget(self, action: #selector(onClose(sender:)), for: .touchUpInside) // メッセージBOXの位置を保持しておく frBox = box.frame // テーブルビューのサイズを保持しておく sizeTable = tableView.frame.size } // 完了ボタンでキーボードを閉じる @objc func onClose(sender: UIBarButtonItem){ msg.endEditing(true) } }
ずらずら書いたけど以上です。ちなみにSwift4で確認した。