UIKit学习笔记6-调用键盘、配置聊天输入栏
创建UIView和它的初始化器,添加要用的组件
UITextField表示这是一个输入框
lazy var chattextField: UITextField = { var chattextField = UITextField(frame: .init(x: 16 + 30 + 8, y: 4, width: SCREENWIDTH - (16 + 30 + 8) * 2, height: 40)) chattextField.backgroundColor = .white chattextField.layer.cornerRadius = 4 chattextField.layer.masksToBounds = true return chattextField }()class ChatInputView: UIView { override init(frame: CGRect) { super.init(frame: frame) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }创建UIButton,并向量化按钮图片,再设置成黑色。
向量化图片,并将其设置为Button图片
withRenderingMode(*:):这是 UIImage 的方法,用于指定图像的渲染模式。 参数:*.alwaysTemplate是一个渲染模式枚举值,表示图像应该始终使用模板模式渲染
let img = UIImage(systemName: "waveform.circle")?.withRenderingMode(.alwaysTemplate) soudImgView.setImage(img, for: .normal)设置向量化后的image主题颜色
soudImgView.tintColor = .black设置UIButton的位置
lazy var plusButton: UIButton = { var plusButton = UIButton(frame: .init(x: SCREENWIDTH - (16 + 30), y: 7, width: 30, height: 30)) let img = UIImage(systemName: "plus.circle")? .withRenderingMode(.alwaysTemplate) plusButton.backgroundColor = .clear plusButton.setImage(img, for: .normal) plusButton.tintColor = .black return plusButton }()上面20和下面20都是不能用的,得隔开。
64-20 = 44(可用部分)
44-22 = 22(减去图片本来的大小)
再除以2,就是11(这里我的按钮大小是30,所以算出来是7)
在当前页面创建该组件并添加到当前页面
lazy var chatInputView: ChatInputView = { let chatInputView = ChatInputView(frame: .init(x: 0, y: SCREENHEIGHT - 64, width: SCREENWIDTH, height: 64)) chatInputView.backgroundColor = .lightGray chatInputView.chattextField.delegate = self return chatInputView }()self.view.addSubview(chatInputView)效果如图
使用系统通知字段监控键盘的展开和收起,以达成控制输入框显示的目的
NotificationCenter
系统通知字段有非常多,需要的时候查询字段表即可。不同的字段表表示执行不同的操作的时候会发通知,发通知之后,执行某某操作(函数)。
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)其中NotificationCenter表示系统通知字段
default表示提供了一个全局的、共享的通知。
addObserver其实就和KVO一样
self指定某个页面,self就是当前页面
selector: #selector(keyboardWillShow))其中keyboardWillShow是在self页面中的函数名,就是要执行这个函数的意思
name: UIResponder.keyboardWillShowNotification就是这个NotificationCenter在什么情形下执行的意思,有很多字段,keyboardWillShowNotification就是键盘展开的时候发通知。
所以现在我们有一个函数keyboardWillShow要编写,这里先不写。
使用同样的方式书写键盘收起通知
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil现在我们有2个函数keyboardWillShow和keyboardWillHide要编写。
keyboardWillShow重新设置滚动视图与输入框
要实现的功能是让输入框随着键盘调用被顶到上方,所以这个函数要【计算键盘的高度】并实现重新设置**chatInputView(输入框)和chat_scrollView(滚动部分)位置的效果。**
添加@objc让这个通知可以被OC代码调用。
这个函数不用记下来,要用的时候查一下就行了。
@objc func keyboardWillShow(notification: NSNotification) { if let userInfo = notification.userInfo, let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { let keyboradHeight = keyboardFrame.cgRectValue.height } }得到了键盘高度之后,重新设置chat_scrollView和chatInputView的位置和尺寸
要为键盘的范围留出位置,所以我们在滚动视图原来的y(64)的基础上,往上顶一个键盘的高度。尺寸不变。
self.chat_scrollView.frame = .init(origin: .init(x: 0, y: 64 - keyboradHeight), size: self.chat_scrollView.frame.size)在调用输入框的时候,重新设置输入框
输入框的位置,同样也是要在原来的y的基础上,往上顶一个键盘的高度。尺寸照样不变
self.chat_scrollView.frame = .init(origin: .init(x: 0, y: 64 - keyboradHeight), size: self.chat_scrollView.frame.size)为这个重设置的过程添加一个动画,使得效果更平滑。
使用UIView.animate(withDuration: ){}
withDuration:后面加的是时间,单位是秒
UIView.animate(withDuration: 0.5) { self.chatInputView.frame = .init(origin: .init(x: 0, y: SCREENHEIGHT - 64 - keyboardHeight), size: self.chatInputView.frame.size) self.chat_scrollView.frame = .init(origin: .init(x: 0, y: 64 - keyboardHeight), size: self.chat_scrollView.frame.size) }keyboardWillHide在输入框被隐藏的时候,回到原来的状态
@objc func keyboardWillHide(notification: NSNotification) { UIView.animate(withDuration: 0.5){ self.chatInputView.frame = .init(origin: .init(x: 0, y: SCREENWIDTH - 64), size: self.chatInputView.frame.size) self.chat_scrollView.frame = .init(origin: .init(x: 0, y: 64), size: self.chat_scrollView.frame.size) } }通过点击键盘回车来实现收起键盘的效果
如果要用到输入框UITextField,最好让该ViewController继承**UITextFieldDelegate**
将对应TextField的delegate设置为self
lazy var chatInputView: ChatInputView = { let chatInputView = ChatInputView(frame: .init(x: 0, y: SCREENHEIGHT - 64, width: SCREENWIDTH, height: 64)) chatInputView.backgroundColor = .lightGray chatInputView.chattextField.delegate = self return chatInputView }()输入return添加一个函数,叫textFieldShouldReturn,它返回的是布尔值,表示【按下回车的时候】是否要处理该事件。
func textFieldShouldReturn(_ textField: UITextField) -> Bool { return true }Bool的闭包里存放的就是要处理的事件
self.chatInputView.chattextField.resignFirstResponder()调用resignFirstResponder()方法表示要取消当前文本框的第一响应者状态,取消了之后,文本框失去焦点,键盘就会被收回。
func textFieldShouldReturn(_ textField: UITextField) -> Bool { self.chatInputView.chattextField.resignFirstResponder() return true }通过回车设置信息发送,并将其添加到数组中
烧烤一下,存放聊天信息的数组中都有什么元素?
有这些
var chatID: String var messageID: String var content: String var target: String var mineHead: String var otherHead: String思路:
- 我们需要得到的是新的
messageID,因为它是随着发送的条数递增的 - 还需要得到的是新的
content,因为它的内容是随着输入变化的 - 然后要把新的数组成员添加进Model内,并使用**
reloadData**刷新滚动视图(tableview的方法)。 - 接着要使用**
scrollToRow**将消息栏滚动到最新一行,这样能看到自己刚刚发出去的消息。 - 最后再在信息发送完毕之后清空信息栏。
returnKeyType用于控制键盘上的返回键类型,在输入框中将返回键设置为.send,就会显示为.send
chatInputView.chattextField.returnKeyType = .send由于发送信息就是按回车,所以,继续在Return…函数里操作
通过实例化的操作,来初始化下一条信息。
定义下一条信息,表示它是ChatModel数组中的某个成员,用于设置下一条信息的头像等
let model = ChatModel[0]通过 插值,把数值变成字符串。成功解决
let messageID = "\\(self.chatModels.count + 1)" //搞定了messageID然后设置新的Model信息
let new_model = ChatModel.init(chatID: self.messCellmodel.chatID, messageID: messageID, content: textField.text ?? "", target: "mine", mineHead: model.mineHead, otherHead: model.otherHead)chatID: self.messCellmodel.chatID:用点击传进来的chatID来定义chatID
messageID: messageID
content: textField.text ?? ""就是用户输入的文本
把新的数组成员添加进Model内,并使用**reloadData**刷新滚动视图(tableview的方法)。
**chatModels.append(new_model)**刷新视图
self.chat_scrollView.reloadData().scrollToRow(at:at:animated:)用于滚动到指定行
row: chatModels.count - 1表示要滚动到的目标行是chatModels数组中的最后一条消息(因为数组索引是从0开始的,故使用count - 1
使用.bottom表示将最后一行放置在聊天视图的底部
为了避免输入空字符串,所以写一个if else来避免。
func textFieldShouldReturn(_ textField: UITextField) -> Bool { if textField.text == "" || textField.text == " " { return false } else { let messageID = "\\(self.chatModels.count + 1)" let model = chatModels[0] let newmodel = ChatModel(chatID: self.messCellmodel.chatID, messageID: messageID, content: chatInputView.chattextField.text ?? "", target: "mine", mineHead: model.mineHead, otherHead: model.otherHead) chatModels.append(newmodel) chat_scrollView.reloadData() chat_scrollView.scrollToRow(at: .init(row: chatModels.count - 1, section: 0), at: .bottom, animated: true) textField.text = "" return true } }