Antidote/Antidote/ChatInputView.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
}
}