Apple uses the following struct to setup PresentationBackgroundInteraction
:
public struct PresentationBackgroundInteraction : Sendable {
public static var automatic: PresentationBackgroundInteraction { get }
public static func enabled(upThrough detent: PresentationDetent) -> PresentationBackgroundInteraction
...
}
It can then be used inside a view modifier like this;
.presentationBackgroundInteraction(.enabled(upThrough(.medium))
If I try to recreate the setup of Apple I run into a couple of problems. Having the function return itself of course creates infinite recursion;
public static func enabled(upThrough detent: PresentationDetent) -> PresentationBackgroundInteraction {
.enabled(upThrough: detent)
}
Assigning it to an internal var can of course mitigate this issue.
However if I would then use it as a view modifier it always returns an instance of the struct rather than the value that I assigned. So I can never alter based on which value was passed to my view modifier.
Struct
public struct PresentationBackgroundInteractionRecreate : Sendable {
public static var automatic: PresentationBackgroundInteractionRecreate = .automatic
}
View
EmptyView()
.presentationBackgroundInteractionRecreate(.automatic)
View Modifier
extension View {
public func presentationBackgroundInteractionRecreate(
_ interaction: presentationBackgroundInteractionRecreate
) -> some View {
// This now always returns; PresentationBackgroundInteractionRecreate()
// How would I be able to access `automatic` here
print(interaction)
return EmptyView()
}
}
Based on some basic reverse-engineering with Mirror
s and dump
ing things out, I've found out that PresentationBackgroundInteraction
is basically a wrapper around an enum, like this:
public struct PresentationBackgroundInteractionRecreate: Hashable, Sendable {
internal enum Kind: Hashable {
case automatic
case disabled
case enabled(upThrough: PresentationDetent?)
}
internal let kind: Kind
internal init(kind: Kind) {
self.kind = kind
}
public static let automatic = Self.init(kind: .automatic)
public static let enabled = Self.init(kind: .enabled(upThrough: nil))
public static let disabled = Self.init(kind: .disabled)
public func enabled(upThrough detent: PresentationDetent) -> Self {
.init(kind: .enabled(upThrough: detent))
}
}
The internal
parts might even be fileprivate
, for all I know.
The view modifier can trivially read the kind
. Reflecting the view returned by presentationBackgroundInteraction
, it seems to use transformPreference
, so I'd imagine the view modifier to look something like this:
extension View {
func presentationBackgroundInteractionRecreate(_ interaction: PresentationBackgroundInteractionRecreate) -> some View {
transformPreference(PresentationOptionsPreferenceKey.self) { value in
switch interaction.kind {
// change "value" in different ways
}
}
}
}
PresentationOptionsPreferenceKey
is another internal
/fileprivate
type. I don't know what the preference key's Value
type (i.e. the type of the value
closure parameter) is, but the point stands.
Then I'd imagine some other parts of SwiftUI would read the preference (again using internal APIs) and set up the UISheetPresentationController
according to those options.
If you were to recreate something similar, you could also create your own PreferenceKey
, and so on.
I can think of some advantages of using structs instead of enums. Using an enum would expose a lot more implementation details than a struct would.
An enum immediately gives away how many different cases it has, and that each case is different, whereas with a struct, .automatic
might as well just return .disabled
under the hood and it'd be "fair game".
Using a struct also prevents people from exhaustively switch
ing on its different cases. This makes adding more types of background interaction easier in the future. On the other hand, an enum strongly suggests to the developer that this is a fixed set of values.