Commit 732bcdb5 by Daniel Dahan

progress on new Motion structure

parent 3d9641b4
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0900"
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......
......@@ -220,6 +220,7 @@ internal class SnapshotWrapperView: UIView {
init(contentView: UIView) {
self.contentView = contentView
super.init(frame: contentView.frame)
contentView.frame = bounds
addSubview(contentView)
}
......@@ -229,8 +230,7 @@ internal class SnapshotWrapperView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
contentView.bounds.size = bounds.size
contentView.center = bounds.center
contentView.frame = bounds
}
}
......@@ -243,13 +243,17 @@ internal extension UIView {
if #available(iOS 9.0, *), isHidden && (superview is UICollectionView || superview is UIStackView || self is UITableViewCell) {
return []
} else if isHidden && (superview is UICollectionView || self is UITableViewCell) {
return []
} else if isMotionEnabledForSubviews {
return [self] + subviews.flatMap { $0.flattenedViewHierarchy }
} else {
return [self]
return [self] + subviews.flatMap {
$0.flattenedViewHierarchy
}
}
return [self]
}
/**
......@@ -285,7 +289,13 @@ internal extension UIView {
*/
func slowSnapshotView() -> UIView {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
layer.render(in: UIGraphicsGetCurrentContext()!)
guard let currentContext = UIGraphicsGetCurrentContext() else {
UIGraphicsEndImageContext()
return UIView()
}
layer.render(in: currentContext)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
......
/*
* 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 UIKit
extension Motion {
/**
Complete the transition.
- Parameter after: A TimeInterval.
- Parameter isFinished: A Boolean indicating if the transition
has completed.
*/
func complete(after: TimeInterval, isFinished: Bool) {
guard isTransitioning else {
return
}
if after <= 0.001 {
complete(isFinished: isFinished)
return
}
let v = (isFinished ? elapsedTime : 1 - elapsedTime) * totalDuration
self.isFinished = isFinished
currentAnimationDuration = after + v
beginTime = CACurrentMediaTime() - v
}
/**
Complete the transition.
- Parameter isFinished: A Boolean indicating if the transition
has completed.
*/
@objc
func complete(isFinished: Bool) {
if state == .notified {
forceFinishing = isFinished
}
guard .animating == state || .starting == state else {
return
}
defer {
transitionContext = nil
fromViewController = nil
toViewController = nil
isNavigationController = false
isTabBarController = false
forceNonInteractive = false
transitionPairs.removeAll()
transitionObservers = nil
transitionContainer = nil
completionCallback = nil
forceFinishing = nil
container = nil
startingProgress = nil
preprocessors.removeAll()
animators.removeAll()
plugins.removeAll()
context = nil
elapsedTime = 0
totalDuration = 0
state = .possible
}
state = .completing
progressRunner.stop()
context.clean()
if let tv = toView, let fv = fromView {
if isFinished && isPresenting && toOverFullScreen {
// finished presenting a overFullScreen view controller.
context.unhide(rootView: tv)
context.removeSnapshots(rootView: tv)
context.storeViewAlpha(rootView: fv)
fromViewController!.motionStoredSnapshot = container
fv.removeFromSuperview()
fv.addSubview(container)
} else if !isFinished && !isPresenting && fromOverFullScreen {
// Cancelled dismissing a overFullScreen view controller.
context.unhide(rootView: fv)
context.removeSnapshots(rootView: fv)
context.storeViewAlpha(rootView: tv)
toViewController!.motionStoredSnapshot = container
container.superview?.addSubview(tv)
tv.addSubview(container)
} else {
context.unhideAll()
context.removeAllSnapshots()
}
// Move fromView & toView back from our container back to the one supplied by UIKit.
if (toOverFullScreen && isFinished) || (fromOverFullScreen && !isFinished) {
transitionContainer?.addSubview(isFinished ? fv : tv)
}
transitionContainer?.addSubview(isFinished ? tv : fv)
if isPresenting != isFinished, !isContainerController {
// Only happens when present a .overFullScreen view controller.
// bug: http://openradar.appspot.com/radar?id=5320103646199808
UIApplication.shared.keyWindow?.addSubview(isPresenting ? fv : tv)
}
}
if container.superview == transitionContainer {
container.removeFromSuperview()
}
for a in animators {
a.clean()
}
transitionContainer?.isUserInteractionEnabled = true
completionCallback?(isFinished)
let tContext = transitionContext
let fvc = fromViewController
let tvc = toViewController
if isFinished {
processEndTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
} else {
processCancelTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
tContext?.cancelInteractiveTransition()
}
tContext?.completeTransition(isFinished)
}
}
......@@ -77,6 +77,7 @@ internal extension MotionContext {
func set(fromViews: [UIView], toViews: [UIView]) {
self.fromViews = fromViews
self.toViews = toViews
map(views: fromViews, identifierMap: &motionIdentifierToSourceView)
map(views: toViews, identifierMap: &motionIdentifierToDestinationView)
}
......@@ -114,8 +115,8 @@ public extension MotionContext {
get {
return viewToTargetState[view]
}
set {
viewToTargetState[view] = newValue
set(value) {
viewToTargetState[view] = value
}
}
}
......@@ -185,6 +186,7 @@ public extension MotionContext {
if let visualEffectView = containerView as? UIVisualEffectView {
containerView = visualEffectView.contentView
}
case .global:
break
}
......@@ -221,8 +223,8 @@ public extension MotionContext {
case .optimized:
#if os(tvOS)
snapshot = view.snapshotView(afterScreenUpdates: true)!
#else
#else
if #available(iOS 9.0, *), let stackView = view as? UIStackView {
snapshot = stackView.slowSnapshotView()
......@@ -246,7 +248,6 @@ public extension MotionContext {
// take a snapshot without the background
barView.layer.sublayers![0].opacity = 0
let realSnapshot = barView.snapshotView(afterScreenUpdates: true)!
barView.layer.sublayers![0].opacity = 1
......@@ -260,6 +261,7 @@ public extension MotionContext {
} else {
snapshot = view.snapshotView() ?? UIView()
}
#endif
}
......@@ -267,6 +269,7 @@ public extension MotionContext {
if let imageView = view as? UIImageView, imageView.adjustsImageWhenAncestorFocused {
snapshot.frame = imageView.focusedFrameGuide.layoutFrame
}
#endif
view.layer.cornerRadius = oldCornerRadius
......@@ -323,6 +326,7 @@ public extension MotionContext {
for sibling in nextSiblings {
insertGlobalViewTree(view: sibling)
}
} else {
containerView.addSubview(snapshot)
}
......@@ -363,7 +367,7 @@ internal extension MotionContext {
- Parameter view: A UIView.
*/
func hide(view: UIView) {
guard nil == viewToAlphas[view], .noSnapshot != self[view]?.snapshotType else {
guard nil == viewToAlphas[view] else {
return
}
......@@ -372,7 +376,7 @@ internal extension MotionContext {
viewToAlphas[view] = 1
} else {
viewToAlphas[view] = view.isOpaque ? .infinity : view.alpha
viewToAlphas[view] = view.alpha
view.alpha = 0
}
}
......@@ -389,10 +393,6 @@ internal extension MotionContext {
if view is UIVisualEffectView {
view.isHidden = false
} else if oldAlpha == .infinity {
view.alpha = 1
view.isOpaque = true
} else {
view.alpha = oldAlpha
}
......
/*
* 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 UIKit
protocol MotionProgressRunnerDelegate: class {
func update(elapsedTime: TimeInterval)
func complete(isFinished: Bool)
}
class MotionProgressRunner {
weak var delegate: MotionProgressRunnerDelegate?
var isRunning: Bool {
return displayLink != nil
}
internal var timePassed: TimeInterval = 0
internal var duration: TimeInterval = 0
internal var displayLink: CADisplayLink?
internal var isReversed: Bool = false
@objc
func displayUpdate(_ link: CADisplayLink) {
timePassed += isReversed ? -link.duration : link.duration
if isReversed, timePassed <= 1.0 / 120 {
delegate?.complete(isFinished: false)
stop()
return
}
if !isReversed, timePassed > duration - 1.0 / 120 {
delegate?.complete(isFinished: true)
stop()
return
}
delegate?.update(elapsedTime: timePassed / duration)
}
func start(timePassed: TimeInterval, totalTime: TimeInterval, reverse: Bool) {
stop()
self.timePassed = timePassed
self.isReversed = reverse
self.duration = totalTime
displayLink = CADisplayLink(target: self, selector: #selector(displayUpdate(_:)))
displayLink!.add(to: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
}
func stop() {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
displayLink = nil
}
}
......@@ -26,42 +26,23 @@
* THE SOFTWARE.
*/
import UIKit
public class MotionIndependentController: MotionController {
/// An initializer.
public override init() {
super.init()
}
/**
Transitions source views to their corresponding destination view
within a given root view.
- Parameter rootView: A UIView.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
- Parameter completion: An optional callback.
*/
public func transition(rootView: UIView, fromViews: [UIView], toViews: [UIView], completion: ((Bool) -> Void)? = nil) {
transitionContainer = rootView
completionCallback = completion
prepareTransition()
prepareContext(fromViews: fromViews, toViews: toViews)
prepareTransitionPairs()
animate()
}
@objc(MotionState)
public enum MotionState: Int {
/// Motion is able to start a new transition.
case possible
/// UIKit has notified Motion about a pending transition.
/// Motion hasn't started preparation.
case notified
/// Motion's `start` method has been called. Preparing the animation.
case starting
/// Motions's `animate` method has been called. Animation has started.
case animating
/// Motions's `complete` method has been called. Transition has ended or has
/// been cancelled. Motion is cleaning up.
case completing
}
fileprivate extension MotionIndependentController {
/**
Prepares the context.
- Parameter fromViews: An Array of UIViews.
- PArameter toViews: An Array of UIViews.
*/
func prepareContext(fromViews: [UIView], toViews: [UIView]) {
context.set(fromViews: fromViews, toViews: toViews)
processContext()
}
}
......@@ -218,7 +218,7 @@ class TransitionPreprocessor: MotionPreprocessor {
if case .auto = defaultAnimation {
if isNavigationController, let navAnim = toViewController?.navigationController?.motionNavigationTransitionType {
defaultAnimation = navAnim
} else if isTabBarController, let tabAnim = toViewController?.tabBarController?.motionTabBarTransitionType {
defaultAnimation = tabAnim
......
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