Commit 510173b2 by Daniel Dahan

added base MotionDynamics

parent 7ba79cc9
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
9606CFAC1E957AC3006B4E74 /* TabsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9606CFAB1E957AC3006B4E74 /* TabsController.swift */; }; 9606CFAC1E957AC3006B4E74 /* TabsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9606CFAB1E957AC3006B4E74 /* TabsController.swift */; };
960E35061EB0FC5700EB124A /* Material+MotionDynamics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 960E35051EB0FC5700EB124A /* Material+MotionDynamics.swift */; };
961409B01E43D15C00E7BA99 /* CollectionViewCard.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961730591E145DE900A9A297 /* CollectionViewCard.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 961409B01E43D15C00E7BA99 /* CollectionViewCard.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961730591E145DE900A9A297 /* CollectionViewCard.swift */; settings = {ATTRIBUTES = (Public, ); }; };
961409B11E43D15C00E7BA99 /* FABMenu.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96A183621E0C6CE200083C30 /* FABMenu.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 961409B11E43D15C00E7BA99 /* FABMenu.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96A183621E0C6CE200083C30 /* FABMenu.swift */; settings = {ATTRIBUTES = (Public, ); }; };
961409B21E43D15C00E7BA99 /* FABMenuController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96A183641E0C6DD400083C30 /* FABMenuController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 961409B21E43D15C00E7BA99 /* FABMenuController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96A183641E0C6DD400083C30 /* FABMenuController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
...@@ -189,6 +190,7 @@ ...@@ -189,6 +190,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
9606CFAB1E957AC3006B4E74 /* TabsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsController.swift; sourceTree = "<group>"; }; 9606CFAB1E957AC3006B4E74 /* TabsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsController.swift; sourceTree = "<group>"; };
960E35051EB0FC5700EB124A /* Material+MotionDynamics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+MotionDynamics.swift"; sourceTree = "<group>"; };
961276621DCD8B1800A7D920 /* CharacterAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterAttribute.swift; sourceTree = "<group>"; }; 961276621DCD8B1800A7D920 /* CharacterAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterAttribute.swift; sourceTree = "<group>"; };
961730591E145DE900A9A297 /* CollectionViewCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCard.swift; sourceTree = "<group>"; }; 961730591E145DE900A9A297 /* CollectionViewCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCard.swift; sourceTree = "<group>"; };
961E6BDE1DDA2A95004E6C93 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; }; 961E6BDE1DDA2A95004E6C93 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
...@@ -657,6 +659,7 @@ ...@@ -657,6 +659,7 @@
children = ( children = (
96BFC1671E61D9FD0075DE1F /* Material+Motion.swift */, 96BFC1671E61D9FD0075DE1F /* Material+Motion.swift */,
96BFC1691E61DAA10075DE1F /* Material+MotionAnimation.swift */, 96BFC1691E61DAA10075DE1F /* Material+MotionAnimation.swift */,
960E35051EB0FC5700EB124A /* Material+MotionDynamics.swift */,
); );
name = Motion; name = Motion;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -946,6 +949,7 @@ ...@@ -946,6 +949,7 @@
965E81221DD4D5C800D61E4B /* TextView.swift in Sources */, 965E81221DD4D5C800D61E4B /* TextView.swift in Sources */,
965E80E61DD4C55200D61E4B /* Material+Obj-C.swift in Sources */, 965E80E61DD4C55200D61E4B /* Material+Obj-C.swift in Sources */,
965E80E71DD4C55200D61E4B /* Material+UIView.swift in Sources */, 965E80E71DD4C55200D61E4B /* Material+UIView.swift in Sources */,
960E35061EB0FC5700EB124A /* Material+MotionDynamics.swift in Sources */,
965E80E81DD4C55200D61E4B /* Material+CALayer.swift in Sources */, 965E80E81DD4C55200D61E4B /* Material+CALayer.swift in Sources */,
965E80E91DD4C55200D61E4B /* Material+String.swift in Sources */, 965E80E91DD4C55200D61E4B /* Material+String.swift in Sources */,
965E80F71DD4D59500D61E4B /* Card.swift in Sources */, 965E80F71DD4D59500D61E4B /* Card.swift in Sources */,
......
...@@ -105,7 +105,7 @@ open class Bar: View { ...@@ -105,7 +105,7 @@ open class Bar: View {
} }
/// ContentView that holds the any desired subviews. /// ContentView that holds the any desired subviews.
open let contentView = UIScrollView() open let contentView = UIView()
/// Left side UIViews. /// Left side UIViews.
open var leftViews: [UIView] { open var leftViews: [UIView] {
...@@ -279,10 +279,6 @@ open class Bar: View { ...@@ -279,10 +279,6 @@ open class Bar: View {
extension Bar { extension Bar {
/// Prepares the contentView. /// Prepares the contentView.
fileprivate func prepareContentView() { fileprivate func prepareContentView() {
contentView.bounces = false
contentView.isPagingEnabled = true
contentView.showsVerticalScrollIndicator = false
contentView.showsHorizontalScrollIndicator = false
contentView.contentScaleFactor = Screen.scale contentView.contentScaleFactor = Screen.scale
} }
} }
...@@ -92,6 +92,9 @@ fileprivate var MotionInstanceControllerKey: UInt8 = 0 ...@@ -92,6 +92,9 @@ fileprivate var MotionInstanceControllerKey: UInt8 = 0
fileprivate struct MotionInstance { fileprivate struct MotionInstance {
fileprivate var identifier: String fileprivate var identifier: String
fileprivate var animations: [MotionAnimation] fileprivate var animations: [MotionAnimation]
/// A reference to the panGesture for interactive transitions.
fileprivate var panGesture: UIPanGestureRecognizer?
} }
fileprivate struct MotionInstanceController { fileprivate struct MotionInstanceController {
...@@ -135,6 +138,7 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat ...@@ -135,6 +138,7 @@ extension UIViewController: MotionDelegate, UIViewControllerTransitioningDelegat
set(value) { set(value) {
if value { if value {
prepareMotionDelegation() prepareMotionDelegation()
view.prepareInteractiveTransitionPanGesture()
motionInstanceController.isMotionEnabled = true motionInstanceController.isMotionEnabled = true
} }
...@@ -242,7 +246,7 @@ extension UIView { ...@@ -242,7 +246,7 @@ extension UIView {
fileprivate var motionInstance: MotionInstance { fileprivate var motionInstance: MotionInstance {
get { get {
return AssociatedObject(base: self, key: &MotionInstanceKey) { return AssociatedObject(base: self, key: &MotionInstanceKey) {
return MotionInstance(identifier: "", animations: []) return MotionInstance(identifier: "", animations: [], panGesture: nil)
} }
} }
set(value) { set(value) {
...@@ -272,6 +276,15 @@ extension UIView { ...@@ -272,6 +276,15 @@ extension UIView {
} }
extension UIView { extension UIView {
/// Prepares the interactive pan gesture.
fileprivate func prepareInteractiveTransitionPanGesture() {
motionInstance.panGesture = UIPanGestureRecognizer()
motionInstance.panGesture?.maximumNumberOfTouches = 1
addGestureRecognizer(motionInstance.panGesture!)
}
}
extension UIView {
/** /**
Snapshots the view instance for animations during transitions. Snapshots the view instance for animations during transitions.
- Parameter afterUpdates: A boolean indicating whether to snapshot the view - Parameter afterUpdates: A boolean indicating whether to snapshot the view
...@@ -403,16 +416,6 @@ open class MotionAnimator: NSObject { ...@@ -403,16 +416,6 @@ open class MotionAnimator: NSObject {
/// A reference to the transition background view. /// A reference to the transition background view.
open let transitionBackgroundView = UIView() open let transitionBackgroundView = UIView()
/// A reference to the view controller that is being transitioned to.
open var toViewController: UIViewController {
return transitionContext.viewController(forKey: .to)!
}
/// A reference to the view controller that is being transitioned from.
open var fromViewController: UIViewController {
return transitionContext.viewController(forKey: .from)!
}
/// The transition context for the current transition. /// The transition context for the current transition.
open fileprivate(set) var transitionContext: UIViewControllerContextTransitioning! open fileprivate(set) var transitionContext: UIViewControllerContextTransitioning!
...@@ -428,6 +431,16 @@ open class MotionAnimator: NSObject { ...@@ -428,6 +431,16 @@ open class MotionAnimator: NSObject {
/// The view that is used to animate the transitions between view controllers. /// The view that is used to animate the transitions between view controllers.
open fileprivate(set) var transitionView = UIView() open fileprivate(set) var transitionView = UIView()
/// A reference to the view controller that is being transitioned to.
open var toViewController: UIViewController {
return transitionContext.viewController(forKey: .to)!
}
/// A reference to the view controller that is being transitioned from.
open var fromViewController: UIViewController {
return transitionContext.viewController(forKey: .from)!
}
/// The view that is being transitioned to. /// The view that is being transitioned to.
open var toView: UIView { open var toView: UIView {
return toViewController.view return toViewController.view
...@@ -828,6 +841,10 @@ extension Motion { ...@@ -828,6 +841,10 @@ extension Motion {
extension Motion { extension Motion {
/// Cleans up the animation transition. /// Cleans up the animation transition.
fileprivate func cleanUpAnimation() { fileprivate func cleanUpAnimation() {
guard !isInteractive else {
return
}
Motion.delay(transitionDuration(using: transitionContext) + delayTransitionByTimeInterval) { [weak self] in Motion.delay(transitionDuration(using: transitionContext) + delayTransitionByTimeInterval) { [weak self] in
guard let s = self else { guard let s = self else {
return return
...@@ -842,6 +859,10 @@ extension Motion { ...@@ -842,6 +859,10 @@ extension Motion {
/// Removes the transitionSnapshot from its superview. /// Removes the transitionSnapshot from its superview.
fileprivate func removeTransitionSnapshot() { fileprivate func removeTransitionSnapshot() {
guard !isInteractive else {
return
}
Motion.delay(delay) { [weak self] in Motion.delay(delay) { [weak self] in
self?.transitionSnapshot.removeFromSuperview() self?.transitionSnapshot.removeFromSuperview()
} }
...@@ -921,7 +942,6 @@ open class InteractiveMotion: UIPercentDrivenInteractiveTransition { ...@@ -921,7 +942,6 @@ open class InteractiveMotion: UIPercentDrivenInteractiveTransition {
self.transitionContext = transitionContext self.transitionContext = transitionContext
preparePanGesture() preparePanGesture()
prepareContainerView()
} }
// //
// //
...@@ -941,20 +961,13 @@ open class InteractiveMotion: UIPercentDrivenInteractiveTransition { ...@@ -941,20 +961,13 @@ open class InteractiveMotion: UIPercentDrivenInteractiveTransition {
extension InteractiveMotion { extension InteractiveMotion {
fileprivate func preparePanGesture() { fileprivate func preparePanGesture() {
panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanUpdate(gestureRecognizer:))) (animator as? PresentingMotion)?.fromView.motionInstance.panGesture?.addTarget(self, action: #selector(handleInteractiveTransition(gestureRecognizer:)))
panGesture.maximumNumberOfTouches = 1
}
/// Prepares the containerView.
fileprivate func prepareContainerView() {
containerView = transitionContext.containerView
containerView.addGestureRecognizer(panGesture)
} }
} }
extension InteractiveMotion { extension InteractiveMotion {
@objc @objc
fileprivate func handlePanUpdate(gestureRecognizer: UIGestureRecognizer) { fileprivate func handleInteractiveTransition(gestureRecognizer: UIGestureRecognizer) {
switch gestureRecognizer.state { switch gestureRecognizer.state {
case .began: case .began:
panGesture.setTranslation(.zero, in: containerView) panGesture.setTranslation(.zero, in: containerView)
......
/*
* Copyright (C) 2015 - 2017, 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 MotionDynamics: UIDynamicBehavior {
open var targetPoint: CGPoint {
get {
return attachmentBehavior.anchorPoint
}
set(value) {
attachmentBehavior.anchorPoint = value
}
}
open var velocity: CGPoint {
get {
return dynamicItemBehavior.linearVelocity(for: item)
}
set(value) {
let v = velocity
dynamicItemBehavior.addLinearVelocity(CGPoint(x: value.x - v.x, y: value.y - v.y), for: item)
}
}
open fileprivate(set) var item: UIDynamicItem
open fileprivate(set) var attachmentBehavior: UIAttachmentBehavior!
open fileprivate(set) var dynamicItemBehavior: UIDynamicItemBehavior!
public init(item: UIDynamicItem) {
self.item = item
}
open func prepare() {
prepareAttachmentBehavior()
prepareDynamicItemBehavior()
}
}
extension MotionDynamics {
fileprivate func prepareAttachmentBehavior() {
attachmentBehavior = UIAttachmentBehavior(item: item, attachedToAnchor: .zero)
attachmentBehavior.frequency = 3.5
attachmentBehavior.damping = 0.4
attachmentBehavior.length = 0
addChildBehavior(attachmentBehavior)
}
fileprivate func prepareDynamicItemBehavior() {
dynamicItemBehavior = UIDynamicItemBehavior(items: [item])
dynamicItemBehavior.density = 100
dynamicItemBehavior.resistance = 10
addChildBehavior(dynamicItemBehavior)
}
}
public enum PaneState: Int {
case opened
case closed
}
@objc(MotionDynamicsAnimatorDelegate)
public protocol MotionDynamicsAnimatorDelegate {
// - (void)animationTick:(CFTimeInterval)dt finished:(BOOL *)finished;
}
open class PaneMotionDynamicsAnimator: MotionDynamicsAnimatorDelegate {
// - (void)animationTick:(CFTimeInterval)dt finished:(BOOL *)finished;
}
open class PaneController: UIViewController {
fileprivate var motionDynamics: MotionDynamics?
fileprivate var pane: UIView!
fileprivate var paneMotionDynamicsAnimator: PaneMotionDynamicsAnimator!
fileprivate var animator: UIDynamicItemBehavior!
open override func viewDidLoad() {
super.viewDidLoad()
prepare()
}
open func prepare() {
prepareAnimator()
preparePane()
prepareMotionDynamics()
preparePaneMotionDynamicsAnimator()
}
}
extension PaneController {
fileprivate func prepareAnimator() {
animator = UIDynamicItemBehavior()
}
fileprivate func preparePane() {
pane = UIView()
}
fileprivate func prepareMotionDynamics() {
guard nil == motionDynamics else {
return
}
motionDynamics = MotionDynamics(item: pane)
}
fileprivate func preparePaneMotionDynamicsAnimator() {
paneMotionDynamicsAnimator = PaneMotionDynamicsAnimator()
}
}
extension PaneController {
fileprivate func targetPointFor(state: PaneState) -> CGPoint {
return .zero
}
}
extension PaneController {
fileprivate func draggable(view: UIView, draggingEndedWith velocity: CGPoint) {
animatePaneTo(state: velocity.y < 0 ? .opened : .closed, initialVelocity: velocity)
}
fileprivate func animatePaneTo(state: PaneState, initialVelocity: CGPoint) {
prepareMotionDynamics()
motionDynamics?.targetPoint = targetPointFor(state: state)
if initialVelocity.equalTo(.zero) {
motionDynamics?.velocity = initialVelocity
}
// animator.addChildBehavior(motionDynamics)
}
}
...@@ -70,10 +70,10 @@ open class TabBar: Bar { ...@@ -70,10 +70,10 @@ open class TabBar: Bar {
/// Enables and disables bouncing when swiping. /// Enables and disables bouncing when swiping.
open var isBounceEnabled: Bool { open var isBounceEnabled: Bool {
get { get {
return contentView.bounces return scrollView.bounces
} }
set(value) { set(value) {
contentView.bounces = value scrollView.bounces = value
} }
} }
...@@ -84,6 +84,9 @@ open class TabBar: Bar { ...@@ -84,6 +84,9 @@ open class TabBar: Bar {
} }
} }
/// A reference to the scroll view when the tab bar style is scrollable.
open fileprivate(set) var scrollView: UIScrollView!
/// A delegation reference. /// A delegation reference.
open weak var delegate: TabBarDelegate? open weak var delegate: TabBarDelegate?
...@@ -219,8 +222,8 @@ open class TabBar: Bar { ...@@ -219,8 +222,8 @@ open class TabBar: Bar {
w += width w += width
} }
if w > contentView.width { if w > scrollView.width {
contentView.contentSize.width = w scrollView.contentSize.width = w
} }
} else { } else {
contentView.grid.axis.columns = buttons.count contentView.grid.axis.columns = buttons.count
...@@ -243,11 +246,11 @@ open class TabBar: Bar { ...@@ -243,11 +246,11 @@ open class TabBar: Bar {
*/ */
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
isBounceEnabled = true
contentEdgeInsetsPreset = .none contentEdgeInsetsPreset = .none
interimSpacePreset = .interimSpace5 interimSpacePreset = .interimSpace5
prepareLine() prepareScrollView()
prepareDivider() prepareDivider()
prepareLine()
} }
} }
...@@ -274,6 +277,18 @@ extension TabBar { ...@@ -274,6 +277,18 @@ extension TabBar {
button.addTarget(self, action: #selector(handleLineAnimation(button:)), for: .touchUpInside) button.addTarget(self, action: #selector(handleLineAnimation(button:)), for: .touchUpInside)
} }
/// Prepares the scroll view.
fileprivate func prepareScrollView() {
scrollView = UIScrollView()
scrollView.bounces = false
scrollView.isPagingEnabled = true
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
contentView.addSubview(scrollView)
}
}
extension TabBar {
/** /**
Removes the line animation handlers. Removes the line animation handlers.
- Parameter button: A UIButton. - Parameter button: A UIButton.
......
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