Commit ee348867 by Daniel Dahan

development: updating Menu and Pulseable

parent 1fd7ceeb
......@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
961730361E0E156400A9A297 /* SpringMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961730351E0E156400A9A297 /* SpringMotion.swift */; };
9617304C1E11931400A9A297 /* MenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9617304B1E11931400A9A297 /* MenuItem.swift */; };
9617B07D1DFCA8CF00410F8F /* Application.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961E6BDE1DDA2A95004E6C93 /* Application.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B07E1DFCA8CF00410F8F /* Card.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB75D1CB40DC500C806FE /* Card.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B07F1DFCA8CF00410F8F /* ImageCard.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96BCB7621CB40DC500C806FE /* ImageCard.swift */; settings = {ATTRIBUTES = (Public, ); }; };
......@@ -230,6 +231,7 @@
/* Begin PBXFileReference section */
961276621DCD8B1800A7D920 /* CharacterAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterAttribute.swift; sourceTree = "<group>"; };
961730351E0E156400A9A297 /* SpringMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringMotion.swift; sourceTree = "<group>"; };
9617304B1E11931400A9A297 /* MenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuItem.swift; sourceTree = "<group>"; };
961DED451DCC40C500F425B6 /* Editor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = "<group>"; };
961DED4A1DCC546100F425B6 /* EditorController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditorController.swift; sourceTree = "<group>"; };
961E6BDE1DDA2A95004E6C93 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
......@@ -549,6 +551,7 @@
isa = PBXGroup;
children = (
96BCB78E1CB40DC500C806FE /* Menu.swift */,
9617304B1E11931400A9A297 /* MenuItem.swift */,
96BCB78F1CB40DC500C806FE /* MenuController.swift */,
96A183621E0C6CE200083C30 /* FABMenu.swift */,
9697F7B61D8F22A4004741EC /* FABMenuItem.swift */,
......@@ -1159,6 +1162,7 @@
965E81261DD4D7C800D61E4B /* CharacterAttribute.swift in Sources */,
965E80FF1DD4D5C800D61E4B /* BottomNavigationController.swift in Sources */,
965E81001DD4D5C800D61E4B /* Capture.swift in Sources */,
9617304C1E11931400A9A297 /* MenuItem.swift in Sources */,
965E81011DD4D5C800D61E4B /* CapturePreview.swift in Sources */,
965E81021DD4D5C800D61E4B /* CaptureController.swift in Sources */,
965E81031DD4D5C800D61E4B /* CollectionView.swift in Sources */,
......
......@@ -30,7 +30,7 @@
import UIKit
open class Button: UIButton, PulseableMotion {
open class Button: UIButton, Pulseable {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
......
......@@ -31,7 +31,7 @@
import UIKit
@objc(CollectionReusableView)
open class CollectionReusableView: UICollectionReusableView, PulseableMotion {
open class CollectionReusableView: UICollectionReusableView, Pulseable {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
......
......@@ -31,7 +31,7 @@
import UIKit
@objc(CollectionViewCell)
open class CollectionViewCell: UICollectionViewCell, PulseableMotion {
open class CollectionViewCell: UICollectionViewCell, Pulseable {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
......
......@@ -82,13 +82,35 @@ public protocol FABMenuDelegate {
@objc(FABMenu)
open class FABMenu: View, SpringableMotion {
open class FABMenu: View {
/// A reference to the SpringMotion object.
internal let spring = SpringMotion()
/// The direction in which the animation opens the menu.
open var springDirection = SpringDirection.up {
didSet {
open var direction: FABMenuDirection {
get {
switch spring.direction {
case .up:
return .up
case .down:
return .down
case .left:
return .left
case .right:
return .right
}
}
set(value) {
switch value {
case .up:
spring.direction = .up
case .down:
spring.direction = .down
case .left:
spring.direction = .left
case .right:
spring.direction = .right
}
layoutSubviews()
}
}
......@@ -279,7 +301,7 @@ extension FABMenu {
extension FABMenu {
/**
Handler to toggle the FABMenu open or close.
Handler to toggle the FABMenu opened or closed.
- Parameter button: A UIButton.
*/
@objc
......
......@@ -82,12 +82,9 @@ public protocol MenuDelegate {
@objc(Menu)
open class Menu: View, SpringableMotion {
/// A reference to the SpringMotion object.
internal let spring = SpringMotion()
open class Menu: View {
/// The direction in which the animation opens the menu.
open var springDirection = SpringDirection.up {
open var direction = MenuDirection.up {
didSet {
layoutSubviews()
}
......@@ -96,171 +93,19 @@ open class Menu: View, SpringableMotion {
/// A reference to the base UIButton.
open var button: UIButton? {
didSet {
oldValue?.removeFromSuperview()
guard let v = button else {
return
}
addSubview(v)
v.addTarget(self, action: #selector(handleToggleMenu(button:)), for: .touchUpInside)
}
}
/// Size of MenuItems.
open var itemSize: CGSize {
get {
return spring.itemSize
}
set(value) {
spring.itemSize = value
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset: InterimSpacePreset {
get {
return spring.interimSpacePreset
}
set(value) {
spring.interimSpacePreset = value
oldValue?.removeTarget(self, action: #selector(handleToggleMenu), for: .touchUpInside)
button?.addTarget(self, action: #selector(handleToggleMenu), for: .touchUpInside)
}
}
/// The space between views.
open var interimSpace: InterimSpace {
get {
return spring.interimSpace
}
set(value) {
spring.interimSpace = value
}
}
/// A boolean indicating if the menu is open or not.
open var isOpened: Bool {
get {
return spring.isOpened
}
set(value) {
spring.isOpened = value
}
}
/// A boolean indicating if the menu is enabled.
open var isEnabled: Bool {
get {
return spring.isEnabled
}
set(value) {
spring.isEnabled = value
}
}
/// An optional delegation handler.
open weak var delegate: MenuDelegate?
/// A reference to the MenuItems
open var items: [UIView] {
get {
return spring.views as! [UIView]
}
set(value) {
for v in spring.views {
v.removeFromSuperview()
}
for v in value {
addSubview(v)
}
spring.views = value
}
}
open override func layoutSubviews() {
super.layoutSubviews()
button?.frame.size = bounds.size
spring.baseSize = bounds.size
}
open override func prepare() {
super.prepare()
backgroundColor = nil
interimSpacePreset = .interimSpace6
}
}
extension Menu {
/**
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.
*/
fileprivate 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.
*/
fileprivate 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 Menu {
/**
Handles the hit test for the Menu and views outside of the Menu bounds.
- Parameter _ point: A CGPoint.
- Parameter with event: An optional UIEvent.
- Returns: An optional UIView.
*/
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard isOpened, isEnabled else {
return super.hitTest(point, with: event)
}
for v in subviews {
let p = v.convert(point, from: self)
if v.bounds.contains(p) {
delegate?.menu?(menu: self, tappedAt: point, isOutside: false)
return v.hitTest(p, with: event)
}
}
delegate?.menu?(menu: self, tappedAt: point, isOutside: true)
close()
return self.hitTest(point, with: event)
}
}
extension Menu {
/**
Handler to toggle the Menu open or close.
Handler to toggle the Menu opened or closed.
- Parameter button: A UIButton.
*/
@objc
fileprivate func handleToggleMenu(button: UIButton) {
guard isOpened else {
open()
return
}
close()
}
}
......@@ -88,110 +88,7 @@ open class MenuController: RootController {
extension MenuController {
/// Prepares the menu.
fileprivate func prepareMenu() {
menu.delegate = self
menu.zPosition = 1000
view.addSubview(menu)
}
}
extension MenuController {
/// Shows the menuBacking.
fileprivate func showFabMenuBacking() {
showFade()
showBlurView()
}
/// Hides the menuBacking.
fileprivate func hideFabMenuBacking() {
hideFade()
hideBlurView()
}
/// Shows the blurView.
fileprivate func showBlurView() {
guard .blur == menuBacking else {
return
}
guard !menu.isOpened, menu.isEnabled else {
return
}
guard nil == blurView else {
return
}
let blur = UIVisualEffectView(effect: UIBlurEffect(style: menuBackingBlurEffectStyle))
blurView = UIView()
blurView?.layout(blur).edges()
view.layout(blurView!).edges()
view.bringSubview(toFront: menu)
}
/// Hides the blurView.
fileprivate func hideBlurView() {
guard menu.isOpened, menu.isEnabled else {
return
}
blurView?.removeFromSuperview()
blurView = nil
}
/// Shows the fade.
fileprivate func showFade() {
guard .fade == menuBacking else {
return
}
guard !menu.isOpened, menu.isEnabled else {
return
}
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.rootViewController.view.alpha = 0.15
})
}
/// Hides the fade.
fileprivate func hideFade() {
guard menu.isOpened, menu.isEnabled else {
return
}
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.rootViewController.view.alpha = 1
})
}
}
extension MenuController: MenuDelegate {
@objc
open func menuWillOpen(menu: Menu) {
isUserInteractionEnabled = false
showFabMenuBacking()
}
@objc
open func menuDidOpen(menu: Menu) {
isUserInteractionEnabled = true
}
@objc
open func menuWillClose(menu: Menu) {
isUserInteractionEnabled = false
hideFabMenuBacking()
}
@objc
open func menuDidClose(menu: Menu) {
isUserInteractionEnabled = true
}
@objc
open func menu(menu: Menu, tappedAt point: CGPoint, isOutside: Bool) {
guard isOutside else {
return
}
}
}
/*
* Copyright (C) 2015 - 2016, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
open class MenuItem: View {
/// A reference to the titleLabel.
open let titleLabel = UILabel()
/// A reference to the button.
open let button = FABButton()
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() {
super.prepare()
backgroundColor = nil
prepareButton()
prepareTitleLabel()
}
/// A reference to the titleLabel text.
open var title: String? {
get {
return titleLabel.text
}
set(value) {
titleLabel.text = value
layoutSubviews()
}
}
open override func layoutSubviews() {
super.layoutSubviews()
guard let t = title, 0 < t.utf16.count else {
titleLabel.removeFromSuperview()
return
}
if nil == titleLabel.superview {
addSubview(titleLabel)
}
}
}
extension MenuItem {
/// Shows the titleLabel.
open func showTitleLabel() {
let interimSpace = InterimSpacePresetToValue(preset: .interimSpace6)
titleLabel.sizeToFit()
titleLabel.width += 1.5 * interimSpace
titleLabel.height += interimSpace / 2
titleLabel.y = (height - titleLabel.height) / 2
titleLabel.x = -titleLabel.width - interimSpace
titleLabel.alpha = 0
titleLabel.isHidden = false
UIView.animate(withDuration: 0.25, animations: { [weak self] in
guard let s = self else {
return
}
s.titleLabel.alpha = 1
})
}
/// Hides the titleLabel.
open func hideTitleLabel() {
titleLabel.isHidden = true
}
}
extension MenuItem {
/// Prepares the button.
fileprivate func prepareButton() {
layout(button).edges()
}
/// Prepares the titleLabel.
fileprivate func prepareTitleLabel() {
titleLabel.font = RobotoFont.regular(with: 14)
titleLabel.textAlignment = .center
titleLabel.backgroundColor = .white
titleLabel.depthPreset = button.depthPreset
titleLabel.cornerRadiusPreset = .cornerRadius1
}
}
......@@ -42,7 +42,7 @@ public enum PulseAnimation: Int {
case pointWithBacking
}
public protocol PulseableMotion {
public protocol Pulseable {
/// A reference to the PulseAnimation.
var pulseAnimation: PulseAnimation { get set }
......@@ -54,7 +54,7 @@ public protocol PulseableMotion {
}
public struct PulseMotion {
/// A UIView that is PulseableMotion.
/// A UIView that is Pulseable.
fileprivate weak var pulseView: UIView?
/// The layer the pulse layers are added to.
......
......@@ -30,7 +30,7 @@
import UIKit
open class PulseView: View, PulseableMotion {
open class PulseView: View, Pulseable {
/// A Pulse reference.
fileprivate var pulse: PulseMotion!
......
......@@ -38,11 +38,6 @@ public enum SpringDirection: Int {
case right
}
public protocol SpringableMotion {
/// A SpringDirection value.
var springDirection: SpringDirection { get set }
}
open class SpringMotion {
/// A SpringDirection value.
open var direction = SpringDirection.up
......
......@@ -30,7 +30,7 @@
import UIKit
open class TableViewCell: UITableViewCell, PulseableMotion {
open class TableViewCell: UITableViewCell, Pulseable {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
......
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