Swift-自定义文本输入视图

时间:2019-04-09 09:51:09

标签: swift

我正在尝试实现自定义文本输入视图,类似于以下大多数消息传递应用程序中使用的视图:

enter image description here

在整个视图最初显示在屏幕底部然后在键盘上方的位置处,选中时,文本框将根据内容调整大小,并包含一个用于上载文本的按钮。

我假设我需要创建一个包含所有这些元素的自定义UIView,但是不确定如何更改文本框大小以及如何在按下时将视图移动到键盘上方。

有人可以指出我正确的方向

2 个答案:

答案 0 :(得分:3)

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

}

就这么简单:)

答案 1 :(得分:2)

如果您想自己以编程方式进行操作,可以尝试一下。

自定义文本输入视图,其中将包含文本输入和发送按钮

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