Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
Expand Down
6 changes: 6 additions & 0 deletions Examples/Examples/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "attachment.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "attachment@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 17 additions & 6 deletions Examples/Examples/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,21 @@ class ViewController: MessageViewController, UITableViewDataSource, UITableViewD

borderColor = .lightGray

messageView.inset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
messageView.inset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 16)
messageView.font = UIFont.preferredFont(forTextStyle: .body)
messageView.buttonLeftInset = 8

messageView.set(buttonIcon: UIImage(named: "attachment"), for: .normal, type: .left)
messageView.addButton(target: self, action: #selector(onLeftButton), type: .left)
messageView.showLeftButton = true

messageView.textView.placeholderText = "New message..."
messageView.textView.placeholderTextColor = .lightGray
messageView.font = UIFont.systemFont(ofSize: 17)
messageView.textView.backgroundColor = .white

messageView.set(buttonTitle: "Send", for: .normal)
messageView.addButton(target: self, action: #selector(onButton))
messageView.buttonTint = .blue
messageView.set(buttonTitle: "Send", for: .normal, type: .right)
messageView.addButton(target: self, action: #selector(onRightButton), type: .right)
messageView.rightButtonTint = .blue

messageAutocompleteController.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
messageAutocompleteController.tableView.dataSource = self
Expand All @@ -52,7 +59,11 @@ class ViewController: MessageViewController, UITableViewDataSource, UITableViewD
setup(scrollView: tableView)
}

@objc func onButton() {
@objc func onLeftButton() {
print("Did press left button")
}

@objc func onRightButton() {
data.append(messageView.text)
messageView.text = ""
tableView.reloadData()
Expand Down
143 changes: 112 additions & 31 deletions MessageViewController/MessageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,44 @@ public final class MessageView: UIView, MessageTextViewListener {
public let textView = MessageTextView()

internal weak var delegate: MessageViewDelegate?
internal let button = UIButton()
internal let leftButton = UIButton()
internal let rightButton = UIButton()
internal let UITextViewContentSizeKeyPath = #keyPath(UITextView.contentSize)
internal let topBorderLayer = CALayer()
internal var contentView: UIView?
internal var buttonAction: Selector?
internal var leftButtonAction: Selector?
internal var rightButtonAction: Selector?
public var showLeftButton: Bool = true

public enum buttonType {
case left
case right
}

internal override init(frame: CGRect) {
super.init(frame: frame)

backgroundColor = .white

addSubview(leftButton)
addSubview(textView)
addSubview(button)
addSubview(rightButton)
layer.addSublayer(topBorderLayer)

//Set action button
leftButton.imageEdgeInsets = .zero
leftButton.titleEdgeInsets = .zero
leftButton.contentEdgeInsets = .zero
leftButton.titleLabel?.font = self.font ?? UIFont.systemFont(ofSize: 14)
leftButton.imageView?.contentMode = .scaleAspectFit
leftButton.imageView?.clipsToBounds = true

// setup text view
textView.contentInset = .zero
textView.textContainerInset = .zero
textView.backgroundColor = .clear
textView.addObserver(self, forKeyPath: UITextViewContentSizeKeyPath, options: [.new], context: nil)
textView.font = .systemFont(ofSize: UIFont.systemFontSize)
textView.font = self.font ?? UIFont.systemFont(ofSize: 14)
textView.add(listener: self)

// setup TextKit props to defaults
Expand All @@ -47,9 +64,12 @@ public final class MessageView: UIView, MessageTextViewListener {
textView.layoutManager.usesFontLeading = true

// setup send button
button.titleEdgeInsets = .zero
button.contentEdgeInsets = .zero
button.imageEdgeInsets = .zero
rightButton.imageEdgeInsets = .zero
rightButton.titleEdgeInsets = .zero
rightButton.contentEdgeInsets = .zero
rightButton.titleLabel?.font = self.font ?? UIFont.systemFont(ofSize: 14)
rightButton.imageView?.contentMode = .scaleAspectFit
rightButton.imageView?.clipsToBounds = true

updateEmptyTextStates()
}
Expand All @@ -67,6 +87,8 @@ public final class MessageView: UIView, MessageTextViewListener {
public var font: UIFont? {
get { return textView.font }
set {
leftButton.titleLabel?.font = newValue
rightButton.titleLabel?.font = newValue
textView.font = newValue
delegate?.wantsLayout(messageView: self)
}
Expand All @@ -92,22 +114,60 @@ public final class MessageView: UIView, MessageTextViewListener {
didSet { setNeedsLayout() }
}

public func set(buttonIcon: UIImage?, for state: UIControlState) {
button.setImage(buttonIcon, for: state)
public func set(buttonIcon: UIImage?, for state: UIControlState, type: buttonType) {
switch type {
case .left:
setLeft(buttonIcon: buttonIcon, for: state)
break
case .right:
setRight(buttonIcon: buttonIcon, for: state)
break
}
buttonLayoutDidChange()
}

public func set(buttonTitle: String, for state: UIControlState) {
button.setTitle(buttonTitle, for: state)
public func set(buttonTitle: String, for state: UIControlState, type: buttonType) {
switch type {
case .left:
setLeft(buttonTitle: buttonTitle, for: state)
break
case .right:
setRight(buttonTitle: buttonTitle, for: state)
break
}
buttonLayoutDidChange()
}

public var buttonTint: UIColor {
get { return button.tintColor }
private func setLeft(buttonIcon: UIImage?, for state: UIControlState) {
leftButton.setImage(buttonIcon, for: state)
}

private func setLeft(buttonTitle: String, for state: UIControlState) {
leftButton.setTitle(buttonTitle, for: state)
}

private func setRight(buttonIcon: UIImage?, for state: UIControlState) {
rightButton.setImage(buttonIcon, for: state)
}

private func setRight(buttonTitle: String, for state: UIControlState) {
rightButton.setTitle(buttonTitle, for: state)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: These look like unnecessary wrappers for these methods. It would be better to just call them directly


public var leftButtonTint: UIColor {
get { return leftButton.tintColor }
set {
leftButton.tintColor = newValue
leftButton.setTitleColor(newValue, for: .normal)
leftButton.imageView?.tintColor = newValue
}
}
public var rightButtonTint: UIColor {
get { return rightButton.tintColor }
set {
button.tintColor = newValue
button.setTitleColor(newValue, for: .normal)
button.imageView?.tintColor = newValue
rightButton.tintColor = newValue
rightButton.setTitleColor(newValue, for: .normal)
rightButton.imageView?.tintColor = newValue
}
}

Expand All @@ -131,13 +191,21 @@ public final class MessageView: UIView, MessageTextViewListener {
set { textView.keyboardType = newValue }
}

public func addButton(target: Any, action: Selector) {
button.addTarget(target, action: action, for: .touchUpInside)
buttonAction = action
public func addButton(target: Any, action: Selector, type: buttonType) {
switch type {
case .left:
leftButton.addTarget(target, action: action, for: .touchUpInside)
leftButtonAction = action
break
case .right:
rightButton.addTarget(target, action: action, for: .touchUpInside)
rightButtonAction = action
break
}
}

public override var keyCommands: [UIKeyCommand]? {
guard let action = buttonAction else { return nil }
guard let action = rightButtonAction else { return nil }
return [UIKeyCommand(input: "\r", modifierFlags: .command, action: action)]
}

Expand All @@ -161,23 +229,35 @@ public final class MessageView: UIView, MessageTextViewListener {
)
let insetBounds = UIEdgeInsetsInsetRect(safeBounds, inset)

let buttonSize = button.bounds.size

let textViewFrame = CGRect(
let size = textView.font?.lineHeight ?? 25 //Use textView line height
let leftButtonSize = CGSize(width: size, height: size)
let rightButtonSize = rightButton.bounds.size

// adjust by bottom offset so content is flush w/ text view
let leftButtonFrame = CGRect(
x: insetBounds.minX,
y: (insetBounds.minY + textViewHeight) - leftButtonSize.height + leftButton.bottomHeightOffset,
width: leftButtonSize.width,
height: leftButtonSize.height
)
leftButton.frame = (showLeftButton) ? leftButtonFrame : .zero

let textViewFrame = CGRect(
x: ((showLeftButton) ? leftButtonFrame.maxX : 0) + buttonLeftInset,
y: insetBounds.minY,
width: insetBounds.width - buttonSize.width - buttonLeftInset,
width: insetBounds.width - ((showLeftButton) ? leftButtonSize.width : 0) - buttonLeftInset - rightButtonSize.width,
height: textViewHeight
)
textView.frame = textViewFrame

// adjust by bottom offset so content is flush w/ text view
button.frame = CGRect(
let rightButtonFrame = CGRect(
x: textViewFrame.maxX + buttonLeftInset,
y: textViewFrame.maxY - buttonSize.height + button.bottomHeightOffset,
width: buttonSize.width,
height: buttonSize.height
y: textViewFrame.maxY - rightButtonSize.height + rightButton.bottomHeightOffset,
width: rightButtonSize.width,
height: rightButtonSize.height
)
rightButton.frame = rightButtonFrame

let contentY = textViewFrame.maxY + inset.bottom
contentView?.frame = CGRect(
Expand Down Expand Up @@ -217,12 +297,13 @@ public final class MessageView: UIView, MessageTextViewListener {

internal func updateEmptyTextStates() {
let isEmpty = text.isEmpty
button.isEnabled = !isEmpty
button.alpha = isEmpty ? 0.25 : 1
rightButton.isEnabled = !isEmpty
rightButton.alpha = isEmpty ? 0.25 : 1
}

internal func buttonLayoutDidChange() {
button.sizeToFit()
leftButton.sizeToFit()
rightButton.sizeToFit()
setNeedsLayout()
}

Expand Down