Swift - пользовательский вид ввода текста - PullRequest
1 голос
/ 09 апреля 2019

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

enter image description here

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

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

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

Ответы [ 2 ]

3 голосов
/ 09 апреля 2019

Посмотрите на 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)
    }

}

, который будет выглядеть так:

enter image description here

Со всеми необходимыми вам функциями, плюс еще.

Я немного отредактировал код из примера проекта, чтобы он выглядел точно так, как вы добавили в вопросе.

А ты 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
    }

}

Просто так:)

2 голосов
/ 09 апреля 2019

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

Настраиваемое представление текста, которое будет содержать ввод текста и кнопку отправки

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
}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...