Я пытаюсь реализовать собственное представление ввода текста, подобное тому, что используется в большинстве приложений для обмена сообщениями, как показано ниже:
Там, где весь вид сначала отображается внизу экрана, а затем над клавиатурой при выборе, размер текстового поля изменяется в зависимости от содержимого и включает кнопку для загрузки текста.
Я предполагаю, что мне нужно создать собственный UIView, содержащий все эти элементы, но я не уверен, как изменить размер текстового поля и переместить представление над клавиатурой при нажатии.
Может ли кто-нибудь указать мне в правильном направлении
Взгляните на MessageInputBar
https://github.com/MessageKit/MessageInputBar
Это облегчит вам задачу и не позволит вам заново изобретать колесо, плюс его широкие возможности настройки, вы можете запустить пример, чтобы увидеть, как он работает.
Редактировать
Просто чтобы дать вам представление
import UIKit
import MessageInputBar
class CustomInputBar: MessageInputBar {
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure() {
backgroundView.backgroundColor = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
let button = InputBarButtonItem()
button.setSize(CGSize(width: 36, height: 36), animated: false)
button.setImage(#imageLiteral(resourceName: "ic_up").withRenderingMode(.alwaysTemplate), for: .normal)
button.imageView?.contentMode = .scaleAspectFit
button.tintColor = UIColor(red: 0, green: 122/255, blue: 1, alpha: 1)
inputTextView.backgroundColor = .white
inputTextView.placeholderTextColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
inputTextView.textContainerInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
inputTextView.placeholderLabelInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
inputTextView.layer.borderColor = UIColor(red: 200/255, green: 200/255, blue: 200/255, alpha: 1).cgColor
inputTextView.layer.borderWidth = 1.0
inputTextView.layer.cornerRadius = 4.0
inputTextView.layer.masksToBounds = true
inputTextView.scrollIndicatorInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
setLeftStackViewWidthConstant(to: 36, animated: false)
setStackViewItems([button], forStack: .left, animated: false)
sendButton.setSize(CGSize(width: 52, height: 36), animated: false)
}
}
который будет выглядеть так:
Со всеми функциями, которые вы хотели, и даже больше.
Я немного отредактировал код из примера проекта, чтобы он выглядел именно так, как вы добавили в вопросе.
А ты ViewController
будешь просто
import UIKit
import MessageInputBar
final class ExampleViewController: UITableViewController {
// MARK: - Properties
override var inputAccessoryView: UIView? {
return messageInputBar
}
override var canBecomeFirstResponder: Bool {
return true
}
// MARK: - MessageInputBar
private let messageInputBar = CustomInputBar()
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
tableView.keyboardDismissMode = .interactive
messageInputBar.delegate = self
}
}
А чтобы послушать MessageInputBarDelegate
просто добавьте
extension ExampleViewController: MessageInputBarDelegate {
func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {
// Use to send the message
messageInputBar.inputTextView.text = String()
messageInputBar.invalidatePlugins()
}
func messageInputBar(_ inputBar: MessageInputBar, textViewTextDidChangeTo text: String) {
// Use to send a typing indicator
}
func messageInputBar(_ inputBar: MessageInputBar, didChangeIntrinsicContentTo size: CGSize) {
// Use to change any other subview insets
}
}
Просто как тот :)
Я попробовал это, но не мог найти лучший способ реализовать это.
Если вы хотите сделать это программно самостоятельно, вы можете попробовать это.
Пользовательский вид ввода текста, который будет содержать ввод текста и кнопку отправки
import UIKit
class TextEntryView: UIView {
let tvMessage: UITextView = {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.textColor = Constants.charlie
textView.font = UIFont.systemFont(ofSize: 17)
textView.isScrollEnabled = false
return textView
}()
let btnSend: UIButton = {
let image: UIImage = UIImage(named: "send_icon")!
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(image, for: .normal)
button.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .white
self.addBorders(edges: .top, color: UIColor(red: 220/250, green: 220/250, blue: 220/250, alpha: 1))
setupLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override class var requiresConstraintBasedLayout: Bool {
return true
}
private func setupLayout() {
self.addSubview(tvMessage)
self.addSubview(btnSend)
tvMessage.topAnchor.constraint(equalTo: self.topAnchor, constant: 6).isActive = true
tvMessage.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 12).isActive = true
tvMessage.trailingAnchor.constraint(equalTo: btnSend.leadingAnchor, constant: -12).isActive = true
tvMessage.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -6).isActive = true
btnSend.topAnchor.constraint(equalTo: self.topAnchor, constant: 6).isActive = true
btnSend.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -12).isActive = true
btnSend.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -6).isActive = true
btnSend.widthAnchor.constraint(equalToConstant: 40).isActive = true
}
}
Добавить пользовательский вид в контроллере
import UIKit
class ChatController: UIViewController, UITextViewDelegate {
let textEntry = TextEntryView()
var bottomConstraint: NSLayoutConstraint?
var textEntryHeightConstraint: NSLayoutConstraint?
override func viewWillAppear(_ animated: Bool) {
initViews()
setupLayout()
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardNotification(notification:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))
tapGesture.cancelsTouchesInView = true
tableView.addGestureRecognizer(tapGesture)
}
@objc func hideKeyboard() {
self.endEditing(true)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func keyboardNotification(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let endFrameY = keyboardFrame?.origin.y ?? 0
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
if endFrameY >= UIScreen.main.bounds.size.height {
bottomConstraint?.constant = 0
} else {
bottomConstraint?.constant = -(keyboardFrame?.size.height)!
}
UIView.animate(withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: { self.layoutIfNeeded() },
completion: nil)
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (textEntry.tvMessage.contentSize.height + 12 < (textEntryHeightConstraint?.constant)!) {
self.textEntry.tvMessage.isScrollEnabled = false
} else {
self.textEntry.tvMessage.isScrollEnabled = true
}
return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
if textEntry.tvMessage.textColor == .lightGray {
textEntry.tvMessage.text = nil
textEntry.tvMessage.textColor = Constants.tertiaryColor
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if (textEntry.tvMessage.text?.isEmpty)! {
textEntry.tvMessage.text = "Write a message"
textEntry.tvMessage.textColor = .lightGray
}
}
}
extension MessageView {
func initViews() {
if #available(iOS 11.0, *) {
bottomConstraint = NSLayoutConstraint(item: textEntry, attribute: .bottom, relatedBy: .equal, toItem: self.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0)
} else {
// Fallback on earlier versions
bottomConstraint = NSLayoutConstraint(item: textEntry, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
}
textEntry.translatesAutoresizingMaskIntoConstraints = false
textEntry.tvMessage.text = "Write a message"
textEntry.tvMessage.textColor = .lightGray
textEntry.tvMessage.delegate = self
}
func setupLayout() {
self.addSubview(textEntry)
textEntry.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
textEntry.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.addConstraint(bottomConstraint!)
textEntryHeightConstraint = textEntry.heightAnchor.constraint(lessThanOrEqualToConstant: 150)
textEntryHeightConstraint?.isActive = true
}
}
Если вы не исправили всплывающее окно с клавиатурой, проверьте мой обновленный ответ.