Updating documentation
This commit is contained in:
parent
379d11e657
commit
659475bc23
|
@ -17,15 +17,17 @@
|
|||
CED4FCEC1D9FD95D00F54838 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CED4FCEA1D9FD95D00F54838 /* LaunchScreen.storyboard */; };
|
||||
CED4FCF71DA014B100F54838 /* Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED4FCF61DA014B100F54838 /* Reaction.swift */; };
|
||||
CED4FCF91DA10D6600F54838 /* ReactionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED4FCF81DA10D6600F54838 /* ReactionButton.swift */; };
|
||||
CED4FCFB1DA10E4300F54838 /* ReactionSelectControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED4FCFA1DA10E4300F54838 /* ReactionSelectControl.swift */; };
|
||||
CED4FCFB1DA10E4300F54838 /* ReactionSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED4FCFA1DA10E4300F54838 /* ReactionSelector.swift */; };
|
||||
CED4FCFD1DA1100900F54838 /* ReactionFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED4FCFC1DA1100900F54838 /* ReactionFeedback.swift */; };
|
||||
CED9D93C1DA64FCD00A70C2D /* Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D93B1DA64FCD00A70C2D /* Components.swift */; };
|
||||
CED9D93E1DA6566700A70C2D /* FacebookReactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D93D1DA6566700A70C2D /* FacebookReactions.swift */; };
|
||||
CED9D9401DA6869500A70C2D /* ReactionSelectControlConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D93F1DA6869500A70C2D /* ReactionSelectControlConfig.swift */; };
|
||||
CED9D9401DA6869500A70C2D /* ReactionSelectorConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D93F1DA6869500A70C2D /* ReactionSelectorConfig.swift */; };
|
||||
CED9D9421DA6931A00A70C2D /* ReactionAlignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D9411DA6931A00A70C2D /* ReactionAlignment.swift */; };
|
||||
CED9D9461DA6A07D00A70C2D /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D9451DA6A07D00A70C2D /* Sequence.swift */; };
|
||||
CED9D9481DA6EAFA00A70C2D /* ReactionFeedbackDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D9471DA6EAFA00A70C2D /* ReactionFeedbackDelegate.swift */; };
|
||||
CED9D94A1DA78F9400A70C2D /* ReactionButtonConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D9491DA78F9400A70C2D /* ReactionButtonConfig.swift */; };
|
||||
CED9D94C1DA7D36700A70C2D /* ReactionSummaryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D94B1DA7D36700A70C2D /* ReactionSummaryConfig.swift */; };
|
||||
CED9D94E1DA7E49500A70C2D /* Configurable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED9D94D1DA7E49500A70C2D /* Configurable.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
|
@ -41,15 +43,17 @@
|
|||
CED4FCED1D9FD95D00F54838 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
CED4FCF61DA014B100F54838 /* Reaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reaction.swift; sourceTree = "<group>"; };
|
||||
CED4FCF81DA10D6600F54838 /* ReactionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionButton.swift; sourceTree = "<group>"; };
|
||||
CED4FCFA1DA10E4300F54838 /* ReactionSelectControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionSelectControl.swift; sourceTree = "<group>"; };
|
||||
CED4FCFA1DA10E4300F54838 /* ReactionSelector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionSelector.swift; sourceTree = "<group>"; };
|
||||
CED4FCFC1DA1100900F54838 /* ReactionFeedback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionFeedback.swift; sourceTree = "<group>"; };
|
||||
CED9D93B1DA64FCD00A70C2D /* Components.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Components.swift; sourceTree = "<group>"; };
|
||||
CED9D93D1DA6566700A70C2D /* FacebookReactions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FacebookReactions.swift; sourceTree = "<group>"; };
|
||||
CED9D93F1DA6869500A70C2D /* ReactionSelectControlConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionSelectControlConfig.swift; sourceTree = "<group>"; };
|
||||
CED9D93F1DA6869500A70C2D /* ReactionSelectorConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionSelectorConfig.swift; sourceTree = "<group>"; };
|
||||
CED9D9411DA6931A00A70C2D /* ReactionAlignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionAlignment.swift; sourceTree = "<group>"; };
|
||||
CED9D9451DA6A07D00A70C2D /* Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sequence.swift; sourceTree = "<group>"; };
|
||||
CED9D9471DA6EAFA00A70C2D /* ReactionFeedbackDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionFeedbackDelegate.swift; sourceTree = "<group>"; };
|
||||
CED9D9491DA78F9400A70C2D /* ReactionButtonConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionButtonConfig.swift; sourceTree = "<group>"; };
|
||||
CED9D94B1DA7D36700A70C2D /* ReactionSummaryConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionSummaryConfig.swift; sourceTree = "<group>"; };
|
||||
CED9D94D1DA7E49500A70C2D /* Configurable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configurable.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -98,6 +102,7 @@
|
|||
children = (
|
||||
CE874F021DA2DA64000D309E /* ComponentBuilder.swift */,
|
||||
CED9D93B1DA64FCD00A70C2D /* Components.swift */,
|
||||
CED9D94D1DA7E49500A70C2D /* Configurable.swift */,
|
||||
CED9D93D1DA6566700A70C2D /* FacebookReactions.swift */,
|
||||
CED4FCF61DA014B100F54838 /* Reaction.swift */,
|
||||
CED9D9411DA6931A00A70C2D /* ReactionAlignment.swift */,
|
||||
|
@ -105,9 +110,10 @@
|
|||
CED9D9491DA78F9400A70C2D /* ReactionButtonConfig.swift */,
|
||||
CED4FCFC1DA1100900F54838 /* ReactionFeedback.swift */,
|
||||
CED9D9471DA6EAFA00A70C2D /* ReactionFeedbackDelegate.swift */,
|
||||
CED4FCFA1DA10E4300F54838 /* ReactionSelectControl.swift */,
|
||||
CED9D93F1DA6869500A70C2D /* ReactionSelectControlConfig.swift */,
|
||||
CED4FCFA1DA10E4300F54838 /* ReactionSelector.swift */,
|
||||
CED9D93F1DA6869500A70C2D /* ReactionSelectorConfig.swift */,
|
||||
CE874F001DA2D916000D309E /* ReactionSummary.swift */,
|
||||
CED9D94B1DA7D36700A70C2D /* ReactionSummaryConfig.swift */,
|
||||
CED9D9451DA6A07D00A70C2D /* Sequence.swift */,
|
||||
CEB867BB1DA41CB600031B0D /* UIReactionControl.swift */,
|
||||
);
|
||||
|
@ -194,16 +200,18 @@
|
|||
CE874F011DA2D916000D309E /* ReactionSummary.swift in Sources */,
|
||||
CED4FCF91DA10D6600F54838 /* ReactionButton.swift in Sources */,
|
||||
CEB867BC1DA41CB600031B0D /* UIReactionControl.swift in Sources */,
|
||||
CED9D94E1DA7E49500A70C2D /* Configurable.swift in Sources */,
|
||||
CE874F031DA2DA64000D309E /* ComponentBuilder.swift in Sources */,
|
||||
CED4FCE41D9FD95D00F54838 /* ViewController.swift in Sources */,
|
||||
CED4FCE21D9FD95D00F54838 /* AppDelegate.swift in Sources */,
|
||||
CED4FCFD1DA1100900F54838 /* ReactionFeedback.swift in Sources */,
|
||||
CED9D9461DA6A07D00A70C2D /* Sequence.swift in Sources */,
|
||||
CED9D9401DA6869500A70C2D /* ReactionSelectControlConfig.swift in Sources */,
|
||||
CED9D9401DA6869500A70C2D /* ReactionSelectorConfig.swift in Sources */,
|
||||
CED9D94A1DA78F9400A70C2D /* ReactionButtonConfig.swift in Sources */,
|
||||
CED9D93C1DA64FCD00A70C2D /* Components.swift in Sources */,
|
||||
CED4FCF71DA014B100F54838 /* Reaction.swift in Sources */,
|
||||
CED4FCFB1DA10E4300F54838 /* ReactionSelectControl.swift in Sources */,
|
||||
CED9D94C1DA7D36700A70C2D /* ReactionSummaryConfig.swift in Sources */,
|
||||
CED4FCFB1DA10E4300F54838 /* ReactionSelector.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yZN-Tr-YCb" customClass="ReactionSelectControl" customModule="ReactionsExample" customModuleProvider="target">
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yZN-Tr-YCb" customClass="ReactionSelector" customModule="ReactionsExample" customModuleProvider="target">
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="52" id="JKf-vL-t3z"/>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import UIKit
|
||||
|
||||
class ViewController: UIViewController, ReactionFeedbackDelegate {
|
||||
@IBOutlet weak var reactionSelect: ReactionSelectControl!
|
||||
@IBOutlet weak var reactionSelect: ReactionSelector!
|
||||
@IBOutlet weak var reactionButton: ReactionButton! {
|
||||
didSet {
|
||||
reactionButton.config = ReactionButtonConfig() {
|
||||
|
@ -22,20 +22,19 @@ class ViewController: UIViewController, ReactionFeedbackDelegate {
|
|||
|
||||
@IBOutlet weak var facebookReactionButton: ReactionButton! {
|
||||
didSet {
|
||||
facebookReactionButton.reactionSelectControl = ReactionSelectControl()
|
||||
facebookReactionButton.config = ReactionButtonConfig() {
|
||||
facebookReactionButton.reactionSelector = ReactionSelector()
|
||||
facebookReactionButton.config = ReactionButtonConfig() {
|
||||
$0.iconMarging = 8
|
||||
$0.spacing = 4
|
||||
$0.font = UIFont(name: "HelveticaNeue", size: 14)
|
||||
}
|
||||
|
||||
facebookReactionButton.reactionSelectControl?.feedbackDelegate = self
|
||||
facebookReactionButton.reactionSelector?.feedbackDelegate = self
|
||||
}
|
||||
}
|
||||
@IBOutlet weak var reactionSummary: ReactionSummary! {
|
||||
didSet {
|
||||
reactionSummary.reactions = Reaction.facebook.all
|
||||
reactionSummary.alignment = .left
|
||||
reactionSummary.text = "A description"
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +60,7 @@ class ViewController: UIViewController, ReactionFeedbackDelegate {
|
|||
}
|
||||
|
||||
@IBAction func summaryTouchedAction(_ sender: AnyObject) {
|
||||
facebookReactionButton.presentOverlay()
|
||||
facebookReactionButton.presentReactionSelector()
|
||||
}
|
||||
|
||||
// MARK: - ReactionFeedback Methods
|
||||
|
@ -69,7 +68,7 @@ class ViewController: UIViewController, ReactionFeedbackDelegate {
|
|||
func reactionFeedbackDidChanged(_ feedback: ReactionFeedback?) {
|
||||
feedbackLabel.isHidden = feedback == nil
|
||||
|
||||
feedbackLabel.text = feedback?.localizedString()
|
||||
feedbackLabel.text = feedback?.localizedString
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,11 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
/// Protocol helper to build interface component in an easy and elegant way
|
||||
/// Protocol helper to build interface component in an easy and elegant way.
|
||||
protocol ComponentBuilder {}
|
||||
|
||||
extension ComponentBuilder where Self: AnyObject {
|
||||
/// Calls the parameter block in order to update the receiver properties and then returns the object.
|
||||
func build(_ block: (Self) -> Void) -> Self {
|
||||
block(self)
|
||||
|
||||
|
|
|
@ -86,12 +86,5 @@ struct Components {
|
|||
$0.contents = option.icon.cgImage
|
||||
}
|
||||
}
|
||||
|
||||
static func facebookSummaryLabel() -> UILabel {
|
||||
return UILabel().build {
|
||||
$0.font = UIFont(name: "HelveticaNeue", size: 14)
|
||||
$0.textColor = UIColor(red: 0.47, green: 0.47, blue: 0.47, alpha: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Reactions
|
||||
*
|
||||
* Copyright 2016-present Yannick Loriot.
|
||||
* http://yannickloriot.com
|
||||
*
|
||||
* 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 Foundation
|
||||
|
||||
/// Protocol to create a config object.
|
||||
protocol Configurable {
|
||||
/**
|
||||
The builder block.
|
||||
The block gives a reference of receiver you can configure.
|
||||
*/
|
||||
typealias ConfigurableBlock = (Self) -> Void
|
||||
|
||||
/**
|
||||
Initialize a configurable with default values.
|
||||
|
||||
- Parameter block: A configurable block to configure itself.
|
||||
*/
|
||||
init(block: ConfigurableBlock)
|
||||
}
|
|
@ -26,32 +26,41 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
/// Default implementation of the facebook reactions.
|
||||
extension Reaction {
|
||||
/// Struct which defines the standard facebook reactions.
|
||||
public struct facebook {
|
||||
/// The facebook's "like" reaction.
|
||||
public static var like: Reaction {
|
||||
return Reaction(id: "like", title: "J'aime", color: UIColor(red: 0.29, green: 0.54, blue: 0.95, alpha: 1), icon: UIImage(named: "like")!, alternativeIcon: UIImage(named: "like-template")?.withRenderingMode(.alwaysTemplate))
|
||||
}
|
||||
|
||||
/// The facebook's "love" reaction.
|
||||
public static var love: Reaction {
|
||||
return Reaction(id: "love", title: "J'adore", color: UIColor(red: 0.93, green: 0.23, blue: 0.33, alpha: 1), icon: UIImage(named: "love")!)
|
||||
}
|
||||
|
||||
/// The facebook's "haha" reaction.
|
||||
public static var haha: Reaction {
|
||||
return Reaction(id: "haha", title: "Haha", color: UIColor(red: 0.99, green: 0.84, blue: 0.38, alpha: 1), icon: UIImage(named: "haha")!)
|
||||
}
|
||||
|
||||
/// The facebook's "wow" reaction.
|
||||
public static var wow: Reaction {
|
||||
return Reaction(id: "wow", title: "Wouah", color: UIColor(red: 0.99, green: 0.84, blue: 0.38, alpha: 1), icon: UIImage(named: "wow")!)
|
||||
}
|
||||
|
||||
/// The facebook's "sad" reaction.
|
||||
public static var sad: Reaction {
|
||||
return Reaction(id: "sad", title: "Triste", color: UIColor(red: 0.99, green: 0.84, blue: 0.38, alpha: 1), icon: UIImage(named: "sad")!)
|
||||
}
|
||||
|
||||
/// The facebook's "angry" reaction.
|
||||
public static var angry: Reaction {
|
||||
return Reaction(id: "angry", title: "Grrr", color: UIColor(red: 0.96, green: 0.37, blue: 0.34, alpha: 1), icon: UIImage(named: "angry")!)
|
||||
}
|
||||
|
||||
/// The list of standard facebook reactions in this order: `.like`, `.love`, `.haha`, `.wow`, `.sad`, `.angry`.
|
||||
public static let all: [Reaction] = [facebook.like, facebook.love, facebook.haha, facebook.wow, facebook.sad, facebook.angry]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,17 +27,40 @@
|
|||
import UIKit
|
||||
|
||||
/**
|
||||
A reaction structure is
|
||||
|
||||
The `Reaction` struct implements a reaction on a `ReactionSelect` object. A tab bar operates strictly in radio mode, where one item is selected at a time—tapping a tab bar item toggles the view above the tab bar. You can also specify a badge value on the tab bar item for adding additional visual information—for example, the Messages app uses a badge on the item to show the number of new messages. This class also provides a number of system defaults for creating items.
|
||||
The `Reaction` struct defines several attributes like the title, the icon or the color of the reaction.
|
||||
|
||||
A `Reaction` can be used with objects like `ReactionSelector`, `ReactionButton` and `ReactionSummary`.
|
||||
*/
|
||||
public struct Reaction {
|
||||
/// The reaction's identifier.
|
||||
public let id: String
|
||||
|
||||
/// The reaction's title.
|
||||
public let title: String
|
||||
|
||||
/// The reaction's color.
|
||||
public let color: UIColor
|
||||
|
||||
/// The reaction's icon image.
|
||||
public let icon: UIImage
|
||||
|
||||
/**
|
||||
The reaction's alternative icon image.
|
||||
|
||||
The alternative icon is only used by the `ReactionButton`. It tries to display the alternative as icon and if it fails it uses the `icon`.
|
||||
*/
|
||||
public let alternativeIcon: UIImage?
|
||||
|
||||
/**
|
||||
Creates and returns a new reaction using the specified properties.
|
||||
|
||||
- Parameter id: The reaction's identifier.
|
||||
- Parameter title: The reaction's title.
|
||||
- Parameter color: The reaction's color.
|
||||
- Parameter icon: The reaction's icon image.
|
||||
- Parameter alternativeIcon: The reaction's alternative icon image.
|
||||
- Returns: Newly initialized reaction with the specified properties.
|
||||
*/
|
||||
public init(id: String, title: String, color: UIColor, icon: UIImage, alternativeIcon: UIImage? = nil) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
|
@ -48,18 +71,21 @@ public struct Reaction {
|
|||
}
|
||||
|
||||
extension Reaction: Equatable {
|
||||
/// Returns a Boolean value indicating whether two values are equal.
|
||||
public static func ==(lhs: Reaction, rhs: Reaction) -> Bool {
|
||||
return lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
extension Reaction: Hashable {
|
||||
/// The hash value.
|
||||
public var hashValue: Int {
|
||||
return id.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
extension Reaction: CustomStringConvertible {
|
||||
/// A textual representation of this instance.
|
||||
public var description: String {
|
||||
return "<Reaction id=\(id) title=\(title)>"
|
||||
}
|
||||
|
|
|
@ -24,11 +24,16 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
These constants specify reaction alignment.
|
||||
*/
|
||||
public enum ReactionAlignment {
|
||||
/// Text and icon are visually left aligned. The icon is to the left of text.
|
||||
case left
|
||||
/// Text and icon are visually right aligned. The icon is to the right of text.
|
||||
case right
|
||||
/// Text and icon are visually center aligned. The icon is to the left of text.
|
||||
case centerLeft
|
||||
/// Text and icon are visually center aligned. The icon is to the right of text.
|
||||
case centerRight
|
||||
}
|
||||
|
|
|
@ -26,6 +26,13 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
A `ReactionButton` object is a control that executes a reaction in response to user interactions.
|
||||
|
||||
You can tap a reaction button in order to highlight/unhighlight a reaction. You can also make a long press to the button to display a `ReactionSelector` if you have attached one.
|
||||
|
||||
You can configure/skin the button using a `ReactionButtonConfig`.
|
||||
*/
|
||||
public final class ReactionButton: UIReactionControl {
|
||||
private let iconImageView: UIImageView = Components.reactionButton.facebookLikeIcon()
|
||||
private let titleLabel: UILabel = Components.reactionButton.facebookLikeLabel()
|
||||
|
@ -34,22 +41,38 @@ public final class ReactionButton: UIReactionControl {
|
|||
$0.backgroundColor = .clear
|
||||
$0.alpha = 0
|
||||
|
||||
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ReactionButton.dismissOverlay)))
|
||||
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ReactionButton.dismissReactionSelector)))
|
||||
}
|
||||
|
||||
/**
|
||||
A Boolean value indicating whether the reaction button is in the selected state.
|
||||
*/
|
||||
public override var isSelected: Bool {
|
||||
didSet { update() }
|
||||
}
|
||||
|
||||
/**
|
||||
The reaction button configuration.
|
||||
*/
|
||||
public var config: ReactionButtonConfig = ReactionButtonConfig() {
|
||||
didSet { update() }
|
||||
}
|
||||
|
||||
/**
|
||||
The reaction used to build the button.
|
||||
|
||||
The reaction `title` fills the button one, and the `alternativeIcon` is used to display the icon. If the `alternativeIcon` is nil, the `icon` is used instead.
|
||||
*/
|
||||
public var reaction = Reaction.facebook.like {
|
||||
didSet { update() }
|
||||
}
|
||||
|
||||
public var reactionSelectControl: ReactionSelectControl? {
|
||||
/**
|
||||
The attached selector that the button will use in order to choose a reaction.
|
||||
|
||||
There are two ways to display the selector: calling the `presentReactionSelector` method or by doing a long press to the button.
|
||||
*/
|
||||
public var reactionSelector: ReactionSelector? {
|
||||
didSet { setupReactionSelect(old: oldValue) }
|
||||
}
|
||||
|
||||
|
@ -63,19 +86,19 @@ public final class ReactionButton: UIReactionControl {
|
|||
addSubview(titleLabel)
|
||||
}
|
||||
|
||||
private func setupReactionSelect(old: ReactionSelectControl?) {
|
||||
if let reactionSelect = reactionSelectControl {
|
||||
overlay.addSubview(reactionSelect)
|
||||
private func setupReactionSelect(old: ReactionSelector?) {
|
||||
if let selector = reactionSelector {
|
||||
overlay.addSubview(selector)
|
||||
}
|
||||
|
||||
old?.removeFromSuperview()
|
||||
old?.removeTarget(self, action: #selector(ReactionButton.reactionTouchedInsideAction), for: .touchUpInside)
|
||||
old?.removeTarget(self, action: #selector(ReactionButton.reactionTouchedOutsideAction), for: .touchUpOutside)
|
||||
|
||||
reaction = reactionSelectControl?.reactions.first ?? Reaction.facebook.like
|
||||
reaction = reactionSelector?.reactions.first ?? Reaction.facebook.like
|
||||
|
||||
reactionSelectControl?.addTarget(self, action: #selector(ReactionButton.reactionTouchedInsideAction), for: .touchUpInside)
|
||||
reactionSelectControl?.addTarget(self, action: #selector(ReactionButton.reactionTouchedOutsideAction), for: .touchUpOutside)
|
||||
reactionSelector?.addTarget(self, action: #selector(ReactionButton.reactionTouchedInsideAction), for: .touchUpInside)
|
||||
reactionSelector?.addTarget(self, action: #selector(ReactionButton.reactionTouchedOutsideAction), for: .touchUpOutside)
|
||||
}
|
||||
|
||||
// MARK: - Updating Object State
|
||||
|
@ -139,34 +162,34 @@ public final class ReactionButton: UIReactionControl {
|
|||
private var isLongPressMoved = false
|
||||
|
||||
func longPressAction(_ gestureRecognizer: UILongPressGestureRecognizer) {
|
||||
guard let reactionSelect = reactionSelectControl else { return }
|
||||
guard let selector = reactionSelector else { return }
|
||||
|
||||
if gestureRecognizer.state == .began {
|
||||
isLongPressMoved = false
|
||||
|
||||
displayOverlay(feedback: .slideFingerAcross)
|
||||
displayReactionSelector(feedback: .slideFingerAcross)
|
||||
}
|
||||
|
||||
if gestureRecognizer.state == .changed {
|
||||
isLongPressMoved = true
|
||||
|
||||
reactionSelect.longPressAction(gestureRecognizer)
|
||||
selector.longPressAction(gestureRecognizer)
|
||||
}
|
||||
else if gestureRecognizer.state == .ended {
|
||||
if isLongPressMoved {
|
||||
reactionSelect.longPressAction(gestureRecognizer)
|
||||
selector.longPressAction(gestureRecognizer)
|
||||
|
||||
dismissOverlay()
|
||||
dismissReactionSelector()
|
||||
}
|
||||
else {
|
||||
reactionSelect.feedback = .tapToSelectAReaction
|
||||
selector.feedback = .tapToSelectAReaction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Responding to Select Events
|
||||
|
||||
func reactionTouchedInsideAction(_ sender: ReactionSelectControl) {
|
||||
func reactionTouchedInsideAction(_ sender: ReactionSelector) {
|
||||
guard let selectedReaction = sender.selectedReaction else { return }
|
||||
|
||||
let isReactionChanged = reaction != selectedReaction
|
||||
|
@ -177,28 +200,36 @@ public final class ReactionButton: UIReactionControl {
|
|||
if isReactionChanged {
|
||||
sendActions(for: .valueChanged)
|
||||
}
|
||||
|
||||
dismissOverlay()
|
||||
|
||||
dismissReactionSelector()
|
||||
}
|
||||
|
||||
func reactionTouchedOutsideAction(_ sender: ReactionSelectControl) {
|
||||
dismissOverlay()
|
||||
func reactionTouchedOutsideAction(_ sender: ReactionSelector) {
|
||||
dismissReactionSelector()
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
// MARK: - Presenting Reaction Selectors
|
||||
|
||||
public func presentOverlay() {
|
||||
displayOverlay(feedback: .tapToSelectAReaction)
|
||||
/**
|
||||
Presents the attached reaction selector.
|
||||
|
||||
If no reaction selector is attached, the method does nothing.
|
||||
*/
|
||||
public func presentReactionSelector() {
|
||||
displayReactionSelector(feedback: .tapToSelectAReaction)
|
||||
}
|
||||
|
||||
public func dismissOverlay() {
|
||||
reactionSelectControl?.feedback = nil
|
||||
|
||||
/**
|
||||
Dismisses the attached reaction selector that was presented by the button.
|
||||
*/
|
||||
public func dismissReactionSelector() {
|
||||
reactionSelector?.feedback = nil
|
||||
|
||||
animateOverlay(alpha: 0, center: CGPoint(x: overlay.bounds.midX, y: overlay.bounds.midY))
|
||||
}
|
||||
|
||||
func displayOverlay(feedback: ReactionFeedback) {
|
||||
guard let reactionSelect = reactionSelectControl, let window = UIApplication.shared.keyWindow else { return }
|
||||
private func displayReactionSelector(feedback: ReactionFeedback) {
|
||||
guard let selector = reactionSelector, let window = UIApplication.shared.keyWindow else { return }
|
||||
|
||||
if overlay.superview == nil {
|
||||
UIApplication.shared.keyWindow?.addSubview(overlay)
|
||||
|
@ -206,26 +237,26 @@ public final class ReactionButton: UIReactionControl {
|
|||
|
||||
overlay.frame = CGRect(x:0 , y: 0, width: window.bounds.width, height: window.bounds.height * 2)
|
||||
|
||||
let centerPoint = convert(CGPoint(x: bounds.midX, y: 0), to: nil)
|
||||
reactionSelect.frame = reactionSelect.boundsToFit()
|
||||
reactionSelect.center = centerPoint
|
||||
let centerPoint = convert(CGPoint(x: bounds.midX, y: 0), to: nil)
|
||||
selector.frame = selector.boundsToFit()
|
||||
selector.center = centerPoint
|
||||
|
||||
if reactionSelect.frame.origin.x - config.spacing < 0 {
|
||||
reactionSelect.center = CGPoint(x: centerPoint.x - reactionSelect.frame.origin.x + config.spacing, y: centerPoint.y)
|
||||
if selector.frame.origin.x - config.spacing < 0 {
|
||||
selector.center = CGPoint(x: centerPoint.x - selector.frame.origin.x + config.spacing, y: centerPoint.y)
|
||||
}
|
||||
else if reactionSelect.frame.origin.x + reactionSelect.frame.width + config.spacing > overlay.bounds.width {
|
||||
reactionSelect.center = CGPoint(x: centerPoint.x - (reactionSelect.frame.origin.x + reactionSelect.frame.width + config.spacing - overlay.bounds.width), y: centerPoint.y)
|
||||
else if selector.frame.origin.x + selector.frame.width + config.spacing > overlay.bounds.width {
|
||||
selector.center = CGPoint(x: centerPoint.x - (selector.frame.origin.x + selector.frame.width + config.spacing - overlay.bounds.width), y: centerPoint.y)
|
||||
}
|
||||
|
||||
reactionSelect.feedback = feedback
|
||||
selector.feedback = feedback
|
||||
|
||||
animateOverlay(alpha: 1, center: CGPoint(x: overlay.bounds.midX, y: overlay.bounds.midY - reactionSelect.bounds.height))
|
||||
animateOverlay(alpha: 1, center: CGPoint(x: overlay.bounds.midX, y: overlay.bounds.midY - selector.bounds.height))
|
||||
}
|
||||
|
||||
private func animateOverlay(alpha: CGFloat, center: CGPoint) {
|
||||
UIView.animate(withDuration: 0.1) { [weak self] in
|
||||
guard let overlay = self?.overlay else { return }
|
||||
|
||||
|
||||
overlay.alpha = alpha
|
||||
overlay.center = center
|
||||
}
|
||||
|
|
|
@ -26,14 +26,26 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public final class ReactionButtonConfig {
|
||||
public typealias ReactionButtonConfigBlock = (ReactionButtonConfig) -> Void
|
||||
/**
|
||||
The reaction button configuration object.
|
||||
*/
|
||||
public final class ReactionButtonConfig: Configurable {
|
||||
/**
|
||||
The builder block.
|
||||
The block gives a reference of receiver you can configure.
|
||||
*/
|
||||
public typealias ConfigurableBlock = (ReactionButtonConfig) -> Void
|
||||
|
||||
/// The spacing between the icon and the text.
|
||||
public var spacing: CGFloat = 8
|
||||
|
||||
/// The marging between the icon and border.
|
||||
public var iconMarging: CGFloat = 4
|
||||
|
||||
/// The font of the text.
|
||||
public var font: UIFont! = UIFont(name: "HelveticaNeue", size: 16)
|
||||
|
||||
/// The color of the text (and image) when no reaction is selected.
|
||||
public var neutralTintColor: UIColor = UIColor(red: 0.47, green: 0.47, blue: 0.47, alpha: 1)
|
||||
|
||||
/**
|
||||
|
@ -43,9 +55,17 @@ public final class ReactionButtonConfig {
|
|||
*/
|
||||
public var alignment: ReactionAlignment = .left
|
||||
|
||||
// MARK: - Initializing a Reaction Button
|
||||
|
||||
// Initialize a configurable with default values.
|
||||
init() {}
|
||||
|
||||
public init(block: ReactionButtonConfigBlock) {
|
||||
|
||||
/**
|
||||
Initialize a configurable with default values.
|
||||
|
||||
- Parameter block: A configurable block to configure itself.
|
||||
*/
|
||||
public init(block: ConfigurableBlock) {
|
||||
block(self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,12 +24,21 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
These constants specify reaction feedback when interacting with the `ReactionSelector`.
|
||||
*/
|
||||
public enum ReactionFeedback {
|
||||
/// Slide finger across feedback
|
||||
case slideFingerAcross
|
||||
/// Release to cancel feedback
|
||||
case releaseToCancel
|
||||
/// Tap to select a reaction feedback
|
||||
case tapToSelectAReaction
|
||||
|
||||
public func localizedString() -> String {
|
||||
// MARK: - Getting a Localized Feedback Description
|
||||
|
||||
/// A string containing the localized description of the feedback.
|
||||
public var localizedString: String {
|
||||
switch self {
|
||||
case .slideFingerAcross:
|
||||
return "Slide finger across"
|
||||
|
|
|
@ -24,6 +24,14 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
The delegate of a `ReactionSelector` object should adopt the `ReactionFeedbackDelegate` protocol. It allows the delegate to know in which state the selector is.
|
||||
*/
|
||||
public protocol ReactionFeedbackDelegate: class {
|
||||
/**
|
||||
Tells the delegate that the feedback did changed.
|
||||
|
||||
- Parameter feedback: The reaction feedback.
|
||||
*/
|
||||
func reactionFeedbackDidChanged(_ feedback: ReactionFeedback?)
|
||||
}
|
||||
|
|
|
@ -27,33 +27,45 @@
|
|||
import UIKit
|
||||
|
||||
/**
|
||||
A `ReactionSelectControl` object is a horizontal control made of multiple reactions, each reaction functioning as a discrete button. A select control affords a compact means to group together a number of reactions.
|
||||
A `ReactionSelector` object is a horizontal control made of multiple reactions, each reaction functioning as a discrete button. A select control affords a compact means to group together a number of reactions.
|
||||
|
||||
The `ReactionSelectControl` object automatically resizes reactions using the `iconSize` and `spacing` values defined in the `config` property.
|
||||
The `ReactionSelector` object automatically resizes reactions using the `iconSize` and `spacing` values defined in the `config` property.
|
||||
|
||||
You can configure/skin the button using a `ReactionSelectorConfig`.
|
||||
*/
|
||||
public final class ReactionSelectControl: UIReactionControl {
|
||||
public final class ReactionSelector: UIReactionControl {
|
||||
private var reactionIconLayers: [CALayer] = []
|
||||
private var reactionLabels: [UILabel] = []
|
||||
private let backgroundLayer = Components.reactionSelect.backgroundLayer()
|
||||
|
||||
/**
|
||||
The reactions available in the selector.
|
||||
*/
|
||||
public var reactions: [Reaction] = Reaction.facebook.all {
|
||||
didSet { setupAndUpdate() }
|
||||
}
|
||||
|
||||
/// The feedback delegate.
|
||||
public weak var feedbackDelegate: ReactionFeedbackDelegate?
|
||||
|
||||
/// The selector feedback state.
|
||||
public internal(set) var feedback: ReactionFeedback? {
|
||||
didSet {
|
||||
if oldValue != feedback { feedbackDelegate?.reactionFeedbackDidChanged(feedback) }
|
||||
}
|
||||
}
|
||||
|
||||
public var config = ReactionSelectControlConfig()
|
||||
|
||||
private var reactionIconLayers: [CALayer] = []
|
||||
private var reactionLabels: [UILabel] = []
|
||||
private let backgroundLayer = Components.reactionSelect.backgroundLayer()
|
||||
/**
|
||||
The reaction selector configuration.
|
||||
*/
|
||||
public var config = ReactionSelectorConfig()
|
||||
|
||||
// MARK: - Managing Internal State
|
||||
|
||||
private var stateHighlightedReactionIndex: Int?
|
||||
private var stateSelectedReaction: Reaction?
|
||||
|
||||
/// The selected reaction.
|
||||
public var selectedReaction: Reaction? {
|
||||
get { return stateSelectedReaction }
|
||||
set {
|
||||
|
@ -81,7 +93,7 @@ public final class ReactionSelectControl: UIReactionControl {
|
|||
|
||||
if backgroundLayer.superlayer == nil {
|
||||
addGestureRecognizer(UILongPressGestureRecognizer().build {
|
||||
$0.addTarget(self, action: #selector(ReactionSelectControl.longPressAction))
|
||||
$0.addTarget(self, action: #selector(ReactionSelector.longPressAction))
|
||||
$0.minimumPressDuration = 0
|
||||
})
|
||||
|
|
@ -26,16 +26,36 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public final class ReactionSelectControlConfig {
|
||||
public typealias ReactionSelectControlConfigBlock = (ReactionSelectControlConfig) -> Void
|
||||
/**
|
||||
The reaction selector configuration object.
|
||||
*/
|
||||
public final class ReactionSelectorConfig: Configurable {
|
||||
/**
|
||||
The builder block.
|
||||
The block gives a reference of receiver you can configure.
|
||||
*/
|
||||
public typealias ConfigurableBlock = (ReactionSelectorConfig) -> Void
|
||||
|
||||
/// The spacing between the icons and borders.
|
||||
public var spacing: CGFloat = 6
|
||||
|
||||
/// The icon size when the selector is inactive.
|
||||
public var iconSize: CGFloat? = nil
|
||||
|
||||
/// Boolean value to know whether the reactions needs to be sticked when they are selected.
|
||||
public var stickyReaction: Bool = false
|
||||
|
||||
// MARK: - Initializing a Reaction Selector
|
||||
|
||||
// Initialize a configurable with default values.
|
||||
init() {}
|
||||
|
||||
public init(block: ReactionSelectControlConfigBlock) {
|
||||
/**
|
||||
Initialize a configurable with default values.
|
||||
|
||||
- Parameter block: A configurable block to configure itself.
|
||||
*/
|
||||
public init(block: ConfigurableBlock) {
|
||||
block(self)
|
||||
}
|
||||
|
|
@ -26,21 +26,34 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
A `ReactionSummary` component aims to display a list of reactions as a thumbnail associate to a text description.
|
||||
|
||||
You can configure/skin the summary using a `ReactionSummaryConfig`.
|
||||
*/
|
||||
public final class ReactionSummary: UIReactionControl {
|
||||
private let textLabel: UILabel = Components.reactionSummary.facebookSummaryLabel()
|
||||
private let textLabel: UILabel = UILabel()
|
||||
private var reactionIconLayers: [CALayer] = []
|
||||
|
||||
private let spacing: CGFloat = 8
|
||||
|
||||
public var font: UIFont! {
|
||||
get { return textLabel.font }
|
||||
set {
|
||||
textLabel.font = newValue
|
||||
|
||||
update()
|
||||
}
|
||||
/**
|
||||
The reaction summary configuration.
|
||||
*/
|
||||
public var config: ReactionSummaryConfig = ReactionSummaryConfig() {
|
||||
didSet { setupAndUpdate() }
|
||||
}
|
||||
|
||||
/**
|
||||
The reactions to summarize.
|
||||
*/
|
||||
public var reactions: [Reaction] = [] {
|
||||
didSet { setupAndUpdate() }
|
||||
}
|
||||
|
||||
/**
|
||||
The text displayed by the reaction summary.
|
||||
|
||||
This string is nil by default.
|
||||
*/
|
||||
public var text: String? {
|
||||
get { return textLabel.text }
|
||||
set {
|
||||
|
@ -50,19 +63,6 @@ public final class ReactionSummary: UIReactionControl {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The technique to use for aligning the icon and the text.
|
||||
|
||||
The default value of this property is left.
|
||||
*/
|
||||
public var alignment: ReactionAlignment = .left {
|
||||
didSet { update() }
|
||||
}
|
||||
|
||||
public var reactions: [Reaction] = [] {
|
||||
didSet { setupAndUpdate() }
|
||||
}
|
||||
|
||||
// MARK: - Building Object
|
||||
|
||||
override func setup() {
|
||||
|
@ -92,28 +92,21 @@ public final class ReactionSummary: UIReactionControl {
|
|||
// MARK: - Updating Object State
|
||||
|
||||
override func update() {
|
||||
let textSize = textLabel.sizeThatFits(CGSize(width: bounds.width, height: bounds.height))
|
||||
let iconSize = min(bounds.height, textSize.height + 4)
|
||||
let iconWidth = (iconSize - 3) * CGFloat(reactionIconLayers.count) + spacing
|
||||
let margin = (bounds.width - iconWidth - textSize.width) / 2
|
||||
textLabel.font = config.font
|
||||
textLabel.textColor = config.textColor
|
||||
|
||||
for (index, l) in reactionIconLayers.enumerated() {
|
||||
let x: CGFloat
|
||||
let textSize = textLabel.sizeThatFits(CGSize(width: bounds.width, height: bounds.height))
|
||||
let iconSize = min(bounds.height, textSize.height + 4)
|
||||
let iconWidth = (iconSize - 3) * CGFloat(reactionIconLayers.count) + config.spacing
|
||||
let margin = (bounds.width - iconWidth - textSize.width) / 2
|
||||
|
||||
switch alignment {
|
||||
case .left: x = (iconSize - 3) * CGFloat(index)
|
||||
case .right: x = bounds.width - iconSize - (iconSize - 3) * CGFloat(index)
|
||||
case .centerLeft: x = margin + (iconSize - 3) * CGFloat(index)
|
||||
case .centerRight: x = bounds.width - iconSize - (iconSize - 3) * CGFloat(index) - margin
|
||||
}
|
||||
|
||||
l.frame = CGRect(x: x, y: (bounds.height - iconSize) / 2, width: iconSize, height: iconSize)
|
||||
l.cornerRadius = iconSize / 2
|
||||
for index in 0 ..< reactionIconLayers.count {
|
||||
updateIconAtIndex(index, with: iconSize, margin: margin)
|
||||
}
|
||||
|
||||
let textX: CGFloat
|
||||
|
||||
switch alignment {
|
||||
switch config.alignment {
|
||||
case .left: textX = iconWidth
|
||||
case .right: textX = bounds.width - iconWidth - textSize.width
|
||||
case .centerLeft: textX = margin + iconWidth
|
||||
|
@ -123,6 +116,21 @@ public final class ReactionSummary: UIReactionControl {
|
|||
textLabel.frame = CGRect(x: textX, y: 0, width: textSize.width, height: bounds.height)
|
||||
}
|
||||
|
||||
private func updateIconAtIndex(_ index: Int, with size: CGFloat, margin: CGFloat) {
|
||||
let x: CGFloat
|
||||
let layer = reactionIconLayers[index]
|
||||
|
||||
switch config.alignment {
|
||||
case .left: x = (size - 3) * CGFloat(index)
|
||||
case .right: x = bounds.width - size - (size - 3) * CGFloat(index)
|
||||
case .centerLeft: x = margin + (size - 3) * CGFloat(index)
|
||||
case .centerRight: x = bounds.width - size - (size - 3) * CGFloat(index) - margin
|
||||
}
|
||||
|
||||
layer.frame = CGRect(x: x, y: (bounds.height - size) / 2, width: size, height: size)
|
||||
layer.cornerRadius = size / 2
|
||||
}
|
||||
|
||||
// MARK: - Responding to Gesture Events
|
||||
|
||||
func tapAction(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Reactions
|
||||
*
|
||||
* Copyright 2016-present Yannick Loriot.
|
||||
* http://yannickloriot.com
|
||||
*
|
||||
* 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
|
||||
|
||||
/**
|
||||
The reaction summary configuration object.
|
||||
*/
|
||||
public final class ReactionSummaryConfig: Configurable {
|
||||
/**
|
||||
The builder block.
|
||||
The block gives a reference of receiver you can configure.
|
||||
*/
|
||||
public typealias ConfigurableBlock = (ReactionSummaryConfig) -> Void
|
||||
|
||||
/// The spacing between the icons and the text.
|
||||
public var spacing: CGFloat = 8
|
||||
|
||||
/// The font of the text.
|
||||
public var font: UIFont! = UIFont(name: "HelveticaNeue", size: 14)
|
||||
|
||||
/// The color of the text.
|
||||
public var textColor: UIColor! = UIColor(red: 0.47, green: 0.47, blue: 0.47, alpha: 1)
|
||||
|
||||
/**
|
||||
The technique to use for aligning the icon and the text.
|
||||
|
||||
The default value of this property is left.
|
||||
*/
|
||||
public var alignment: ReactionAlignment = .left
|
||||
|
||||
// MARK: - Initializing a Reaction Summary
|
||||
|
||||
// Initialize a configurable with default values.
|
||||
init() {}
|
||||
|
||||
/**
|
||||
Initialize a configurable with default values.
|
||||
|
||||
- Parameter block: A configurable block to configure itself.
|
||||
*/
|
||||
public init(block: ConfigurableBlock) {
|
||||
block(self)
|
||||
}
|
||||
}
|
|
@ -26,7 +26,9 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
/// Convenient projet extension
|
||||
extension Sequence where Iterator.Element: Hashable {
|
||||
/// Returns uniq elements in the sequence by keeping the element order.
|
||||
func uniq() -> [Iterator.Element] {
|
||||
var alreadySeen: [Iterator.Element: Bool] = [:]
|
||||
|
||||
|
|
|
@ -26,16 +26,26 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
The `UIReactionControl` class implements common behavior for reaction elements. It mainly defines two methods:
|
||||
|
||||
- `setup`: Manage the view hierarchy by adding and/or removing elements.
|
||||
- `update`: Layout the view hierarchy and update state.
|
||||
|
||||
You should override these methods if you subclass the `UIReactionControl`.
|
||||
*/
|
||||
public class UIReactionControl: UIControl {
|
||||
// MARK: - Initializing a ReactionSelect Object
|
||||
|
||||
override init(frame: CGRect) {
|
||||
/// Initializes and returns a newly allocated view object with the specified frame rectangle.
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
setupAndUpdate()
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
/// Returns an object initialized from data in a given unarchiver.
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
setupAndUpdate()
|
||||
|
@ -43,12 +53,14 @@ public class UIReactionControl: UIControl {
|
|||
|
||||
// MARK: - Laying out Subviews
|
||||
|
||||
/// Lays out subviews.
|
||||
public override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
update()
|
||||
}
|
||||
|
||||
/// Called when a designable object is created in Interface Builder.
|
||||
public override func prepareForInterfaceBuilder() {
|
||||
super.prepareForInterfaceBuilder()
|
||||
|
||||
|
@ -57,8 +69,10 @@ public class UIReactionControl: UIControl {
|
|||
|
||||
// MARK: - Building Object
|
||||
|
||||
/// Setup the view hierarchy
|
||||
func setup() {}
|
||||
|
||||
/// Call the setup then the update method
|
||||
final func setupAndUpdate() {
|
||||
setup()
|
||||
|
||||
|
@ -69,5 +83,6 @@ public class UIReactionControl: UIControl {
|
|||
|
||||
// MARK: - Updating Object State
|
||||
|
||||
/// Update the state and layout the view hierarchy
|
||||
func update() {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue