From b4563fdb313697dbad058eb04bad1623729c962b Mon Sep 17 00:00:00 2001 From: Nick Entin Date: Tue, 15 Sep 2020 19:44:27 -0700 Subject: [PATCH] Remove the collection keyframes feature This feature was never included in the public API and still had a lot of work left in order to get it working properly (in particular around interacting with other animation content). No progress has been made on this feature in the last 9 months and AFAIK there is no work planned for this in the near future. This removes it from the framework for now to simplify things. --- Example/Stagehand.xcodeproj/project.pbxproj | 4 - .../CollectionKeyframesViewController.swift | 203 ---------------- .../PerformanceBenchmarkViewController.swift | 29 +-- Example/Stagehand/RootViewController.swift | 1 - .../Animation/Animation+Optimization.swift | 2 - Sources/Stagehand/Animation/Animation.swift | 226 ------------------ 6 files changed, 1 insertion(+), 464 deletions(-) delete mode 100644 Example/Stagehand/CollectionKeyframesViewController.swift diff --git a/Example/Stagehand.xcodeproj/project.pbxproj b/Example/Stagehand.xcodeproj/project.pbxproj index 715209c..39c64c9 100644 --- a/Example/Stagehand.xcodeproj/project.pbxproj +++ b/Example/Stagehand.xcodeproj/project.pbxproj @@ -32,7 +32,6 @@ 3D8E3D8222F17D3B00D70FCB /* AnimationCancelationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D8E3D8122F17D3B00D70FCB /* AnimationCancelationViewController.swift */; }; 3D8E3D8422F2BC9300D70FCB /* RepeatingAnimationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D8E3D8322F2BC9300D70FCB /* RepeatingAnimationsViewController.swift */; }; 3DA3997C230FCFAC00DE41A0 /* ExecutionBlockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA3997B230FCFAC00DE41A0 /* ExecutionBlockViewController.swift */; }; - 3DA5EB7C2318E64A001DF944 /* CollectionKeyframesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA5EB7B2318E64A001DF944 /* CollectionKeyframesViewController.swift */; }; 3DB3927D23249D680009E8B3 /* ColorAnimationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DB3927C23249D680009E8B3 /* ColorAnimationsViewController.swift */; }; 3DBFEE422400B2460086D61C /* ChildAnimationProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBFEE412400B2460086D61C /* ChildAnimationProgressViewController.swift */; }; 3DC75494232D82FD00402BD9 /* ChildAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC75492232D819700402BD9 /* ChildAnimationTests.swift */; }; @@ -103,7 +102,6 @@ 3D8E3D8122F17D3B00D70FCB /* AnimationCancelationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationCancelationViewController.swift; sourceTree = ""; }; 3D8E3D8322F2BC9300D70FCB /* RepeatingAnimationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingAnimationsViewController.swift; sourceTree = ""; }; 3DA3997B230FCFAC00DE41A0 /* ExecutionBlockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExecutionBlockViewController.swift; sourceTree = ""; }; - 3DA5EB7B2318E64A001DF944 /* CollectionKeyframesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionKeyframesViewController.swift; sourceTree = ""; }; 3DB3927C23249D680009E8B3 /* ColorAnimationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorAnimationsViewController.swift; sourceTree = ""; }; 3DBFEE412400B2460086D61C /* ChildAnimationProgressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChildAnimationProgressViewController.swift; sourceTree = ""; }; 3DC75492232D819700402BD9 /* ChildAnimationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChildAnimationTests.swift; sourceTree = ""; }; @@ -200,7 +198,6 @@ 3D8E3D8322F2BC9300D70FCB /* RepeatingAnimationsViewController.swift */, 3DA3997B230FCFAC00DE41A0 /* ExecutionBlockViewController.swift */, 3DEE4409231331DD0057D796 /* AnimationGroupViewController.swift */, - 3DA5EB7B2318E64A001DF944 /* CollectionKeyframesViewController.swift */, 3D875EFF2323542500670803 /* PerformanceBenchmarkViewController.swift */, 3D2712ED236A17C5001D3B4B /* AnimationQueueViewController.swift */, 3D2C05902489F807003F5A4B /* CGAffineTransformDebuggingViewController.swift */, @@ -601,7 +598,6 @@ 607FACD81AFB9204008FA782 /* RootViewController.swift in Sources */, B3C4CA092459F75800893CB5 /* ShapeLayerUtils.swift in Sources */, 3DBFEE422400B2460086D61C /* ChildAnimationProgressViewController.swift in Sources */, - 3DA5EB7C2318E64A001DF944 /* CollectionKeyframesViewController.swift in Sources */, 3DA3997C230FCFAC00DE41A0 /* ExecutionBlockViewController.swift in Sources */, 3D2712EE236A17C5001D3B4B /* AnimationQueueViewController.swift in Sources */, 3D8E3D8222F17D3B00D70FCB /* AnimationCancelationViewController.swift in Sources */, diff --git a/Example/Stagehand/CollectionKeyframesViewController.swift b/Example/Stagehand/CollectionKeyframesViewController.swift deleted file mode 100644 index 54b95b9..0000000 --- a/Example/Stagehand/CollectionKeyframesViewController.swift +++ /dev/null @@ -1,203 +0,0 @@ -// -// Copyright 2019 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import UIKit - -// This file is testing the collection keyframes feature of Stagehand, which is not yet ready for release. Use a -// testable import here so we can call the internal methods. -@testable import Stagehand - -final class CollectionKeyframesViewController: DemoViewController { - - // MARK: - Life Cycle - - override init() { - super.init() - - contentView = mainView - - animationRows = [ - ("Bounce All", { [unowned self] in - self.reset() - - var animation = Animation() - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: 0, - value: .identity - ) - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: 0.5, - value: .init(translationX: 0, y: -self.mainView.bounds.height / 2) - ) - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: 1, - value: .identity - ) - - self.animationInstance = animation.perform(on: self.mainView) - }), - ("Bounce Odd Views", { [unowned self] in - self.reset() - - var animation = Animation() - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.oddViews, - at: 0, - value: .identity - ) - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.oddViews, - at: 0.5, - value: .init(translationX: 0, y: -self.mainView.bounds.height / 2) - ) - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.oddViews, - at: 1, - value: .identity - ) - - self.animationInstance = animation.perform(on: self.mainView) - }), - ("Bounce In Order", { [unowned self] in - self.reset() - - var animation = Animation() - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: { index, count in 0.33 * Double(index) / Double(count - 1) }, - value: .identity - ) - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: { index, count in 0.33 + 0.33 * Double(index) / Double(count - 1) }, - value: .init(translationX: 0, y: -self.mainView.bounds.height / 2) - ) - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: { index, count in 0.67 + 0.33 * Double(index) / Double(count - 1) }, - value: .identity - ) - - self.animationInstance = animation.perform(on: self.mainView) - }), - ("Bounce All (in Child Animation)", { [unowned self] in - self.reset() - - var animation = Animation() - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: 0, - value: .identity - ) - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: 0.5, - value: .init(translationX: 0, y: -self.mainView.bounds.height / 2) - ) - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.animatableViews, - at: 1, - value: .identity - ) - - var parentAnimation = Animation() - parentAnimation.addChild(animation, for: \.self, startingAt: 0, relativeDuration: 1) - - self.animationInstance = parentAnimation.perform(on: self.mainView) - }), - ] - } - - // MARK: - Private Properties - - private let mainView: View = .init() - - private var animationInstance: AnimationInstance? - - // MARK: - Private Methods - - private func reset() { - animationInstance?.cancel() - animationInstance = nil - - mainView.animatableViews.forEach { $0.transform = .identity } - } - -} - -// MARK: - - -extension CollectionKeyframesViewController { - - final class View: UIView { - - // MARK: - Life Cycle - - override init(frame: CGRect) { - super.init(frame: frame) - - animatableViews.forEach { view in - view.bounds.size = .init(width: 40, height: 40) - view.backgroundColor = .red - addSubview(view) - } - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Public Properties - - let animatableViews: [UIView] = (0..<5).map { _ in UIView() } - - var oddViews: [UIView] { - return animatableViews - .enumerated() - .filter { $0.0 % 2 != 0 } - .map { $0.1 } - } - - // MARK: - UIView - - override func layoutSubviews() { - for (index, view) in animatableViews.enumerated() { - view.center = .init( - x: bounds.minX + 50 + (CGFloat(index) * (bounds.width - 100) / CGFloat(animatableViews.count - 1)), - y: bounds.height * 3 / 4 - ) - } - } - - } - -} diff --git a/Example/Stagehand/PerformanceBenchmarkViewController.swift b/Example/Stagehand/PerformanceBenchmarkViewController.swift index 4652a2f..14b770f 100644 --- a/Example/Stagehand/PerformanceBenchmarkViewController.swift +++ b/Example/Stagehand/PerformanceBenchmarkViewController.swift @@ -14,12 +14,9 @@ // limitations under the License. // +import Stagehand import UIKit -// This file is testing the collection keyframes feature of Stagehand, which is not yet ready for release. Use a -// testable import here so we can call the internal methods. -@testable import Stagehand - final class PerformanceBenchmarkViewController: DemoViewController { // MARK: - Life Cycle @@ -35,30 +32,6 @@ final class PerformanceBenchmarkViewController: DemoViewController { self.animationInstances.forEach { $0.cancel(behavior: .revert) } self.animationInstances = [] }), - ("Add Rotating Children Animation", { [unowned self] in - var animation = Animation() - for degree in 0...360 { - animation.addKeyframe( - for: \.transform, - ofElementsIn: \.rotatingChildViews, - at: { index, count in - let adjustedByIndex = Double(degree) / 360 + Double(index) / Double(count) - return adjustedByIndex - floor(adjustedByIndex) - }, - value: CGAffineTransform.identity - .translatedBy( - x: View.radius * cos(CGFloat(degree) * .pi / 180), - y: View.radius * sin(CGFloat(degree) * .pi / 180) - ) - .rotated(by: CGFloat(degree) * .pi / 180) - ) - } - - animation.duration = 4 - animation.repeatStyle = .infinitelyRepeating(autoreversing: false) - - self.animationInstances.append(animation.perform(on: self.mainView)) - }), ("Add Rotating Center View Animation", { [unowned self] in var animation = Animation() diff --git a/Example/Stagehand/RootViewController.swift b/Example/Stagehand/RootViewController.swift index 80c525f..593bd51 100644 --- a/Example/Stagehand/RootViewController.swift +++ b/Example/Stagehand/RootViewController.swift @@ -46,7 +46,6 @@ final class RootViewController: UITableViewController { ("Repeating Animations", { RepeatingAnimationsViewController() }), ("Execution Blocks", { ExecutionBlockViewController() }), ("Animation Groups", { AnimationGroupViewController() }), - ("Collection Keyframes", { CollectionKeyframesViewController() }), ("Performance Benchmark", { PerformanceBenchmarkViewController() }), ("Animation Queues", { AnimationQueueViewController() }), ("CGAffineTransform Debugging", { CGAffineTransformDebuggingViewController() }), diff --git a/Sources/Stagehand/Animation/Animation+Optimization.swift b/Sources/Stagehand/Animation/Animation+Optimization.swift index c2195c0..663abbd 100644 --- a/Sources/Stagehand/Animation/Animation+Optimization.swift +++ b/Sources/Stagehand/Animation/Animation+Optimization.swift @@ -51,7 +51,6 @@ extension Animation { // container for child animations. guard keyframeSeriesByProperty.isEmpty - && collectionKeyframeSeriesByProperty.isEmpty && assignments.isEmpty && executionBlocks.isEmpty && perFrameExecutionBlocks.isEmpty @@ -124,7 +123,6 @@ extension Animation { private mutating func removeEmptyChildren() { self.children = children.filter { child in return !child.animation.keyframeSeriesByProperty.isEmpty - || !child.animation.collectionKeyframeSeriesByProperty.isEmpty || !child.animation.assignments.isEmpty || !child.animation.executionBlocks.isEmpty || !child.animation.perFrameExecutionBlocks.isEmpty diff --git a/Sources/Stagehand/Animation/Animation.swift b/Sources/Stagehand/Animation/Animation.swift index ef43ef9..35aa7a1 100644 --- a/Sources/Stagehand/Animation/Animation.swift +++ b/Sources/Stagehand/Animation/Animation.swift @@ -111,8 +111,6 @@ public struct Animation { internal var keyframeSeriesByProperty: [PartialKeyPath: AnyKeyframeSeries] = [:] - internal private(set) var collectionKeyframeSeriesByProperty: [CollectionKeyframeSeriesKey: AnyCollectionKeyframeSeries] = [:] - internal private(set) var assignments: [Assignment] = [] internal private(set) var executionBlocks: [ExecutionBlock] = [] @@ -184,68 +182,6 @@ public struct Animation { } } - /// Add a keyframe for the given `property` of each element in the given `collection` with a fixed value. - /// - /// - Note: Collection keyframes are still a work in progress, and so are currently `internal`. Once they have been - /// fully implemented and documented, this method will be changed to be `public`. - /// - /// - parameter property: The key path for the property to be animated. - /// - parameter collection: The key path for the collection containing the elements to be animated. - /// - parameter relativeTimestamp: The relative timestamp at which this should be the value of the property. Must - /// be in the range [0,1], where 0 is the beginning of the animation and 1 is the end. - /// - parameter value: The value of the property at this keyframe. - internal mutating func addKeyframe( - for property: WritableKeyPath, - ofElementsIn collection: KeyPath, - at relativeTimestamp: Double, - value: PropertyType - ) { - addKeyframe( - for: property, - ofElementsIn: collection, - at: { _, _ in relativeTimestamp }, - value: value - ) - } - - /// Add a keyframe for the given `property` of each element in the given `collection` with a fixed value. - /// - /// - Note: Collection keyframes are still a work in progress, and so are currently `internal`. Once they have been - /// fully implemented and documented, this method will be changed to be `public`. - /// - /// - parameter property: The key path for the property to be animated. - /// - parameter collection: The key path for the collection containing the elements to be animated. - /// - parameter relativeTimestamp: A block to calculate the relative timestamp at which this should be the value of - /// the property, based on the element's position in the collection. The return value of this block must be in the - /// range [0,1], where 0 is the beginning of the animation and 1 is the end. - /// - parameter value: The value of the property at this keyframe. - internal mutating func addKeyframe( - for property: WritableKeyPath, - ofElementsIn collection: KeyPath, - at relativeTimestamp: @escaping (_ index: Int, _ count: Int) -> Double, - value: PropertyType - ) { - let key = CollectionKeyframeSeriesKey(collection: collection, property: property) - - let keyframe = CollectionKeyframeSeries.Keyframe( - relativeTimestamp: relativeTimestamp, - value: value - ) - - if var keyframeSeries = collectionKeyframeSeriesByProperty[key] as? CollectionKeyframeSeries { - keyframeSeries.keyframes.append(keyframe) - collectionKeyframeSeriesByProperty[key] = keyframeSeries - - } else { - let keyframeSeries = CollectionKeyframeSeries( - collection: collection, - property: property, - keyframes: [keyframe] - ) - collectionKeyframeSeriesByProperty[key] = keyframeSeries - } - } - /// Add an assignment for the given `property` at the `relativeTimestamp`. /// /// When the animation is run in reverse, the property will be returned to its value prior to the assignment. @@ -366,17 +302,6 @@ public struct Animation { child.animation.keyframeSeriesByProperty[property] = keyframeSeries } - // Map the child's collection keyframes into the child animation. - for (_, childKeyframeSeries) in childAnimation.collectionKeyframeSeriesByProperty { - let (key, keyframeSeries) = childKeyframeSeries.mapForParentElement( - subelement, - relativeStartTimestamp: relativeStartTimestamp, - relativeDuration: relativeDuration - ) - - child.animation.collectionKeyframeSeriesByProperty[key] = keyframeSeries - } - // Map the child's property assignments into the child animation. child.animation.assignments = childAnimation.assignments.map { childAssignment in return Assignment( @@ -524,13 +449,6 @@ public struct Animation { initialValue: initialValues[series.key]! ) } - - for collectionSeries in self.collectionKeyframeSeriesByProperty { - collectionSeries.value.applyToElement( - &element, - at: adjustedRelativeTimestamp - ) - } } /// Applies the first value of each animatable property (those defined by keyframes, _not_ including collection @@ -702,150 +620,6 @@ internal protocol AnyKeyframeSeries { // MARK: - -extension Animation { - - private struct CollectionKeyframeSeries: AnyCollectionKeyframeSeries { - - // MARK: - Public Types - - struct Keyframe { - - var relativeTimestamp: (Int, Int) -> Double - - var value: PropertyType - - } - - // MARK: - Public Properties - - var collection: KeyPath - - var property: WritableKeyPath - - var keyframes: [Keyframe] - - // MARK: - Public Methods - - func apply(to element: inout ElementType, at relativeTimestamp: Double) { - let collection = element[keyPath: self.collection] - for (index, var item) in collection.enumerated() { - let valuesByRelativeTimestamp = Dictionary( - keyframes.map { ($0.relativeTimestamp(index, collection.count), $0.value) }, - uniquingKeysWith: { $1 } - ) - - if let value = valuesByRelativeTimestamp[relativeTimestamp] { - item[keyPath: property] = value - - } else { - let values = valuesByRelativeTimestamp.sorted { $0.key < $1.key } - - guard let previousIndex = values.lastIndex(where: { $0.key < relativeTimestamp }) else { - item[keyPath: property] = values.first!.value - continue - } - - let (previousTimestamp, previousValue) = values[previousIndex] - - let nextIndex = values.index(after: previousIndex) - guard nextIndex != values.endIndex else { - item[keyPath: property] = previousValue - continue - } - - let (nextTimestamp, nextValue) = values[nextIndex] - - item[keyPath: property] = PropertyType.value( - between: previousValue, - and: nextValue, - at: ((relativeTimestamp - previousTimestamp) / (nextTimestamp - previousTimestamp)) - ) - } - } - } - - func mapForParent( - _ subelementPath: WritableKeyPath, - relativeStartTimestamp: Double, - relativeDuration: Double - ) -> (Animation.CollectionKeyframeSeriesKey, Animation.CollectionKeyframeSeries) { - let reinterpolatedKeyframes = keyframes.map { keyframe in - return Animation.CollectionKeyframeSeries.Keyframe( - relativeTimestamp: { index, count in - let initialRelativeTimestamp = keyframe.relativeTimestamp(index, count) - return (relativeStartTimestamp + (initialRelativeTimestamp * relativeDuration)) - }, - value: keyframe.value - ) - } - - let mappedCollectionPath: KeyPath = subelementPath.appending(path: collection) - - return ( - .init( - collection: mappedCollectionPath, - property: property - ), - .init( - collection: mappedCollectionPath, - property: property, - keyframes: reinterpolatedKeyframes - ) - ) - } - - // MARK: - AnyCollectionKeyframeSeries - - var collectionPath: AnyKeyPath { - return collection - } - - func applyToElement(_ element: inout AnyObject, at relativeTimestamp: Double) { - var element = element as! ElementType - apply(to: &element, at: relativeTimestamp) - } - - func mapForParentElement( - _ subelementPath: PartialKeyPath, - relativeStartTimestamp: Double, - relativeDuration: Double - ) -> (Animation.CollectionKeyframeSeriesKey, AnyCollectionKeyframeSeries) { - let (key, keyframeSeries) = mapForParent( - subelementPath as! WritableKeyPath, - relativeStartTimestamp: relativeStartTimestamp, - relativeDuration: relativeDuration - ) - return (key, keyframeSeries) - } - - } - - internal struct CollectionKeyframeSeriesKey: Hashable { - - var collection: PartialKeyPath - - var property: AnyKeyPath - - } - -} - -internal protocol AnyCollectionKeyframeSeries { - - var collectionPath: AnyKeyPath { get } - - func applyToElement(_ element: inout AnyObject, at relativeTimestamp: Double) - - func mapForParentElement( - _ subelementPath: PartialKeyPath, - relativeStartTimestamp: Double, - relativeDuration: Double - ) -> (Animation.CollectionKeyframeSeriesKey, AnyCollectionKeyframeSeries) - -} - -// MARK: - - extension Animation { internal struct Assignment {