Swift — настраиваемый вид ввода текста

Я пытаюсь реализовать собственное представление ввода текста, подобное тому, что используется в большинстве приложений для обмена сообщениями, как показано ниже:

Swift — настраиваемый вид ввода текста

Там, где весь вид сначала отображается внизу экрана, а затем над клавиатурой при выборе, размер текстового поля изменяется в зависимости от содержимого и включает кнопку для загрузки текста.

Я предполагаю, что мне нужно создать собственный UIView, содержащий все эти элементы, но я не уверен, как изменить размер текстового поля и переместить представление над клавиатурой при нажатии.

Может ли кто-нибудь указать мне в правильном направлении

Если вы не исправили всплывающее окно с клавиатурой, проверьте мой обновленный ответ.

elbert rivas 09.04.2019 12:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
2 188
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Взгляните на 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
    }

}

Просто как тот :)

Я попробовал это, но не мог найти лучший способ реализовать это.

Khledon 09.04.2019 12:20

Если вы хотите сделать это программно самостоятельно, вы можете попробовать это.

Пользовательский вид ввода текста, который будет содержать ввод текста и кнопку отправки

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
}
}

Другие вопросы по теме