Files
MessageKit/Sources/Views/InputBarItem.swift
T
2017-11-10 00:08:08 +08:00

327 lines
10 KiB
Swift

/*
MIT License
Copyright (c) 2017 MessageKit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import UIKit
/**
A InputItem that inherits from UIButton
## Important Notes ##
1. Intended to be used in an `InputStackView`
*/
open class InputBarButtonItem: UIButton {
/// The spacing properties of the InputBarButtonItem
///
/// - fixed: The spacing is fixed
/// - flexible: The spacing is flexible
/// - none: There is no spacing
public enum Spacing {
case fixed(CGFloat)
case flexible
case none
}
public typealias InputBarButtonItemAction = ((InputBarButtonItem) -> Void)
// MARK: - Properties
/// A weak reference to the MessageInputBar that the InputBarButtonItem used in
open weak var messageInputBar: MessageInputBar?
/// The spacing property of the InputBarButtonItem that determines the contentHuggingPriority and any
/// additional space to the intrinsicContentSize
open var spacing: Spacing = .none {
didSet {
switch spacing {
case .flexible:
setContentHuggingPriority(UILayoutPriority(rawValue: 1), for: .horizontal)
case .fixed:
setContentHuggingPriority(UILayoutPriority(rawValue: 1000), for: .horizontal)
case .none:
setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .horizontal)
}
}
}
/// When not nil this size overrides the intrinsicContentSize
private var size: CGSize? = CGSize(width: 20, height: 20) {
didSet {
invalidateIntrinsicContentSize()
}
}
open override var intrinsicContentSize: CGSize {
var contentSize = size ?? super.intrinsicContentSize
switch spacing {
case .fixed(let width):
contentSize.width += width
case .flexible, .none:
break
}
return contentSize
}
/// A reference to the stack view position that the InputBarButtonItem is held in
open var parentStackViewPosition: InputStackView.Position?
/// The title for the UIControlState.normal
open var title: String? {
get {
return title(for: .normal)
}
set {
setTitle(newValue, for: .normal)
}
}
/// The image for the UIControlState.normal
open var image: UIImage? {
get {
return image(for: .normal)
}
set {
setImage(newValue, for: .normal)
}
}
/// Calls the onSelectedAction or onDeselectedAction when set
open override var isHighlighted: Bool {
didSet {
if isHighlighted {
onSelectedAction?(self)
} else {
onDeselectedAction?(self)
}
}
}
/// Calls the onEnabledAction or onDisabledAction when set
open override var isEnabled: Bool {
didSet {
if isEnabled {
onEnabledAction?(self)
} else {
onDisabledAction?(self)
}
}
}
// MARK: - Reactive Hooks
private var onTouchUpInsideAction: InputBarButtonItemAction?
private var onKeyboardEditingBeginsAction: InputBarButtonItemAction?
private var onKeyboardEditingEndsAction: InputBarButtonItemAction?
private var onTextViewDidChangeAction: ((InputBarButtonItem, InputTextView) -> Void)?
private var onSelectedAction: InputBarButtonItemAction?
private var onDeselectedAction: InputBarButtonItemAction?
private var onEnabledAction: InputBarButtonItemAction?
private var onDisabledAction: InputBarButtonItemAction?
// MARK: - Initialization
public convenience init() {
self.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - Setup
/// Sets up the default properties
open func setup() {
contentVerticalAlignment = .center
contentHorizontalAlignment = .center
imageView?.contentMode = .scaleAspectFit
setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .horizontal)
setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .vertical)
setTitleColor(UIColor(red: 0, green: 122/255, blue: 1, alpha: 1), for: .normal)
setTitleColor(UIColor(red: 0, green: 122/255, blue: 1, alpha: 0.3), for: .highlighted)
setTitleColor(.lightGray, for: .disabled)
adjustsImageWhenHighlighted = false
addTarget(self, action: #selector(InputBarButtonItem.touchUpInsideAction), for: .touchUpInside)
}
// MARK: - Size Adjustment
/// Sets the size of the InputBarButtonItem which overrides the intrinsicContentSize. When set to nil
/// the default intrinsicContentSize is used. The new size will be laid out in the UIStackView that
/// the InputBarButtonItem is held in
///
/// - Parameters:
/// - newValue: The new size
/// - animated: If the layout should be animated
open func setSize(_ newValue: CGSize?, animated: Bool) {
size = newValue
if animated, let position = parentStackViewPosition {
messageInputBar?.performLayout(animated) { [weak self] in
self?.messageInputBar?.layoutStackViews([position])
}
}
}
// MARK: - Hook Setup Methods
/// Used to setup your own initial properties
///
/// - Parameter item: A reference to Self
/// - Returns: Self
@discardableResult
open func configure(_ item: InputBarButtonItemAction) -> Self {
item(self)
return self
}
/// Sets the onKeyboardEditingBeginsAction
///
/// - Parameter action: The new onKeyboardEditingBeginsAction
/// - Returns: Self
@discardableResult
open func onKeyboardEditingBegins(_ action: @escaping InputBarButtonItemAction) -> Self {
onKeyboardEditingBeginsAction = action
return self
}
/// Sets the onKeyboardEditingEndsAction
///
/// - Parameter action: The new onKeyboardEditingEndsAction
/// - Returns: Self
@discardableResult
open func onKeyboardEditingEnds(_ action: @escaping InputBarButtonItemAction) -> Self {
onKeyboardEditingEndsAction = action
return self
}
/// Sets the onTextViewDidChangeAction
///
/// - Parameter action: The new onTextViewDidChangeAction
/// - Returns: Self
@discardableResult
open func onTextViewDidChange(_ action: @escaping (_ item: InputBarButtonItem, _ textView: InputTextView) -> Void) -> Self {
onTextViewDidChangeAction = action
return self
}
/// Sets the onTouchUpInsideAction
///
/// - Parameter action: The new onTouchUpInsideAction
/// - Returns: Self
@discardableResult
open func onTouchUpInside(_ action: @escaping InputBarButtonItemAction) -> Self {
onTouchUpInsideAction = action
return self
}
/// Sets the onSelectedAction
///
/// - Parameter action: The new onSelectedAction
/// - Returns: Self
@discardableResult
open func onSelected(_ action: @escaping InputBarButtonItemAction) -> Self {
onSelectedAction = action
return self
}
/// Sets the onDeselectedAction
///
/// - Parameter action: The new onDeselectedAction
/// - Returns: Self
@discardableResult
open func onDeselected(_ action: @escaping InputBarButtonItemAction) -> Self {
onDeselectedAction = action
return self
}
/// Sets the onEnabledAction
///
/// - Parameter action: The new onEnabledAction
/// - Returns: Self
@discardableResult
open func onEnabled(_ action: @escaping InputBarButtonItemAction) -> Self {
onEnabledAction = action
return self
}
/// Sets the onDisabledAction
///
/// - Parameter action: The new onDisabledAction
/// - Returns: Self
@discardableResult
open func onDisabled(_ action: @escaping InputBarButtonItemAction) -> Self {
onDisabledAction = action
return self
}
// MARK: - InputItem Protocol
/// Executes the onTextViewDidChangeAction with the given textView
///
/// - Parameter textView: A reference to the InputTextView
open func textViewDidChangeAction(with textView: InputTextView) {
onTextViewDidChangeAction?(self, textView)
}
/// Executes the onKeyboardEditingEndsAction
open func keyboardEditingEndsAction() {
onKeyboardEditingEndsAction?(self)
}
/// Executes the onKeyboardEditingBeginsAction
open func keyboardEditingBeginsAction() {
onKeyboardEditingBeginsAction?(self)
}
/// Executes the onTouchUpInsideAction
@objc
open func touchUpInsideAction() {
onTouchUpInsideAction?(self)
}
// MARK: - Static Spacers
/// An InputBarButtonItem that's spacing property is set to be .flexible
open static var flexibleSpace: InputBarButtonItem {
let item = InputBarButtonItem()
item.setSize(.zero, animated: false)
item.spacing = .flexible
return item
}
/// An InputBarButtonItem that's spacing property is set to be .fixed with the width arguement
open static func fixedSpace(_ width: CGFloat) -> InputBarButtonItem {
let item = InputBarButtonItem()
item.setSize(.zero, animated: false)
item.spacing = .fixed(width)
return item
}
}