Commit 81b97305 by Daniel Dahan

updated README

parent c9b17207
## Welcome to Motion ## Welcome to Motion
Seamless animation and transition in Swift. Seamless animations and transitions in Swift.
** Releasing Later Today ** ### Documentation
Documentation for using Motion will be released February 3rd 2017.
### Sample
Take a look at a sample [Photo Collection](https://github.com/CosmicMind/Samples/tree/master/Motion/PhotoCollection) project.
![Motion Photo Collection Sample](http://www.cosmicmind.com/motion/motion_sample.gif)
## License ## License
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
......
...@@ -325,6 +325,21 @@ extension UIView { ...@@ -325,6 +325,21 @@ extension UIView {
} }
} }
/// The global position of a view.
open var motionPosition: CGPoint {
return superview?.convert(position, to: nil) ?? position
}
/// The layer.transform of a view.
open var motionTransform: CATransform3D {
get {
return layer.transform
}
set(value) {
layer.transform = value
}
}
/// Computes the scale X axis value of the view. /// Computes the scale X axis value of the view.
open var motionScaleX: CGFloat { open var motionScaleX: CGFloat {
return transform.a return transform.a
......
...@@ -30,34 +30,35 @@ ...@@ -30,34 +30,35 @@
import UIKit import UIKit
fileprivate var MotionTransitionInstanceKey: UInt8 = 0 fileprivate var MotionInstanceKey: UInt8 = 0
fileprivate var MotionTransitionInstanceControllerKey: UInt8 = 0 fileprivate var MotionInstanceControllerKey: UInt8 = 0
fileprivate struct MotionTransitionInstance { fileprivate struct MotionInstance {
fileprivate var identifier: String fileprivate var identifier: String
fileprivate var animations: [MotionAnimation] fileprivate var animations: [MotionAnimation]
} }
fileprivate struct MotionTransitionInstanceController { fileprivate struct MotionInstanceController {
fileprivate var isEnabled: Bool fileprivate var isEnabled: Bool
fileprivate weak var delegate: MotionDelegate?
} }
extension UIViewController: UIViewControllerTransitioningDelegate { extension UIViewController: UIViewControllerTransitioningDelegate {
/// MotionTransitionInstanceController Reference. /// MotionInstanceController Reference.
fileprivate var motionTransition: MotionTransitionInstanceController { fileprivate var motion: MotionInstanceController {
get { get {
return AssociatedObject(base: self, key: &MotionTransitionInstanceControllerKey) { return AssociatedObject(base: self, key: &MotionInstanceControllerKey) {
return MotionTransitionInstanceController(isEnabled: false) return MotionInstanceController(isEnabled: false, delegate: nil)
} }
} }
set(value) { set(value) {
AssociateObject(base: self, key: &MotionTransitionInstanceControllerKey, value: value) AssociateObject(base: self, key: &MotionInstanceControllerKey, value: value)
} }
} }
open var isMotionTransitionEnabled: Bool { open var isMotionEnabled: Bool {
get { get {
return motionTransition.isEnabled return motion.isEnabled
} }
set(value) { set(value) {
if value { if value {
...@@ -65,74 +66,66 @@ extension UIViewController: UIViewControllerTransitioningDelegate { ...@@ -65,74 +66,66 @@ extension UIViewController: UIViewControllerTransitioningDelegate {
transitioningDelegate = self transitioningDelegate = self
} }
motionTransition.isEnabled = value motion.isEnabled = value
}
}
open weak var motionDelegate: MotionDelegate? {
get {
return motion.delegate
}
set(value) {
motion.delegate = value
} }
} }
} }
extension UIViewController { extension UIViewController {
open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return isMotionTransitionEnabled ? MotionTransition(isPresenting: true) : nil return isMotionEnabled ? MotionTransition(isPresenting: true) : nil
} }
open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return isMotionTransitionEnabled ? MotionTransition() : nil return isMotionEnabled ? MotionTransition() : nil
} }
open func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { open func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return isMotionTransitionEnabled ? MotionTransitionPresentationController(presentedViewController: presented, presenting: presenting) : nil return isMotionEnabled ? MotionTransitionPresentationController(presentedViewController: presented, presenting: presenting) : nil
}
}
extension UIViewController {
open func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return isMotionTransitionEnabled ? MotionTransition(isPresenting: operation == .push) : nil
}
}
extension UIViewController {
open func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return isMotionTransitionEnabled ? MotionTransition() : nil
} }
} }
extension UIView { extension UIView {
/// The global position of a view.
open var motionPosition: CGPoint {
return superview?.convert(position, to: nil) ?? position
}
/// MaterialTransitionItem Reference. /// MaterialTransitionItem Reference.
fileprivate var motionTransition: MotionTransitionInstance { fileprivate var motionInstance: MotionInstance {
get { get {
return AssociatedObject(base: self, key: &MotionTransitionInstanceKey) { return AssociatedObject(base: self, key: &MotionInstanceKey) {
return MotionTransitionInstance(identifier: "", animations: []) return MotionInstance(identifier: "", animations: [])
} }
} }
set(value) { set(value) {
AssociateObject(base: self, key: &MotionTransitionInstanceKey, value: value) AssociateObject(base: self, key: &MotionInstanceKey, value: value)
} }
} }
open var motionTransitionIdentifier: String { open var motionIdentifier: String {
get { get {
return motionTransition.identifier return motionInstance.identifier
} }
set(value) { set(value) {
motionTransition.identifier = value motionInstance.identifier = value
} }
} }
open var motionTransitionAnimations: [MotionAnimation] { open var motionAnimations: [MotionAnimation] {
get { get {
return motionTransition.animations return motionInstance.animations
} }
set(value) { set(value) {
motionTransition.animations = value motionInstance.animations = value
} }
} }
open func motionTransitionSnapshot(afterUpdates: Bool) -> UIView { open func motionSnapshot(afterUpdates: Bool, shouldHide: Bool = true) -> UIView {
isHidden = false isHidden = false
let oldCornerRadius = cornerRadius let oldCornerRadius = cornerRadius
...@@ -141,19 +134,19 @@ extension UIView { ...@@ -141,19 +134,19 @@ extension UIView {
let oldBackgroundColor = backgroundColor let oldBackgroundColor = backgroundColor
backgroundColor = .clear backgroundColor = .clear
let oldTransform = transform let oldTransform = motionTransform
transform = .identity motionTransform = CATransform3DIdentity
let v = snapshotView(afterScreenUpdates: afterUpdates)! let v = snapshotView(afterScreenUpdates: afterUpdates)!
cornerRadius = oldCornerRadius cornerRadius = oldCornerRadius
backgroundColor = oldBackgroundColor backgroundColor = oldBackgroundColor
transform = oldTransform motionTransform = oldTransform
let contentView = v.subviews.first! let contentView = v.subviews.first!
contentView.cornerRadius = cornerRadius contentView.cornerRadius = cornerRadius
contentView.masksToBounds = true contentView.masksToBounds = true
v.motionTransitionIdentifier = motionTransitionIdentifier v.motionIdentifier = motionIdentifier
v.position = motionPosition v.position = motionPosition
v.bounds = bounds v.bounds = bounds
v.cornerRadius = cornerRadius v.cornerRadius = cornerRadius
...@@ -169,10 +162,10 @@ extension UIView { ...@@ -169,10 +162,10 @@ extension UIView {
v.shadowColor = shadowColor v.shadowColor = shadowColor
v.shadowOffset = shadowOffset v.shadowOffset = shadowOffset
v.contentMode = contentMode v.contentMode = contentMode
v.transform = transform v.motionTransform = motionTransform
v.backgroundColor = backgroundColor v.backgroundColor = backgroundColor
isHidden = true isHidden = shouldHide
return v return v
} }
...@@ -216,6 +209,15 @@ open class MotionTransitionPresentationController: UIPresentationController { ...@@ -216,6 +209,15 @@ open class MotionTransitionPresentationController: UIPresentationController {
} }
} }
@objc(MotionDelegate)
public protocol MotionDelegate {
@objc
optional func motion(transition: MotionTransition, willTransition toView: UIView, fromView: UIView)
@objc
optional func motion(transition: MotionTransition, didTransition toView: UIView, fromView: UIView)
}
open class MotionTransition: NSObject { open class MotionTransition: NSObject {
open var isPresenting: Bool open var isPresenting: Bool
...@@ -271,7 +273,7 @@ open class MotionTransition: NSObject { ...@@ -271,7 +273,7 @@ open class MotionTransition: NSObject {
open func subviews(of view: UIView, views: inout [UIView]) { open func subviews(of view: UIView, views: inout [UIView]) {
for v in view.subviews { for v in view.subviews {
if 0 < v.motionTransitionIdentifier.utf16.count { if 0 < v.motionIdentifier.utf16.count {
views.append(v) views.append(v)
} }
subviews(of: v, views: &views) subviews(of: v, views: &views)
...@@ -321,19 +323,15 @@ extension MotionTransition { ...@@ -321,19 +323,15 @@ extension MotionTransition {
} }
fileprivate func prepareTransitionSnapshot() { fileprivate func prepareTransitionSnapshot() {
transitionSnapshot = fromView.motionTransitionSnapshot(afterUpdates: true) transitionSnapshot = fromView.motionSnapshot(afterUpdates: true, shouldHide: false)
transitionSnapshot.frame = containerView.bounds transitionSnapshot.frame = containerView.bounds
containerView.addSubview(transitionSnapshot) containerView.insertSubview(transitionSnapshot, aboveSubview: fromView)
} }
fileprivate func prepareTransitionPairs() { fileprivate func prepareTransitionPairs() {
for from in fromSubviews { for from in fromSubviews {
guard 0 < from.motionTransitionIdentifier.utf16.count else {
continue
}
for to in toSubviews { for to in toSubviews {
guard to.motionTransitionIdentifier == from.motionTransitionIdentifier else { guard to.motionIdentifier == from.motionIdentifier else {
continue continue
} }
...@@ -348,17 +346,15 @@ extension MotionTransition { ...@@ -348,17 +346,15 @@ extension MotionTransition {
} }
fileprivate func prepareTransitionBackgroundView() { fileprivate func prepareTransitionBackgroundView() {
transitionBackgroundView.backgroundColor = fromView.backgroundColor transitionBackgroundView.backgroundColor = isPresenting ? .clear : fromView.backgroundColor ?? .clear
transitionBackgroundView.frame = transitionView.bounds transitionBackgroundView.frame = transitionView.bounds
transitionView.addSubview(transitionBackgroundView) transitionView.addSubview(transitionBackgroundView)
} }
fileprivate func prepareTransitionToView() { fileprivate func prepareTransitionToView() {
if isPresenting { toView.isHidden = isPresenting
containerView.insertSubview(toView, belowSubview: transitionView) containerView.insertSubview(toView, belowSubview: transitionView)
}
toView.isHidden = false
toView.updateConstraints() toView.updateConstraints()
toView.setNeedsLayout() toView.setNeedsLayout()
toView.layoutIfNeeded() toView.layoutIfNeeded()
...@@ -369,7 +365,6 @@ extension MotionTransition { ...@@ -369,7 +365,6 @@ extension MotionTransition {
addBackgroundMotionAnimation() addBackgroundMotionAnimation()
cleanupAnimation() cleanupAnimation()
hideFromView()
removeTransitionSnapshot() removeTransitionSnapshot()
} }
} }
...@@ -386,19 +381,21 @@ extension MotionTransition { ...@@ -386,19 +381,21 @@ extension MotionTransition {
snapshotAnimations.append(sizeAnimation) snapshotAnimations.append(sizeAnimation)
snapshotAnimations.append(cornerRadiusAnimation) snapshotAnimations.append(cornerRadiusAnimation)
snapshotAnimations.append(Motion.position(to: to.motionPosition)) snapshotAnimations.append(Motion.position(to: to.motionPosition))
snapshotAnimations.append(Motion.rotation(angle: to.motionRotationAngle)) snapshotAnimations.append(Motion.transform(transform: to.motionTransform))
snapshotAnimations.append(Motion.background(color: to.backgroundColor ?? .clear)) snapshotAnimations.append(Motion.background(color: to.backgroundColor ?? .clear))
snapshotChildAnimations.append(cornerRadiusAnimation) snapshotChildAnimations.append(cornerRadiusAnimation)
snapshotChildAnimations.append(sizeAnimation) snapshotChildAnimations.append(sizeAnimation)
snapshotChildAnimations.append(Motion.position(x: to.bounds.width / 2, y: to.bounds.height / 2)) snapshotChildAnimations.append(Motion.position(x: to.bounds.width / 2, y: to.bounds.height / 2))
let d = motionDuration(animations: to.motionTransitionAnimations) let d = motionDuration(animations: to.motionAnimations)
fromViewController.motionDelegate?.motion?(transition: self, willTransition: toView, fromView: fromView)
let snapshot = from.motionTransitionSnapshot(afterUpdates: true) let snapshot = from.motionSnapshot(afterUpdates: true)
transitionView.addSubview(snapshot) transitionView.addSubview(snapshot)
Motion.delay(motionDelay(animations: to.motionTransitionAnimations)) { [weak self, weak to] in Motion.delay(motionDelay(animations: to.motionAnimations)) { [weak self, weak to] in
guard let s = self else { guard let s = self else {
return return
} }
...@@ -407,7 +404,7 @@ extension MotionTransition { ...@@ -407,7 +404,7 @@ extension MotionTransition {
return return
} }
let tf = s.motionTimingFunction(animations: v.motionTransitionAnimations) let tf = s.motionTimingFunction(animations: v.motionAnimations)
let snapshotGroup = Motion.animate(group: snapshotAnimations, duration: d) let snapshotGroup = Motion.animate(group: snapshotAnimations, duration: d)
snapshotGroup.fillMode = MotionAnimationFillModeToValue(mode: .forwards) snapshotGroup.fillMode = MotionAnimationFillModeToValue(mode: .forwards)
...@@ -426,7 +423,7 @@ extension MotionTransition { ...@@ -426,7 +423,7 @@ extension MotionTransition {
} }
fileprivate func addBackgroundMotionAnimation() { fileprivate func addBackgroundMotionAnimation() {
transitionBackgroundView.motion(.backgroundColor(toView.backgroundColor ?? .clear), .duration(transitionDuration(using: transitionContext))) transitionBackgroundView.motion(.backgroundColor(isPresenting ? toView.backgroundColor ?? .clear : .clear), .duration(transitionDuration(using: transitionContext)))
} }
} }
...@@ -481,29 +478,24 @@ extension MotionTransition { ...@@ -481,29 +478,24 @@ extension MotionTransition {
return return
} }
s.hideToSubviews() s.showToSubviews()
s.clearTransitionView() s.clearTransitionView()
s.clearTransitionPairs() s.clearTransitionPairs()
s.completeTransition() s.completeTransition()
} }
} }
fileprivate func hideFromView() {
Motion.delay(delay) { [weak self] in
self?.fromView.isHidden = true
}
}
fileprivate func removeTransitionSnapshot() { fileprivate func removeTransitionSnapshot() {
Motion.delay(delay) { [weak self] in Motion.delay(delay) { [weak self] in
self?.transitionSnapshot.removeFromSuperview() self?.transitionSnapshot.removeFromSuperview()
} }
} }
fileprivate func hideToSubviews() { fileprivate func showToSubviews() {
toSubviews.forEach { toSubviews.forEach {
$0.isHidden = false $0.isHidden = false
} }
toView.isHidden = false
} }
fileprivate func clearTransitionPairs() { fileprivate func clearTransitionPairs() {
...@@ -518,6 +510,7 @@ extension MotionTransition { ...@@ -518,6 +510,7 @@ extension MotionTransition {
} }
fileprivate func completeTransition() { fileprivate func completeTransition() {
fromViewController.motionDelegate?.motion?(transition: self, didTransition: toView, fromView: fromView)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled) transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
} }
} }
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