218 lines
7.2 KiB
Swift
218 lines
7.2 KiB
Swift
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
import UIKit
|
|
import SnapKit
|
|
|
|
private struct Constants {
|
|
static let TopBorderHeight = 0.5
|
|
static let Offset: CGFloat = 5.0
|
|
static let CameraHorizontalOffset: CGFloat = 10.0
|
|
static let CameraBottomOffset: CGFloat = -10.0
|
|
static let TextViewMinHeight: CGFloat = 35.0
|
|
static let MIN_MYHEIGHT: CGFloat = 45
|
|
static let MAX_MYHEIGHT: CGFloat = 90
|
|
static let MARGIN_MYHEIGHT: CGFloat = 5
|
|
static let MAX_TEXT_INPUT_CHARS = 1000
|
|
}
|
|
|
|
protocol ChatInputViewDelegate: class {
|
|
func chatInputViewCameraButtonPressed(_ view: ChatInputView, cameraView: UIView)
|
|
func chatInputViewSendButtonPressed(_ view: ChatInputView)
|
|
func chatInputViewTextDidChange(_ view: ChatInputView)
|
|
}
|
|
|
|
class ChatInputView: UIView {
|
|
weak var delegate: ChatInputViewDelegate?
|
|
|
|
var text: String {
|
|
get {
|
|
return textView.text
|
|
}
|
|
set {
|
|
textView.text = newValue
|
|
updateViews()
|
|
}
|
|
}
|
|
|
|
var maxHeight: CGFloat {
|
|
didSet {
|
|
updateViews()
|
|
}
|
|
}
|
|
|
|
var cameraButtonEnabled: Bool = true{
|
|
didSet {
|
|
updateViews()
|
|
}
|
|
}
|
|
|
|
fileprivate var topBorder: UIView!
|
|
fileprivate var cameraButton: UIButton!
|
|
fileprivate var textView: UITextView!
|
|
fileprivate var sendButton: UIButton!
|
|
fileprivate var myHeight: Constraint!
|
|
fileprivate var didconstraint = 0
|
|
|
|
init(theme: Theme) {
|
|
self.maxHeight = 0.0
|
|
|
|
super.init(frame: CGRect.zero)
|
|
|
|
backgroundColor = theme.colorForType(.ChatInputBackground)
|
|
|
|
createViews(theme)
|
|
installConstraints()
|
|
updateViews()
|
|
}
|
|
|
|
required convenience init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func becomeFirstResponder() -> Bool {
|
|
return textView.becomeFirstResponder()
|
|
}
|
|
|
|
override func resignFirstResponder() -> Bool {
|
|
return textView.resignFirstResponder()
|
|
}
|
|
}
|
|
|
|
// MARK: Actions
|
|
extension ChatInputView {
|
|
@objc func cameraButtonPressed() {
|
|
delegate?.chatInputViewCameraButtonPressed(self, cameraView: cameraButton)
|
|
}
|
|
|
|
@objc func sendButtonPressed() {
|
|
delegate?.chatInputViewSendButtonPressed(self)
|
|
updateTextviewHeight(textView)
|
|
}
|
|
}
|
|
|
|
extension ChatInputView: UITextViewDelegate {
|
|
func textViewDidChange(_ textView: UITextView) {
|
|
updateViews()
|
|
updateTextviewHeight(textView)
|
|
delegate?.chatInputViewTextDidChange(self)
|
|
}
|
|
|
|
override func didMoveToWindow() {
|
|
updateTextviewHeight(textView)
|
|
}
|
|
|
|
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
|
// get the current text, or use an empty string if that failed
|
|
let currentText = textView.text ?? ""
|
|
|
|
// attempt to read the range they are trying to change, or exit if we can't
|
|
guard let stringRange = Range(range, in: currentText) else { return false }
|
|
|
|
// add their new text to the existing text
|
|
let updatedText = currentText.replacingCharacters(in: stringRange, with: text)
|
|
|
|
// make sure the result is under MAX_TEXT_INPUT_CHARS characters
|
|
return updatedText.count <= Constants.MAX_TEXT_INPUT_CHARS
|
|
}
|
|
}
|
|
|
|
private extension ChatInputView {
|
|
|
|
func createViews(_ theme: Theme) {
|
|
topBorder = UIView()
|
|
topBorder.backgroundColor = theme.colorForType(.SeparatorsAndBorders)
|
|
addSubview(topBorder)
|
|
|
|
let cameraImage = UIImage.templateNamed("chat-camera")
|
|
|
|
cameraButton = UIButton()
|
|
cameraButton.setImage(cameraImage, for: UIControlState())
|
|
cameraButton.tintColor = theme.colorForType(.LinkText)
|
|
cameraButton.addTarget(self, action: #selector(ChatInputView.cameraButtonPressed), for: .touchUpInside)
|
|
cameraButton.setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal)
|
|
addSubview(cameraButton)
|
|
|
|
textView = UITextView()
|
|
textView.delegate = self
|
|
textView.font = UIFont.systemFont(ofSize: 16.0)
|
|
textView.backgroundColor = theme.colorForType(.NormalBackground)
|
|
textView.layer.cornerRadius = 5.0
|
|
textView.layer.borderWidth = 0.5
|
|
textView.layer.borderColor = theme.colorForType(.SeparatorsAndBorders).cgColor
|
|
textView.layer.masksToBounds = true
|
|
textView.setContentHuggingPriority(UILayoutPriority(rawValue: 0.0), for: .horizontal)
|
|
textView.autocapitalizationType = .none
|
|
|
|
addSubview(textView)
|
|
|
|
sendButton = UIButton(type: .system)
|
|
sendButton.setTitle(String(localized: "chat_send_button"), for: UIControlState())
|
|
sendButton.titleLabel?.font = UIFont.antidoteFontWithSize(16.0, weight: .bold)
|
|
sendButton.addTarget(self, action: #selector(ChatInputView.sendButtonPressed), for: .touchUpInside)
|
|
sendButton.setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal)
|
|
addSubview(sendButton)
|
|
}
|
|
|
|
func installConstraints() {
|
|
topBorder.snp.makeConstraints {
|
|
$0.top.leading.trailing.equalTo(self)
|
|
$0.height.equalTo(Constants.TopBorderHeight)
|
|
}
|
|
|
|
cameraButton.snp.makeConstraints {
|
|
$0.leading.equalTo(self).offset(Constants.CameraHorizontalOffset)
|
|
$0.bottom.equalTo(self).offset(Constants.CameraBottomOffset)
|
|
}
|
|
|
|
textView.snp.makeConstraints {
|
|
$0.leading.equalTo(cameraButton.snp.trailing).offset(Constants.CameraHorizontalOffset)
|
|
$0.top.equalTo(self).offset(Constants.Offset)
|
|
$0.bottom.equalTo(self).offset(-Constants.Offset)
|
|
$0.height.greaterThanOrEqualTo(Constants.TextViewMinHeight)
|
|
}
|
|
|
|
sendButton.snp.makeConstraints {
|
|
$0.leading.equalTo(textView.snp.trailing).offset(Constants.Offset)
|
|
$0.trailing.equalTo(self).offset(-Constants.Offset)
|
|
$0.bottom.equalTo(self).offset(-Constants.Offset)
|
|
}
|
|
}
|
|
|
|
func updateTextviewHeight(_ t : UITextView)
|
|
{
|
|
if (self.didconstraint == 1)
|
|
{
|
|
self.myHeight.uninstall()
|
|
self.didconstraint = 0
|
|
}
|
|
|
|
let text_needs_size = t.sizeThatFits(
|
|
CGSize(width: t.frame.size.width,
|
|
height: CGFloat.greatestFiniteMagnitude))
|
|
var new_height = text_needs_size.height + Constants.MARGIN_MYHEIGHT
|
|
if (text_needs_size.height > Constants.MAX_MYHEIGHT)
|
|
{
|
|
new_height = Constants.MAX_MYHEIGHT
|
|
}
|
|
else if (text_needs_size.height < Constants.MIN_MYHEIGHT) {
|
|
new_height = Constants.MIN_MYHEIGHT
|
|
}
|
|
|
|
if (self.didconstraint == 0) {
|
|
self.didconstraint = 1
|
|
self.snp.makeConstraints {
|
|
self.myHeight = $0.height.equalTo(new_height).constraint
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateViews() {
|
|
textView.isScrollEnabled = true
|
|
textView.autocapitalizationType = .none
|
|
cameraButton.isEnabled = cameraButtonEnabled
|
|
sendButton.isEnabled = !textView.text.isEmpty
|
|
}
|
|
}
|