Commit 1dfc3fb8 by Daniel Dahan

updated Motion identifier values and internal handling of associated objects

parent 398021c6
......@@ -25,7 +25,6 @@
96AEB6951EE4610F009A3BE0 /* Motion+CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB66F1EE4610F009A3BE0 /* Motion+CALayer.swift */; };
96AEB6961EE4610F009A3BE0 /* Motion+CAMediaTimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6701EE4610F009A3BE0 /* Motion+CAMediaTimingFunction.swift */; };
96AEB6971EE4610F009A3BE0 /* Motion+CG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6711EE4610F009A3BE0 /* Motion+CG.swift */; };
96AEB6981EE4610F009A3BE0 /* Motion+DispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6721EE4610F009A3BE0 /* Motion+DispatchQueue.swift */; };
96AEB6991EE4610F009A3BE0 /* Motion+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6731EE4610F009A3BE0 /* Motion+UIKit.swift */; };
96AEB69A1EE4610F009A3BE0 /* Motion+UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6741EE4610F009A3BE0 /* Motion+UIView.swift */; };
96AEB69B1EE4610F009A3BE0 /* Motion+UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6751EE4610F009A3BE0 /* Motion+UIViewController.swift */; };
......@@ -69,7 +68,6 @@
96AEB66F1EE4610F009A3BE0 /* Motion+CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+CALayer.swift"; sourceTree = "<group>"; };
96AEB6701EE4610F009A3BE0 /* Motion+CAMediaTimingFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+CAMediaTimingFunction.swift"; sourceTree = "<group>"; };
96AEB6711EE4610F009A3BE0 /* Motion+CG.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+CG.swift"; sourceTree = "<group>"; };
96AEB6721EE4610F009A3BE0 /* Motion+DispatchQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+DispatchQueue.swift"; sourceTree = "<group>"; };
96AEB6731EE4610F009A3BE0 /* Motion+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+UIKit.swift"; sourceTree = "<group>"; };
96AEB6741EE4610F009A3BE0 /* Motion+UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+UIView.swift"; sourceTree = "<group>"; };
96AEB6751EE4610F009A3BE0 /* Motion+UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+UIViewController.swift"; sourceTree = "<group>"; };
......@@ -139,11 +137,10 @@
96AEB66F1EE4610F009A3BE0 /* Motion+CALayer.swift */,
96AEB6701EE4610F009A3BE0 /* Motion+CAMediaTimingFunction.swift */,
96AEB6711EE4610F009A3BE0 /* Motion+CG.swift */,
96AEB6721EE4610F009A3BE0 /* Motion+DispatchQueue.swift */,
963150D11EE50DA6002B0D42 /* Motion+Obj-C.swift */,
96AEB6731EE4610F009A3BE0 /* Motion+UIKit.swift */,
96AEB6741EE4610F009A3BE0 /* Motion+UIView.swift */,
96AEB6751EE4610F009A3BE0 /* Motion+UIViewController.swift */,
963150D11EE50DA6002B0D42 /* Motion+Obj-C.swift */,
963150D91EE51EB4002B0D42 /* MotionAnimationFillMode.swift */,
);
path = Extensions;
......@@ -311,7 +308,6 @@
96AEB6961EE4610F009A3BE0 /* Motion+CAMediaTimingFunction.swift in Sources */,
96AEB6941EE4610F009A3BE0 /* Motion+Array.swift in Sources */,
96AEB6951EE4610F009A3BE0 /* Motion+CALayer.swift in Sources */,
96AEB6981EE4610F009A3BE0 /* Motion+DispatchQueue.swift in Sources */,
96AEB6A61EE4610F009A3BE0 /* MotionTypes.swift in Sources */,
96AEB68E1EE4610F009A3BE0 /* MotionCoreAnimationViewContext.swift in Sources */,
96AEB6921EE4610F009A3BE0 /* MotionDebugView.swift in Sources */,
......
......@@ -56,7 +56,7 @@ class MotionDebugView: UIView {
}
var showOnTop: Bool = false
var rotation: CGFloat = π / 6
var rotation: CGFloat = .pi / 6
var scale: CGFloat = 0.6
var translation: CGPoint = .zero
var progress: Float {
......@@ -145,10 +145,10 @@ class MotionDebugView: UIView {
startRotation = rotation
}
rotation = startRotation + panGR.translation(in: nil).x / 150
if rotation > π {
rotation -= 2 * π
} else if rotation < -π {
rotation += 2 * π
if rotation > .pi {
rotation -= 2 * .pi
} else if rotation < -.pi {
rotation += 2 * .pi
}
delegate?.onPerspectiveChanged(translation:translation, rotation: rotation, scale:scale)
}
......
/*
* The MIT License (MIT)
*
* Copyright (C) 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Original Inspiration & Author
* Copyright (c) 2016 Luke Zhao <me@lkzhao.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
func delay(_ time: Double, execute: @escaping () -> Void) {
if time > 0 {
DispatchQueue.main.asyncAfter(deadline: .now() + time, execute: execute)
} else {
DispatchQueue.main.async(execute: execute)
}
}
......@@ -51,49 +51,101 @@ internal extension UIView {
}
}
public extension UIView {
private struct AssociatedKeys {
static var motionID = "motionID"
static var motionModifiers = "motionModifers"
static var motionStoredAlpha = "motionStoredAlpha"
static var motionEnabled = "motionEnabled"
}
/**
**motionID** is the identifier for the view. When doing a transition between two view controllers,
Motion will search through all the subviews for both view controllers and matches views with the same **motionID**.
fileprivate var MotionInstanceKey: UInt8 = 0
Whenever a pair is discovered,
Motion will automatically transit the views from source state to the destination state.
*/
@IBInspectable public var motionID: String? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.motionID) as? String }
set { objc_setAssociatedObject(self, &AssociatedKeys.motionID, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}
fileprivate struct MotionInstance {
/// A boolean indicating whether Motion is enabled.
fileprivate var isEnabled: Bool
/// An optional reference to the motion identifier.
fileprivate var identifier: String?
/// An optional reference to the motion animations.
fileprivate var animations: [MotionAnimation]?
/// An optional reference to the motion transition animations.
fileprivate var transitions: [MotionTransition]?
/// An alpha value.
fileprivate var alpha: CGFloat?
}
/**
**isMotionEnabled** allows to specify whether a view and its subviews should be consider for animations.
If true, Motion will search through all the subviews for motionIds and modifiers. Defaults to true
*/
@IBInspectable public var isMotionEnabled: Bool {
get { return objc_getAssociatedObject(self, &AssociatedKeys.motionEnabled) as? Bool ?? true }
set { objc_setAssociatedObject(self, &AssociatedKeys.motionEnabled, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}
extension UIView {
/// MotionInstance reference.
fileprivate var motionInstance: MotionInstance {
get {
return AssociatedObject.get(base: self, key: &MotionInstanceKey) {
return MotionInstance(isEnabled: true, identifier: nil, animations: nil, transitions: nil, alpha: 1)
}
}
set(value) {
AssociatedObject.set(base: self, key: &MotionInstanceKey, value: value)
}
}
/// A boolean that indicates whether motion is enabled.
@IBInspectable
public var isMotionEnabled: Bool {
get {
return motionInstance.isEnabled
}
set(value) {
motionInstance.isEnabled = value
}
}
/// An identifier value used to connect views across UIViewControllers.
@IBInspectable
open var motionIdentifier: String? {
get {
return motionInstance.identifier
}
set(value) {
motionInstance.identifier = value
}
}
/// The animations to run.
open var motionAnimations: [MotionAnimation]? {
get {
return motionInstance.animations
}
set(value) {
motionInstance.animations = value
}
}
/// The animations to run while in transition.
open var motionTransitions: [MotionTransition]? {
get {
return motionInstance.transitions
}
set(value) {
motionInstance.transitions = value
}
}
/// The animations to run while in transition.
@IBInspectable
open var motionAlpha: CGFloat? {
get {
return motionInstance.alpha
}
set(value) {
motionInstance.alpha = value
}
}
}
/**
Use **motionModifiers** to specify animations alongside the main transition. Checkout `MotionTransition.swift` for available modifiers.
*/
public var motionModifiers: [MotionTransition]? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.motionModifiers) as? [MotionTransition] }
set { objc_setAssociatedObject(self, &AssociatedKeys.motionModifiers, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}
public extension UIView {
/**
**motionModifierString** provides another way to set **motionModifiers**. It can be assigned through storyboard.
**motionModifierString** provides another way to set **motionTransitions**. It can be assigned through storyboard.
*/
@IBInspectable public var motionModifierString: String? {
get { fatalError("Reverse lookup is not supported") }
set { motionModifiers = newValue?.parse() }
set { motionTransitions = newValue?.parse() }
}
internal func slowSnapshotView() -> UIView {
......@@ -117,54 +169,6 @@ public extension UIView {
}
return isHidden && (superview is UICollectionView || self is UITableViewCell) ? [] : ([self] + subviews.flatMap { $0.flattenedViewHierarchy })
}
/// Used for .overFullScreen presentation
internal var motionStoredAlpha: CGFloat? {
get {
if let doubleValue = (objc_getAssociatedObject(self, &AssociatedKeys.motionStoredAlpha) as? NSNumber)?.doubleValue {
return CGFloat(doubleValue)
}
return nil
}
set {
if let newValue = newValue {
objc_setAssociatedObject(self, &AssociatedKeys.motionStoredAlpha, NSNumber(value:newValue.native), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
} else {
objc_setAssociatedObject(self, &AssociatedKeys.motionStoredAlpha, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
fileprivate var MotionInstanceKey: UInt8 = 0
fileprivate struct MotionInstance {
/// An optional reference to the motion animations.
fileprivate var animations: [MotionAnimation]?
}
extension UIView {
/// MotionInstance reference.
fileprivate var motionInstance: MotionInstance {
get {
return AssociatedObject.get(base: self, key: &MotionInstanceKey) {
return MotionInstance(animations: nil)
}
}
set(value) {
AssociatedObject.set(base: self, key: &MotionInstanceKey, value: value)
}
}
/// The animations to run.
open var motionAnimations: [MotionAnimation]? {
get {
return motionInstance.animations
}
set(value) {
motionInstance.animations = value
}
}
}
extension UIView {
......
......@@ -111,7 +111,7 @@ public extension Motion {
}
/// Set the default animation for next transition
/// This usually overrides rootView's motionModifiers during the transition
/// This usually overrides rootView's motionTransitions during the transition
///
/// - Parameter animation: animation type
func setDefaultAnimationForNextTransition(_ animation: MotionDefaultAnimationType) {
......
......@@ -29,8 +29,8 @@
import UIKit
public class MotionContext {
internal var motionIDToSourceView = [String: UIView]()
internal var motionIDToDestinationView = [String: UIView]()
internal var motionIdentifierToSourceView = [String: UIView]()
internal var motionIdentifierToDestinationView = [String: UIView]()
internal var snapshotViews = [UIView: UIView]()
internal var viewAlphas = [UIView: CGFloat]()
internal var targetStates = [UIView: MotionTargetState]()
......@@ -45,18 +45,18 @@ public class MotionContext {
internal func set(fromViews: [UIView], toViews: [UIView]) {
self.fromViews = fromViews
self.toViews = toViews
process(views: fromViews, idMap: &motionIDToSourceView)
process(views: toViews, idMap: &motionIDToDestinationView)
process(views: fromViews, idMap: &motionIdentifierToSourceView)
process(views: toViews, idMap: &motionIdentifierToDestinationView)
}
internal func process(views: [UIView], idMap: inout [String: UIView]) {
for view in views {
view.layer.removeAllAnimations()
if container.convert(view.bounds, from: view).intersects(container.bounds) {
if let motionID = view.motionID {
idMap[motionID] = view
if let motionIdentifier = view.motionIdentifier {
idMap[motionIdentifier] = view
}
if let modifiers = view.motionModifiers {
if let modifiers = view.motionTransitions {
targetStates[view] = MotionTargetState(modifiers: modifiers)
}
}
......@@ -83,24 +83,24 @@ public class MotionContext {
extension MotionContext {
/**
- Returns: a source view matching the motionID, nil if not found
- Returns: a source view matching the motionIdentifier, nil if not found
*/
public func sourceView(for motionID: String) -> UIView? {
return motionIDToSourceView[motionID]
public func sourceView(for motionIdentifier: String) -> UIView? {
return motionIdentifierToSourceView[motionIdentifier]
}
/**
- Returns: a destination view matching the motionID, nil if not found
- Returns: a destination view matching the motionIdentifier, nil if not found
*/
public func destinationView(for motionID: String) -> UIView? {
return motionIDToDestinationView[motionID]
public func destinationView(for motionIdentifier: String) -> UIView? {
return motionIdentifierToDestinationView[motionIdentifier]
}
/**
- Returns: a view with the same motionID, but on different view controller, nil if not found
- Returns: a view with the same motionIdentifier, but on different view controller, nil if not found
*/
public func pairedView(for view: UIView) -> UIView? {
if let id = view.motionID {
if let id = view.motionIdentifier {
if sourceView(for: id) == view {
return destinationView(for: id)
} else if destinationView(for: id) == view {
......@@ -237,7 +237,7 @@ extension MotionContext {
}
snapshot.frame = containerView.convert(view.bounds, from: view)
snapshot.motionID = view.motionID
snapshot.motionIdentifier = view.motionIdentifier
hide(view: view)
......@@ -352,16 +352,16 @@ extension MotionContext {
return snapshots
}
internal func loadViewAlpha(rootView: UIView) {
if let storedAlpha = rootView.motionStoredAlpha {
if let storedAlpha = rootView.motionAlpha {
rootView.alpha = storedAlpha
rootView.motionStoredAlpha = nil
rootView.motionAlpha = nil
}
for subview in rootView.subviews {
loadViewAlpha(rootView: subview)
}
}
internal func storeViewAlpha(rootView: UIView) {
rootView.motionStoredAlpha = viewAlphas[rootView]
rootView.motionAlpha = viewAlphas[rootView]
for subview in rootView.subviews {
storeViewAlpha(rootView: subview)
}
......
......@@ -102,8 +102,8 @@ extension MotionTransition: MotionStringConvertible {
}
return .cascade(delta: parameters.getDouble(0) ?? 0.02, direction: cascadeDirection, delayMatchedViews:parameters.getBool(2) ?? false)
case "source":
if let motionID = parameters.get(0)?.name {
return .source(motionID: motionID)
if let motionIdentifier = parameters.get(0)?.name {
return .source(motionIdentifier: motionIdentifier)
}
case "useGlobalCoordinateSpace":
return .useGlobalCoordinateSpace
......
......@@ -408,7 +408,7 @@ extension MotionTransition {
// other modifiers
extension MotionTransition {
/**
Transition from/to the state of the view with matching motionID
Transition from/to the state of the view with matching motionIdentifier
Will also force the view to use global coordinate space.
The following layer properties will be animated from the given view.
......@@ -430,11 +430,11 @@ extension MotionTransition {
borderColor
- Parameters:
- motionID: the source view's motionId.
- motionIdentifier: the source view's motionId.
*/
public static func source(motionID: String) -> MotionTransition {
public static func source(motionIdentifier: String) -> MotionTransition {
return MotionTransition { targetState in
targetState.source = motionID
targetState.source = motionIdentifier
}
}
......@@ -531,12 +531,12 @@ extension MotionTransition {
}
/**
ignore all motionModifiers attributes for a view's direct subviews.
ignore all motionTransitions attributes for a view's direct subviews.
*/
public static var ignoreSubviewModifiers: MotionTransition = .ignoreSubviewModifiers()
/**
ignore all motionModifiers attributes for a view's subviews.
ignore all motionTransitions attributes for a view's subviews.
- Parameters:
- recursive: if false, will only ignore direct subviews' modifiers. default false.
*/
......
......@@ -31,7 +31,7 @@ import UIKit
class MatchPreprocessor: BasePreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) {
for tv in toViews {
guard let id = tv.motionID, let fv = context.sourceView(for: id) else { continue }
guard let id = tv.motionIdentifier, let fv = context.sourceView(for: id) else { continue }
var tvState = context[tv] ?? MotionTargetState()
var fvState = context[fv] ?? MotionTargetState()
......
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