-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b4201ba
commit 989cb78
Showing
9 changed files
with
297 additions
and
58 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
168 changes: 168 additions & 0 deletions
168
Sources/Turbocharger/Sources/View/PlatformViewRepresentable.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// | ||
// Copyright (c) Nathan Tannar | ||
// | ||
|
||
import SwiftUI | ||
import Engine | ||
|
||
#if !os(watchOS) | ||
|
||
/// A protocol for defining a `NSViewRepresentable`/`UIViewRepresentable` | ||
/// that has a backwards compatible `sizeThatFits` | ||
public protocol PlatformViewRepresentable: DynamicProperty, View where Body == Never { | ||
|
||
#if os(macOS) | ||
associatedtype PlatformView: NSView | ||
#else | ||
associatedtype PlatformView: UIView | ||
#endif | ||
|
||
@MainActor(unsafe) func makeView(context: Context) -> PlatformView | ||
@MainActor(unsafe) func updateView(_ view: PlatformView, context: Context) | ||
@MainActor(unsafe) func sizeThatFits(_ proposal: ProposedSize, view: PlatformView) -> CGSize? | ||
@MainActor(unsafe) static func dismantleView(_ view: PlatformView, coordinator: Coordinator) | ||
|
||
associatedtype Coordinator = Void | ||
@MainActor(unsafe) func makeCoordinator() -> Coordinator | ||
|
||
typealias Context = _PlatformViewRepresentableBody<Self>.Context | ||
} | ||
|
||
extension PlatformViewRepresentable { | ||
public var body: Never { | ||
bodyError() | ||
} | ||
|
||
private var content: _PlatformViewRepresentableBody<Self> { | ||
_PlatformViewRepresentableBody(representable: self) | ||
} | ||
|
||
public static func _makeView( | ||
view: _GraphValue<Self>, | ||
inputs: _ViewInputs | ||
) -> _ViewOutputs { | ||
_PlatformViewRepresentableBody<Self>._makeView(view: view[\.content], inputs: inputs) | ||
} | ||
|
||
public static func _makeViewList( | ||
view: _GraphValue<Self>, | ||
inputs: _ViewListInputs | ||
) -> _ViewListOutputs { | ||
_PlatformViewRepresentableBody<Self>._makeViewList(view: view[\.content], inputs: inputs) | ||
} | ||
|
||
@available(iOS 14.0, macOS 11.0, tvOS 14.0, *) | ||
public static func _viewListCount( | ||
inputs: _ViewListCountInputs | ||
) -> Int? { | ||
_PlatformViewRepresentableBody<Self>._viewListCount(inputs: inputs) | ||
} | ||
} | ||
|
||
#if os(macOS) | ||
public struct _PlatformViewRepresentableBody< | ||
Representable: PlatformViewRepresentable | ||
>: NSViewRepresentable { | ||
|
||
var representable: Representable | ||
|
||
public func makeNSView( | ||
context: Context | ||
) -> Representable.PlatformView { | ||
representable.makeView(context: context) | ||
} | ||
|
||
public func updateNSView( | ||
_ nsView: Representable.PlatformView, | ||
context: Context | ||
) { | ||
representable.updateView(nsView, context: context) | ||
} | ||
|
||
@available(macOS 13.0, iOS 16.0, tvOS 16.0, *) | ||
public func sizeThatFits( | ||
_ proposal: ProposedViewSize, | ||
nsView: Representable.PlatformView, | ||
context: Context | ||
) -> CGSize? { | ||
representable.sizeThatFits(ProposedSize(proposal), view: nsView) | ||
} | ||
|
||
public func _overrideSizeThatFits( | ||
_ size: inout CGSize, | ||
in proposedSize: _ProposedSize, | ||
nsView: Representable.PlatformView | ||
) { | ||
if #available(macOS 13.0, iOS 16.0, tvOS 16.0, *) { | ||
// Already handled | ||
} else if let sizeThatFits = representable.sizeThatFits(ProposedSize(proposedSize), view: nsView) { | ||
size = sizeThatFits | ||
} | ||
} | ||
|
||
public static func dismantleNSView( | ||
_ nsView: Representable.PlatformView, | ||
coordinator: Coordinator | ||
) { | ||
Representable.dismantleView(nsView, coordinator: coordinator) | ||
} | ||
|
||
public func makeCoordinator() -> Representable.Coordinator { | ||
representable.makeCoordinator() | ||
} | ||
} | ||
#else | ||
public struct _PlatformViewRepresentableBody< | ||
Representable: PlatformViewRepresentable | ||
>: UIViewRepresentable { | ||
|
||
var representable: Representable | ||
|
||
public func makeUIView( | ||
context: Context | ||
) -> Representable.PlatformView { | ||
representable.makeView(context: context) | ||
} | ||
|
||
public func updateUIView( | ||
_ uiView: Representable.PlatformView, | ||
context: Context | ||
) { | ||
representable.updateView(uiView, context: context) | ||
} | ||
|
||
@available(iOS 16.0, tvOS 16.0, watchOS 9.0, *) | ||
public func sizeThatFits( | ||
_ proposal: ProposedViewSize, | ||
uiView: Representable.PlatformView, | ||
context: Context | ||
) -> CGSize? { | ||
representable.sizeThatFits(ProposedSize(proposal), view: uiView) | ||
} | ||
|
||
public func _overrideSizeThatFits( | ||
_ size: inout CGSize, | ||
in proposedSize: _ProposedSize, | ||
uiView: Representable.PlatformView | ||
) { | ||
if #available(iOS 16.0, tvOS 16.0, watchOS 9.0, *) { | ||
// Already handled | ||
} else if let sizeThatFits = representable.sizeThatFits(ProposedSize(proposedSize), view: uiView) { | ||
size = sizeThatFits | ||
} | ||
} | ||
|
||
public static func dismantleUIView( | ||
_ uiView: Representable.PlatformView, | ||
coordinator: Coordinator | ||
) { | ||
Representable.dismantleView(uiView, coordinator: coordinator) | ||
} | ||
|
||
public func makeCoordinator() -> Representable.Coordinator { | ||
representable.makeCoordinator() | ||
} | ||
} | ||
#endif | ||
|
||
#endif // !os(watchOS) |
This file contains 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
This file contains 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
53 changes: 53 additions & 0 deletions
53
Sources/Turbocharger/Sources/ViewModifier/OnAppearAndChange.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// | ||
// Copyright (c) Nathan Tannar | ||
// | ||
|
||
import SwiftUI | ||
import Engine | ||
|
||
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) | ||
@frozen | ||
public struct OnAppearAndChangeModifier< | ||
Value: Equatable | ||
>: VersionedViewModifier { | ||
|
||
@usableFromInline | ||
var value: Value | ||
|
||
@usableFromInline | ||
var action: (Value) -> Void | ||
|
||
@inlinable | ||
public init(value: Value, action: @escaping (Value) -> Void) { | ||
self.value = value | ||
self.action = action | ||
} | ||
|
||
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, visionOS 1.0, *) | ||
public func v5Body(content: Content) -> some View { | ||
content | ||
.onChange(of: value, initial: true) { _, newValue in | ||
action(newValue) | ||
} | ||
} | ||
|
||
#if !os(visionOS) | ||
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) | ||
public func v2Body(content: Content) -> some View { | ||
content | ||
.onAppear { action(value) } | ||
.onChange(of: value, perform: action) | ||
} | ||
#endif | ||
} | ||
|
||
extension View { | ||
|
||
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) | ||
public func onAppearAndChange<V: Equatable>( | ||
of value: V, | ||
perform action: @escaping (V) -> Void | ||
) -> some View { | ||
modifier(OnAppearAndChangeModifier(value: value, action: action)) | ||
} | ||
} |
Oops, something went wrong.