Skip to content

Commit

Permalink
Update Transaction and EnvironmentValues (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Ye committed Jan 1, 2024
1 parent 3a0ef86 commit ac6fbe5
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 101 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//
// EnvironmentValues.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/9/22.
// Lastest Version: iOS 15.5
// Status: Complete
// ID: 83E729E7BD00420AB79EFD8DF557072A

/// A collection of environment values propagated through a view hierarchy.
///
/// OpenSwiftUI exposes a collection of values to your app's views in an
/// `EnvironmentValues` structure. To read a value from the structure,
/// declare a property using the ``Environment`` property wrapper and
/// specify the value's key path. For example, you can read the current locale:
///
/// @Environment(\.locale) var locale: Locale
///
/// Use the property you declare to dynamically control a view's layout.
/// OpenSwiftUI automatically sets or updates many environment values, like
/// ``EnvironmentValues/pixelLength``, ``EnvironmentValues/scenePhase``, or
/// ``EnvironmentValues/locale``, based on device characteristics, system state,
/// or user settings. For others, like ``EnvironmentValues/lineLimit``, OpenSwiftUI
/// provides a reasonable default value.
///
/// You can set or override some values using the ``View/environment(_:_:)``
/// view modifier:
///
/// MyView()
/// .environment(\.lineLimit, 2)
///
/// The value that you set affects the environment for the view that you modify
/// --- including its descendants in the view hierarchy --- but only up to the
/// point where you apply a different environment modifier.
///
/// OpenSwiftUI provides dedicated view modifiers for setting some values, which
/// typically makes your code easier to read. For example, rather than setting
/// the ``EnvironmentValues/lineLimit`` value directly, as in the previous
/// example, you should instead use the ``View/lineLimit(_:)-513mb`` modifier:
///
/// MyView()
/// .lineLimit(2)
///
/// In some cases, using a dedicated view modifier provides additional
/// functionality. For example, you must use the
/// ``View/preferredColorScheme(_:)`` modifier rather than setting
/// ``EnvironmentValues/colorScheme`` directly to ensure that the new
/// value propagates up to the presenting container when presenting a view
/// like a popover:
///
/// MyView()
/// .popover(isPresented: $isPopped) {
/// PopoverContent()
/// .preferredColorScheme(.dark)
/// }
///
/// Create custom environment values by defining a type that
/// conforms to the ``EnvironmentKey`` protocol, and then extending the
/// environment values structure with a new property. Use your key to get and
/// set the value, and provide a dedicated modifier for clients to use when
/// setting the value:
///
/// private struct MyEnvironmentKey: EnvironmentKey {
/// static let defaultValue: String = "Default value"
/// }
///
/// extension EnvironmentValues {
/// var myCustomValue: String {
/// get { self[MyEnvironmentKey.self] }
/// set { self[MyEnvironmentKey.self] = newValue }
/// }
/// }
///
/// extension View {
/// func myCustomValue(_ myCustomValue: String) -> some View {
/// environment(\.myCustomValue, myCustomValue)
/// }
/// }
///
/// Clients of your value then access the value in the usual way, reading it
/// with the ``Environment`` property wrapper, and setting it with the
/// `myCustomValue` view modifier.
public struct EnvironmentValues: CustomStringConvertible {
/// Creates an environment values instance.
///
/// You don't typically create an instance of ``EnvironmentValues``
/// directly. Doing so would provide access only to default values that
/// don't update based on system settings or device characteristics.
/// Instead, you rely on an environment values' instance
/// that OpenSwiftUI manages for you when you use the ``Environment``
/// property wrapper and the ``View/environment(_:_:)`` view modifier.
public init() {
_plist = PropertyList()
tracker = nil
}

/// Accesses the environment value associated with a custom key.
///
/// Create custom environment values by defining a key
/// that conforms to the ``EnvironmentKey`` protocol, and then using that
/// key with the subscript operator of the ``EnvironmentValues`` structure
/// to get and set a value for that key:
///
/// private struct MyEnvironmentKey: EnvironmentKey {
/// static let defaultValue: String = "Default value"
/// }
///
/// extension EnvironmentValues {
/// var myCustomValue: String {
/// get { self[MyEnvironmentKey.self] }
/// set { self[MyEnvironmentKey.self] = newValue }
/// }
/// }
///
/// You use custom environment values the same way you use system-provided
/// values, setting a value with the ``View/environment(_:_:)`` view
/// modifier, and reading values with the ``Environment`` property wrapper.
/// You can also provide a dedicated view modifier as a convenience for
/// setting the value:
///
/// extension View {
/// func myCustomValue(_ myCustomValue: String) -> some View {
/// environment(\.myCustomValue, myCustomValue)
/// }
/// }
///
public subscript<K: EnvironmentKey>(key: K.Type) -> K.Value {
get {
if let tracker {
tracker.value(_plist, for: EnvironmentPropertyKey<K>.self)
} else {
_plist[EnvironmentPropertyKey<K>.self]
}
}
set {
_plist[EnvironmentPropertyKey<K>.self] = newValue
if let tracker {
tracker.invalidateValue(
for: EnvironmentPropertyKey<K>.self,
from: _plist,
to: _plist
)
}
}
}

/// A string that represents the contents of the environment values
/// instance.
public var description: String { _plist.description }

private var _plist: PropertyList
private let tracker: PropertyList.Tracker?

init(plist: PropertyList) {
_plist = plist
tracker = nil
}
}

private struct EnvironmentPropertyKey<EnvKey: EnvironmentKey>: PropertyKey {
static var defaultValue: EnvKey.Value { EnvKey.defaultValue }
}

private struct DerivedEnvironmentPropertyKey<EnvKey: DerivedEnvironmentKey>: DerivedPropertyKey {
static func value(in plist: PropertyList) -> some Equatable {
EnvKey.value(in: EnvironmentValues(plist: plist))
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ struct PropertyList: CustomStringConvertible {
return
}
elements.forEach { element, stop in
fatalError("TODO")
let element = element.takeUnretainedValue()
guard element.keyType == Key.self else {
return
}
body((element as! TypedElement<Key>).value, &stop)
}
}

Expand Down
43 changes: 43 additions & 0 deletions Sources/OpenSwiftUI/Views/Transaction/Transaction+Animation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// Transaction+Animation.swift
// OpenSwiftUI
//
// Created by Kyle on 2024/1/1.
// Lastest Version: iOS 15.5
// Status: Complete
// ID: 39EC6D46662E6D7A6963F5C611934B0A

private struct AnimationKey: TransactionKey {
static let defaultValue: Animation? = nil
}

private struct DisablesAnimationsKey: TransactionKey {
static var defaultValue: Bool { false }
}

extension Transaction {
/// Creates a transaction and assigns its animation property.
///
/// - Parameter animation: The animation to perform when the current state
/// changes.
public init(animation: Animation?) {
self.plist = PropertyList()
self.animation = animation
}

/// The animation, if any, associated with the current state change.
public var animation: Animation? {
get { plist[Key<AnimationKey>.self] }
set { plist[Key<AnimationKey>.self] = newValue }
}

/// A Boolean value that indicates whether views should disable animations.
///
/// This value is `true` during the initial phase of a two-part transition
/// update, to prevent ``View/animation(_:)`` from inserting new animations
/// into the transaction.
public var disablesAnimations: Bool {
get { plist[Key<DisablesAnimationsKey>.self] }
set { plist[Key<DisablesAnimationsKey>.self] = newValue }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Transaction+isContinuous.swift
// OpenSwiftUI
//
// Created by Kyle on 2024/1/1.
// Lastest Version: iOS 15.5
// Status: Complete
// ID: 73F9B1285B58E062EB66BE983530A970

private struct ContinuousKey: TransactionKey {
static var defaultValue: Bool { false }
}

extension Transaction {
/// A Boolean value that indicates whether the transaction originated from
/// an action that produces a sequence of values.
///
/// This value is `true` if a continuous action created the transaction, and
/// is `false` otherwise. Continuous actions include things like dragging a
/// slider or pressing and holding a stepper, as opposed to tapping a
/// button.
public var isContinuous: Bool {
get { plist[Key<ContinuousKey>.self] }
set { plist[Key<ContinuousKey>.self] = newValue }
}
}
Loading

0 comments on commit ac6fbe5

Please sign in to comment.