Commit 42b6d525 by Daniel Dahan

development: added delegation methods to FABMenu

parent 9875036d
......@@ -42,8 +42,8 @@ open class Button: UIButton, PulseableMotion {
/// A Pulse reference.
fileprivate var pulse: PulseMotion!
/// PulseMotionAnimation value.
open var pulseAnimation: PulseMotionAnimation {
/// PulseAnimation value.
open var pulseAnimation: PulseAnimation {
get {
return pulse.animation
}
......@@ -52,7 +52,7 @@ open class Button: UIButton, PulseableMotion {
}
}
/// PulseMotionAnimation color.
/// PulseAnimation color.
@IBInspectable
open var pulseColor: UIColor {
get {
......
......@@ -43,8 +43,8 @@ open class CollectionReusableView: UICollectionReusableView, PulseableMotion {
/// A Pulse reference.
fileprivate var pulse: PulseMotion!
/// PulseMotionAnimation value.
open var pulseAnimation: PulseMotionAnimation {
/// PulseAnimation value.
open var pulseAnimation: PulseAnimation {
get {
return pulse.animation
}
......@@ -53,7 +53,7 @@ open class CollectionReusableView: UICollectionReusableView, PulseableMotion {
}
}
/// PulseMotionAnimation color.
/// PulseAnimation color.
@IBInspectable
open var pulseColor: UIColor {
get {
......
......@@ -43,8 +43,8 @@ open class CollectionViewCell: UICollectionViewCell, PulseableMotion {
/// A Pulse reference.
fileprivate var pulse: PulseMotion!
/// PulseMotionAnimation value.
open var pulseAnimation: PulseMotionAnimation {
/// PulseAnimation value.
open var pulseAnimation: PulseAnimation {
get {
return pulse.animation
}
......@@ -53,7 +53,7 @@ open class CollectionViewCell: UICollectionViewCell, PulseableMotion {
}
}
/// PulseMotionAnimation color.
/// PulseAnimation color.
@IBInspectable
open var pulseColor: UIColor {
get {
......
......@@ -42,7 +42,6 @@ open class FABButton: Button {
super.prepare()
depthPreset = .depth1
shapePreset = .circle
pulseAnimation = .centerWithBacking
backgroundColor = .white
}
}
......@@ -41,14 +41,43 @@ public enum FABMenuDirection: Int {
@objc(FABMenuDelegate)
public protocol FABMenuDelegate {
/**
Gets called when the user taps while the menu is opened.
- Parameter menu: A FABMenu.
A delegation method that is executed when the user taps while
the menu is opened.
- Parameter fabMenu: A FABMenu.
- Parameter tappedAt point: A CGPoint.
- Parameter isOutside: A boolean indicating whether the tap
was outside the menu button area.
*/
@objc
optional func fabMenu(fabMenu: FABMenu, tappedAt point: CGPoint, isOutside: Bool)
/**
A delegation method that is execited when the menu will open.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuWillOpen(fabMenu: FABMenu)
/**
A delegation method that is execited when the menu did open.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuDidOpen(fabMenu: FABMenu)
/**
A delegation method that is execited when the menu will close.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuWillClose(fabMenu: FABMenu)
/**
A delegation method that is execited when the menu did close.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuDidClose(fabMenu: FABMenu)
}
......@@ -58,21 +87,27 @@ open class FABMenu: View, SpringableMotion {
internal let spring = SpringMotion()
/// The direction in which the animation opens the menu.
open var springDirection = SpringMotionDirection.up {
open var springDirection = SpringDirection.up {
didSet {
layoutSubviews()
}
}
open var baseSize: CGSize {
get {
return spring.baseSize
}
set(value) {
spring.baseSize = value
/// A reference to the base FABButton.
open var fabButton: FABButton? {
didSet {
oldValue?.removeFromSuperview()
guard let v = fabButton else {
return
}
addSubview(v)
v.addTarget(self, action: #selector(handleToggleMenu(button:)), for: .touchUpInside)
}
}
/// Size of FABMenuItems.
open var itemSize: CGSize {
get {
return spring.itemSize
......@@ -82,41 +117,43 @@ open class FABMenu: View, SpringableMotion {
}
}
open var isOpened: Bool {
/// A preset wrapper around interimSpace.
open var interimSpacePreset: InterimSpacePreset {
get {
return spring.isOpened
return spring.interimSpacePreset
}
set(value) {
spring.isOpened = value
spring.interimSpacePreset = value
}
}
open var isEnable: Bool {
/// The space between views.
open var interimSpace: InterimSpace {
get {
return spring.isEnabled
return spring.interimSpace
}
set(value) {
spring.isEnabled = value
spring.interimSpace = value
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset: InterimSpacePreset {
/// A boolean indicating if the menu is open or not.
open var isOpened: Bool {
get {
return spring.interimSpacePreset
return spring.isOpened
}
set(value) {
spring.interimSpacePreset = value
spring.isOpened = value
}
}
/// The space between views.
open var interimSpace: InterimSpace {
/// A boolean indicating if the menu is enabled.
open var isEnable: Bool {
get {
return spring.interimSpace
return spring.isEnabled
}
set(value) {
spring.interimSpace = value
spring.isEnabled = value
}
}
......@@ -141,6 +178,12 @@ open class FABMenu: View, SpringableMotion {
}
}
open override func layoutSubviews() {
super.layoutSubviews()
fabButton?.frame.size = bounds.size
spring.baseSize = bounds.size
}
open override func prepare() {
super.prepare()
backgroundColor = nil
......@@ -150,6 +193,36 @@ open class FABMenu: View, SpringableMotion {
extension FABMenu {
/**
Open the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func open(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.expand(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
/**
Close the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func close(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.contract(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
}
extension FABMenu {
/**
Handles the hit test for the Menu and views outside of the Menu bounds.
- Parameter _ point: A CGPoint.
- Parameter with event: An optional UIEvent.
......@@ -170,36 +243,58 @@ extension FABMenu {
delegate?.fabMenu?(fabMenu: self, tappedAt: point, isOutside: true)
closeMenu()
return self.hitTest(point, with: event)
}
}
extension FABMenu {
/**
Open the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
Handler to toggle the FABMenu open or close.
- Parameter button: A UIButton.
*/
open func open(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.expand(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
@objc
fileprivate func handleToggleMenu(button: UIButton) {
guard isOpened else {
openMenu()
return
}
closeMenu()
}
}
extension FABMenu {
/// Opens the menu and reveals the FABMenuItems.
fileprivate func openMenu() {
delegate?.fabMenuWillOpen?(fabMenu: self)
open { [weak self] (view) in
guard let s = self else {
return
}
(view as? FABMenuItem)?.showTitleLabel()
if view == s.items.last {
s.delegate?.fabMenuDidOpen?(fabMenu: s)
}
}
}
/**
Close the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func close(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.contract(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
/// Closes the menu and hides the FABMenuItems.
fileprivate func closeMenu() {
delegate?.fabMenuWillClose?(fabMenu: self)
close { [weak self] (view) in
guard let s = self else {
return
}
(view as? FABMenuItem)?.hideTitleLabel()
if view == s.items.last {
s.delegate?.fabMenuDidClose?(fabMenu: s)
}
}
}
}
......@@ -51,7 +51,7 @@ extension UIViewController {
open class FABMenuController: RootController {
/// Reference to the MenuView.
@IBInspectable
open let menu = FABMenu()
open let fabMenu = FABMenu()
open override func layoutSubviews() {
super.layoutSubviews()
......@@ -67,67 +67,68 @@ open class FABMenuController: RootController {
*/
open override func prepare() {
super.prepare()
prepareMenu()
prepareFABMenu()
}
}
extension FABMenuController {
/// Prepares the Menu.
fileprivate func prepareMenu() {
menu.zPosition = 1000
view.addSubview(menu)
/// Prepares the fabMenu.
fileprivate func prepareFABMenu() {
fabMenu.delegate = self
fabMenu.zPosition = 1000
view.addSubview(fabMenu)
}
}
extension FABMenuController {
/**
Opens the menu with a callback.
- Parameter completion: An Optional callback that is executed when
all menu items have been opened.
*/
open func openMenu(completion: ((UIView) -> Void)? = nil) {
if true == isUserInteractionEnabled {
isUserInteractionEnabled = false
UIView.animate(withDuration: 0.15, animations: { [weak self] in
guard let s = self else {
return
}
s.rootViewController.view.alpha = 0.15
})
menu.open { [completion = completion] (view) in
completion?(view)
}
}
}
extension FABMenuController: FABMenuDelegate {
/**
Opens the menu with a callback.
- Parameter completion: An Optional callback that is executed when
all menu items have been closed.
*/
open func closeMenu(completion: ((UIView) -> Void)? = nil) {
if false == isUserInteractionEnabled {
UIView.animate(withDuration: 0.15, animations: { [weak self] in
guard let s = self else {
return
}
s.rootViewController.view.alpha = 1
})
menu.close { [weak self] (view) in
guard let s = self else {
return
}
completion?(view)
if view == s.menu.items.last {
s.isUserInteractionEnabled = true
}
}
}
}
// /**
// Opens the menu with a callback.
// - Parameter completion: An Optional callback that is executed when
// all menu items have been opened.
// */
// open func openMenu(completion: ((UIView) -> Void)? = nil) {
// if true == isUserInteractionEnabled {
// isUserInteractionEnabled = false
//
// UIView.animate(withDuration: 0.15, animations: { [weak self] in
// guard let s = self else {
// return
// }
// s.rootViewController.view.alpha = 0.15
// })
//
// fabMenu.open { [completion = completion] (view) in
// completion?(view)
// }
// }
// }
//
// /**
// Opens the menu with a callback.
// - Parameter completion: An Optional callback that is executed when
// all menu items have been closed.
// */
// open func closeMenu(completion: ((UIView) -> Void)? = nil) {
// if false == isUserInteractionEnabled {
// UIView.animate(withDuration: 0.15, animations: { [weak self] in
// guard let s = self else {
// return
// }
// s.rootViewController.view.alpha = 1
// })
//
// fabMenu.close { [weak self] (view) in
// guard let s = self else {
// return
// }
//
// completion?(view)
//
// if view == s.fabMenu.items.last {
// s.isUserInteractionEnabled = true
// }
// }
// }
// }
}
......@@ -61,7 +61,7 @@ open class Menu: View, SpringableMotion {
open weak var delegate: MenuDelegate?
/// The direction in which the animation opens the menu.
open var springDirection = SpringMotionDirection.up {
open var springDirection = SpringDirection.up {
didSet {
layoutSubviews()
}
......
......@@ -30,8 +30,8 @@
import UIKit
@objc(PulseMotionAnimation)
public enum PulseMotionAnimation: Int {
@objc(PulseAnimation)
public enum PulseAnimation: Int {
case none
case center
case centerWithBacking
......@@ -43,8 +43,8 @@ public enum PulseMotionAnimation: Int {
}
public protocol PulseableMotion {
/// A reference to the PulseMotionAnimation.
var pulseAnimation: PulseMotionAnimation { get set }
/// A reference to the PulseAnimation.
var pulseAnimation: PulseAnimation { get set }
/// A UIColor.
var pulseColor: UIColor { get set }
......@@ -63,8 +63,8 @@ public struct PulseMotion {
/// Pulse layers.
fileprivate var layers = [CAShapeLayer]()
/// A reference to the PulseMotionAnimation.
public var animation = PulseMotionAnimation.pointWithBacking
/// A reference to the PulseAnimation.
public var animation = PulseAnimation.pointWithBacking
/// A UIColor.
public var color = Color.grey.base
......
......@@ -34,8 +34,8 @@ open class PulseView: View, PulseableMotion {
/// A Pulse reference.
fileprivate var pulse: PulseMotion!
/// PulseMotionAnimation value.
open var pulseAnimation: PulseMotionAnimation {
/// PulseAnimation value.
open var pulseAnimation: PulseAnimation {
get {
return pulse.animation
}
......@@ -44,7 +44,7 @@ open class PulseView: View, PulseableMotion {
}
}
/// PulseMotionAnimation color.
/// PulseAnimation color.
@IBInspectable
open var pulseColor: UIColor {
get {
......
......@@ -30,8 +30,8 @@
import UIKit
@objc(SpringMotionDirection)
public enum SpringMotionDirection: Int {
@objc(SpringDirection)
public enum SpringDirection: Int {
case up
case down
case left
......@@ -39,13 +39,13 @@ public enum SpringMotionDirection: Int {
}
public protocol SpringableMotion {
/// A SpringMotionDirection value.
var springDirection: SpringMotionDirection { get set }
/// A SpringDirection value.
var springDirection: SpringDirection { get set }
}
open class SpringMotion {
/// A SpringMotionDirection value.
open var direction = SpringMotionDirection.up
/// A SpringDirection value.
open var direction = SpringDirection.up
/// A Boolean that indicates if the menu is open or not.
open var isOpened = false
......@@ -92,20 +92,13 @@ open class SpringMotion {
open func reload() {
isOpened = false
guard let first = views.first else {
return
}
first.frame.size = baseSize
first.zPosition = 10000
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
v.alpha = 0
v.isHidden = true
v.frame.size = itemSize
v.x = first.x + (baseSize.width - itemSize.width) / 2
v.y = first.y + (baseSize.height - itemSize.height) / 2
v.x = (baseSize.width - itemSize.width) / 2
v.y = (baseSize.height - itemSize.height) / 2
v.zPosition = CGFloat(10000 - views.count - i)
}
}
......@@ -239,11 +232,7 @@ extension SpringMotion {
- Parameter completion: A completion block to execute on each view's animation.
*/
fileprivate func expandUp(duration: TimeInterval, delay: TimeInterval, usingSpringWithDamping: CGFloat, initialSpringVelocity: CGFloat, options: UIViewAnimationOptions, animations: ((UIView) -> Void)?, completion: ((UIView) -> Void)?) {
guard let first = views.first else {
return
}
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
v.isHidden = false
......@@ -252,10 +241,9 @@ extension SpringMotion {
usingSpringWithDamping: usingSpringWithDamping,
initialSpringVelocity: initialSpringVelocity,
options: options,
animations: { [s = self, first = first, v = v] in
animations: { [s = interimSpace, m = CGFloat(i + 1), v = v] in
v.alpha = 1
v.y = first.y - CGFloat(i) * v.height - CGFloat(i) * s.interimSpace
v.y = -m * (v.height + s)
animations?(v)
}) { [weak self, v = v] _ in
self?.handleOpenCompletion(view: v, completion: completion)
......@@ -274,11 +262,7 @@ extension SpringMotion {
- Parameter completion: A completion block to execute on each view's animation.
*/
fileprivate func contractUp(duration: TimeInterval, delay: TimeInterval, usingSpringWithDamping: CGFloat, initialSpringVelocity: CGFloat, options: UIViewAnimationOptions, animations: ((UIView) -> Void)?, completion: ((UIView) -> Void)?) {
guard let first = views.first else {
return
}
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
UIView.animate(withDuration: Double(i) * duration,
......@@ -286,10 +270,9 @@ extension SpringMotion {
usingSpringWithDamping: usingSpringWithDamping,
initialSpringVelocity: initialSpringVelocity,
options: options,
animations: { [base = first, v = v] in
animations: { [v = v] in
v.alpha = 0
v.y = base.y
v.y = 0
animations?(v)
}) { [weak self, v = v] _ in
self?.handleCloseCompletion(view: v, completion: completion)
......@@ -314,7 +297,7 @@ extension SpringMotion {
let h = baseSize.height
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
v.isHidden = false
......@@ -351,7 +334,7 @@ extension SpringMotion {
let h = baseSize.height
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
UIView.animate(withDuration: Double(i) * duration,
......@@ -385,7 +368,7 @@ extension SpringMotion {
return
}
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
v.isHidden = false
......@@ -420,7 +403,7 @@ extension SpringMotion {
return
}
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
UIView.animate(withDuration: Double(i) * duration,
......@@ -456,7 +439,7 @@ extension SpringMotion {
let h = baseSize.height
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
v.isHidden = false
......@@ -493,7 +476,7 @@ extension SpringMotion {
let w = baseSize.width
for i in 1..<views.count {
for i in 0..<views.count {
let v = views[i]
UIView.animate(withDuration: Double(i) * duration,
......
......@@ -42,8 +42,8 @@ open class TableViewCell: UITableViewCell, PulseableMotion {
/// A Pulse reference.
fileprivate var pulse: PulseMotion!
/// PulseMotionAnimation value.
open var pulseAnimation: PulseMotionAnimation {
/// PulseAnimation value.
open var pulseAnimation: PulseAnimation {
get {
return pulse.animation
}
......@@ -52,7 +52,7 @@ open class TableViewCell: UITableViewCell, PulseableMotion {
}
}
/// PulseMotionAnimation color.
/// PulseAnimation color.
@IBInspectable
open var pulseColor: UIColor {
get {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment