Last active
August 22, 2021 02:24
-
-
Save matt-curtis/825c16c5dd058e009bf996e93eee60c2 to your computer and use it in GitHub Desktop.
AnimatableMemberHaving: A protocol that enables types whose members are made up of Animatable or VectorArithmetic-conforming properties to become Animatable themselves by specifying those members
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
/// A protocol that enables types whose members are made up of `Animatable` or `VectorArithmetic`-conforming properties | |
/// to become `Animatable` themselves by specifying those members. | |
/// | |
/// Here's an example of how to use this protocol: | |
/// | |
/// ``` | |
/// struct CornerRadii: AnimatableMemberHaving { | |
/// | |
/// var topLeftCorner: Double | |
/// var topRightCorner: Double | |
/// var bottomRightCorner: Double | |
/// var bottomLeftCorner: Double | |
/// | |
/// static let animatableMembers = AnimatableMembers | |
/// .append(\Self.topLeftCorner) | |
/// .append(\.topRightCorner) | |
/// .append(\.bottomRightCorner) | |
/// .append(\.bottomLeftCorner) | |
/// | |
/// } | |
/// ``` | |
protocol AnimatableMemberHaving: Animatable { | |
associatedtype AnimatableMemberData: VectorArithmetic | |
static var animatableMembers: AnimatableMembers<Self, AnimatableMemberData> { get } | |
} | |
extension AnimatableMemberHaving { | |
var animatableData: AnimatableMemberData { | |
get { | |
Self.animatableMembers.vectorArithmetic(from: self) | |
} | |
set { | |
Self.animatableMembers.setVectorArithmetic(newValue, on: &self) | |
} | |
} | |
} | |
/// Holds information about a group of members belonging to a specific type. | |
/// Uses nested `AnimatablePair`s to represent the combined `VectorArithmetic` of those members. | |
struct AnimatableMembers<Base, Value: VectorArithmetic> { | |
// MARK: - Properties | |
private let get: (Base) -> Value | |
private let set: (inout Base, Value) -> Void | |
// MARK: - Static append (single member) | |
static func append(_ keyPath: WritableKeyPath<Base, Value>) -> Self { | |
.init( | |
get: { $0[keyPath: keyPath] }, | |
set: { $0[keyPath: keyPath] = $1 } | |
) | |
} | |
static func append<T: Animatable>(_ keyPath: WritableKeyPath<Base, T>) -> Self where Value == T.AnimatableData { | |
let keyPath = keyPath.appending(path: \.animatableData) | |
return .init( | |
get: { $0[keyPath: keyPath] }, | |
set: { $0[keyPath: keyPath] = $1 } | |
) | |
} | |
// MARK: - Composite pairing | |
func append<T: VectorArithmetic>(_ keyPath: WritableKeyPath<Base, T>) -> AnimatableMembers<Base, AnimatablePair<Value, T>> { | |
.init( | |
get: { AnimatablePair(get($0), $0[keyPath: keyPath]) }, | |
set: { set(&$0, $1.first); $0[keyPath: keyPath] = $1.second } | |
) | |
} | |
func append<T: Animatable>(_ keyPath: WritableKeyPath<Base, T>) -> AnimatableMembers<Base, AnimatablePair<Value, T.AnimatableData>> { | |
let keyPath = keyPath.appending(path: \.animatableData) | |
return .init( | |
get: { AnimatablePair(get($0), $0[keyPath: keyPath]) }, | |
set: { set(&$0, $1.first); $0[keyPath: keyPath] = $1.second } | |
) | |
} | |
// MARK: - Getter & setter | |
func vectorArithmetic(from base: Base) -> Value { | |
get(base) | |
} | |
func setVectorArithmetic(_ value: Value, on base: inout Base) { | |
set(&base, value) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment