lottie-ios 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +117 -11
- package/Gemfile +1 -0
- package/Gemfile.lock +5 -0
- package/Lottie.xcodeproj/project.pbxproj +1542 -194
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (iOS).xcscheme +1 -1
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (macOS).xcscheme +1 -1
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (tvOS).xcscheme +4 -5
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (visionOS).xcscheme +66 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +18 -0
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -11
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +346 -102
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +16 -5
- package/Package.resolved +2 -2
- package/Package.swift +16 -9
- package/README.md +2 -2
- package/Rakefile +122 -25
- package/Sources/Private/CoreAnimation/Animations/DropShadowAnimation.swift +160 -0
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +83 -10
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +85 -79
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +7 -7
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +9 -0
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -0
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +2 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +11 -13
- package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +20 -5
- package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +13 -3
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +6 -4
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +263 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +18 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +32 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +99 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +29 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +57 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +60 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +37 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/README.md +31 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +46 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +391 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +44 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +51 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +172 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +128 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +452 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +148 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +40 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +43 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +42 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +51 -0
- package/Sources/Private/EmbeddedLibraries/README.md +27 -0
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+BackingConfiguration.swift +4 -4
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+MemoryFile.swift +2 -2
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Writing.swift +4 -4
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive.swift +8 -7
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Compression.swift +5 -5
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/FileManager+ZIP.swift +4 -4
- package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +6 -0
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +3 -1
- package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +18 -5
- package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +31 -3
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +33 -8
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +8 -1
- package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +13 -4
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +2 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -0
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +3 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +2 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +2 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +6 -2
- package/Sources/Private/Model/Assets/Asset.swift +8 -0
- package/Sources/Private/Model/Assets/AssetLibrary.swift +2 -2
- package/Sources/Private/Model/Assets/ImageAsset.swift +3 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +16 -2
- package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +20 -7
- package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +26 -0
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +8 -5
- package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +45 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +38 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +38 -0
- package/Sources/Private/Model/LayerEffects/LayerEffect.swift +103 -0
- package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +72 -0
- package/Sources/Private/Model/LayerStyles/LayerStyle.swift +85 -0
- package/Sources/Private/Model/Layers/LayerModel.swift +27 -0
- package/Sources/Private/Model/Objects/Marker.swift +1 -1
- package/Sources/Private/Model/Objects/Transform.swift +1 -2
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -1
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +2 -2
- package/Sources/Private/Model/ShapeItems/Merge.swift +1 -1
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +31 -26
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -9
- package/Sources/Private/Model/ShapeItems/Star.swift +1 -1
- package/Sources/Private/Model/Text/Font.swift +2 -2
- package/Sources/Private/Model/Text/Glyph.swift +1 -1
- package/Sources/Private/RootAnimationLayer.swift +2 -2
- package/Sources/Private/Utility/Debugging/LayerDebugging.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +32 -2
- package/Sources/Private/Utility/Helpers/AnimationContext.swift +4 -2
- package/Sources/Private/Utility/Helpers/AnyEquatable.swift +24 -0
- package/Sources/Private/Utility/Helpers/Binding+Map.swift +18 -0
- package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +20 -0
- package/Sources/Private/Utility/LottieAnimationSource.swift +41 -0
- package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -2
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +1 -1
- package/Sources/Public/Animation/LottieAnimation.swift +21 -2
- package/Sources/Public/Animation/LottieAnimationLayer.swift +1464 -0
- package/Sources/Public/Animation/LottieAnimationView.swift +258 -735
- package/Sources/Public/Animation/LottiePlaybackMode.swift +117 -0
- package/Sources/Public/Animation/LottieView.swift +462 -0
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +2 -1
- package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +5 -6
- package/Sources/Public/Configuration/DecodingStrategy.swift +15 -0
- package/Sources/Public/Configuration/LottieConfiguration.swift +47 -0
- package/Sources/Public/Configuration/ReducedMotionOption.swift +114 -0
- package/Sources/Public/{LottieConfiguration.swift → Configuration/RenderingEngineOption.swift} +2 -57
- package/Sources/Public/{iOS → Controls}/AnimatedButton.swift +56 -13
- package/Sources/Public/{iOS → Controls}/AnimatedControl.swift +80 -8
- package/Sources/Public/{iOS → Controls}/AnimatedSwitch.swift +71 -31
- package/Sources/Public/Controls/LottieButton.swift +122 -0
- package/Sources/Public/Controls/LottieSwitch.swift +144 -0
- package/Sources/Public/Controls/LottieViewType.swift +79 -0
- package/Sources/Public/DotLottie/DotLottieConfiguration.swift +24 -0
- package/Sources/Public/DotLottie/DotLottieFile.swift +21 -6
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +79 -0
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +11 -1
- package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +14 -0
- package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +13 -0
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +22 -5
- package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +14 -0
- package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +13 -0
- package/Sources/Public/FontProvider/AnimationFontProvider.swift +8 -0
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +24 -0
- package/Sources/Public/Keyframes/Keyframe.swift +6 -0
- package/Sources/Public/Primitives/Vectors.swift +2 -2
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +79 -6
- package/Sources/Public/iOS/AnimationSubview.swift +1 -1
- package/Sources/Public/iOS/BundleImageProvider.swift +16 -2
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +2 -2
- package/Sources/Public/iOS/FilepathImageProvider.swift +22 -3
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +7 -1
- package/Sources/Public/macOS/BundleImageProvider.macOS.swift +15 -1
- package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +21 -2
- package/lottie-ios.podspec +2 -1
- package/package.json +1 -1
- package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +0 -15
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Helpers.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Progress.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Reading.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ReadingDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+WritingDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ZIP64.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+CompressionDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Serialization.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+Serialization.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+ZIP64.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/README.md +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/URL+ZIP.swift +0 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
// Created by eric_horacek on 9/16/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Combine
|
|
5
|
+
import SwiftUI
|
|
6
|
+
|
|
7
|
+
#if !os(macOS)
|
|
8
|
+
|
|
9
|
+
// MARK: - SwiftUIHostingViewReuseBehavior
|
|
10
|
+
|
|
11
|
+
/// The reuse behavior of an `EpoxySwiftUIHostingView`.
|
|
12
|
+
enum SwiftUIHostingViewReuseBehavior: Hashable {
|
|
13
|
+
/// Instances of a `EpoxySwiftUIHostingView` with `RootView`s of same type can be reused within
|
|
14
|
+
/// the Epoxy container.
|
|
15
|
+
///
|
|
16
|
+
/// This is the default reuse behavior.
|
|
17
|
+
case reusable
|
|
18
|
+
/// Instances of a `EpoxySwiftUIHostingView` with `RootView`s of same type can only reused within
|
|
19
|
+
/// the Epoxy container when they have identical `reuseID`s.
|
|
20
|
+
case unique(reuseID: AnyHashable)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// MARK: - CallbackContextEpoxyModeled
|
|
24
|
+
|
|
25
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
26
|
+
extension CallbackContextEpoxyModeled
|
|
27
|
+
where
|
|
28
|
+
Self: WillDisplayProviding & DidEndDisplayingProviding,
|
|
29
|
+
CallbackContext: ViewProviding & AnimatedProviding
|
|
30
|
+
{
|
|
31
|
+
/// Updates the appearance state of a `EpoxySwiftUIHostingView` in coordination with the
|
|
32
|
+
/// `willDisplay` and `didEndDisplaying` callbacks of this `EpoxyableModel`.
|
|
33
|
+
///
|
|
34
|
+
/// - Note: You should only need to call then from the implementation of a concrete
|
|
35
|
+
/// `EpoxyableModel` convenience vendor method, e.g. `SwiftUI.View.itemModel(…)`.
|
|
36
|
+
func linkDisplayLifecycle<RootView: View>() -> Self
|
|
37
|
+
where
|
|
38
|
+
CallbackContext.View == EpoxySwiftUIHostingView<RootView>
|
|
39
|
+
{
|
|
40
|
+
willDisplay { context in
|
|
41
|
+
context.view.handleWillDisplay(animated: context.animated)
|
|
42
|
+
}
|
|
43
|
+
.didEndDisplaying { context in
|
|
44
|
+
context.view.handleDidEndDisplaying(animated: context.animated)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// MARK: - EpoxySwiftUIHostingView
|
|
50
|
+
|
|
51
|
+
/// A `UIView` that hosts a SwiftUI view within an Epoxy container, e.g. an Epoxy `CollectionView`.
|
|
52
|
+
///
|
|
53
|
+
/// Wraps an `EpoxySwiftUIHostingController` and adds it as a child view controller to the next
|
|
54
|
+
/// ancestor view controller in the hierarchy.
|
|
55
|
+
///
|
|
56
|
+
/// There's a private API that accomplishes this same behavior without needing a `UIViewController`:
|
|
57
|
+
/// `_UIHostingView`, but we can't safely use it as 1) the behavior may change out from under us, 2)
|
|
58
|
+
/// the API is private and 3) the `_UIHostingView` doesn't not accept setting a new `View` instance.
|
|
59
|
+
///
|
|
60
|
+
/// - SeeAlso: `EpoxySwiftUIHostingController`
|
|
61
|
+
@available(iOS 13.0, tvOS 13.0, *)
|
|
62
|
+
final class EpoxySwiftUIHostingView<RootView: View>: UIView, EpoxyableView {
|
|
63
|
+
|
|
64
|
+
// MARK: Lifecycle
|
|
65
|
+
|
|
66
|
+
init(style: Style) {
|
|
67
|
+
// Ignore the safe area to ensure the view isn't laid out incorrectly when being sized while
|
|
68
|
+
// overlapping the safe area.
|
|
69
|
+
epoxyContent = EpoxyHostingContent(rootView: style.initialContent.rootView)
|
|
70
|
+
viewController = EpoxySwiftUIHostingController(
|
|
71
|
+
rootView: .init(content: epoxyContent, environment: epoxyEnvironment),
|
|
72
|
+
ignoreSafeArea: true)
|
|
73
|
+
|
|
74
|
+
dataID = style.initialContent.dataID ?? DefaultDataID.noneProvided as AnyHashable
|
|
75
|
+
|
|
76
|
+
super.init(frame: .zero)
|
|
77
|
+
|
|
78
|
+
epoxyEnvironment.intrinsicContentSizeInvalidator = .init(invalidate: { [weak self] in
|
|
79
|
+
self?.viewController.view.invalidateIntrinsicContentSize()
|
|
80
|
+
|
|
81
|
+
// Inform the enclosing collection view that the size has changed, if we're contained in one,
|
|
82
|
+
// allowing the cell to resize.
|
|
83
|
+
//
|
|
84
|
+
// On iOS 16+, we could call `invalidateIntrinsicContentSize()` on the enclosing collection
|
|
85
|
+
// view cell instead, but that currently causes visual artifacts with `MagazineLayout`. The
|
|
86
|
+
// better long term fix is likely to switch to `UIHostingConfiguration` on iOS 16+ anyways.
|
|
87
|
+
if let enclosingCollectionView = self?.superview?.superview?.superview as? UICollectionView {
|
|
88
|
+
enclosingCollectionView.collectionViewLayout.invalidateLayout()
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
layoutMargins = .zero
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@available(*, unavailable)
|
|
95
|
+
required init?(coder _: NSCoder) {
|
|
96
|
+
fatalError("init(coder:) has not been implemented")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// MARK: Internal
|
|
100
|
+
|
|
101
|
+
struct Style: Hashable {
|
|
102
|
+
init(reuseBehavior: SwiftUIHostingViewReuseBehavior, initialContent: Content) {
|
|
103
|
+
self.reuseBehavior = reuseBehavior
|
|
104
|
+
self.initialContent = initialContent
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
var reuseBehavior: SwiftUIHostingViewReuseBehavior
|
|
108
|
+
var initialContent: Content
|
|
109
|
+
|
|
110
|
+
static func == (lhs: Style, rhs: Style) -> Bool {
|
|
111
|
+
lhs.reuseBehavior == rhs.reuseBehavior
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func hash(into hasher: inout Hasher) {
|
|
115
|
+
hasher.combine(reuseBehavior)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
struct Content: Equatable {
|
|
120
|
+
init(rootView: RootView, dataID: AnyHashable?) {
|
|
121
|
+
self.rootView = rootView
|
|
122
|
+
self.dataID = dataID
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
var rootView: RootView
|
|
126
|
+
var dataID: AnyHashable?
|
|
127
|
+
|
|
128
|
+
static func == (_: Content, _: Content) -> Bool {
|
|
129
|
+
// The content should never be equal since we need the `rootView` to be updated on every
|
|
130
|
+
// content change.
|
|
131
|
+
false
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
override func didMoveToWindow() {
|
|
136
|
+
super.didMoveToWindow()
|
|
137
|
+
|
|
138
|
+
// We'll only be able to discover a valid parent `viewController` once we're added to a window,
|
|
139
|
+
// so we do so here in addition to the `handleWillDisplay(…)` method.
|
|
140
|
+
if window != nil {
|
|
141
|
+
addViewControllerIfNeeded()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func setContent(_ content: Content, animated _: Bool) {
|
|
146
|
+
// This triggers a change in the observed `EpoxyHostingContent` object and allows the
|
|
147
|
+
// propagation of the SwiftUI transaction, instead of just replacing the `rootView`.
|
|
148
|
+
epoxyContent.rootView = content.rootView
|
|
149
|
+
dataID = content.dataID ?? DefaultDataID.noneProvided as AnyHashable
|
|
150
|
+
|
|
151
|
+
// The view controller must be added to the view controller hierarchy to measure its content.
|
|
152
|
+
if window != nil {
|
|
153
|
+
addViewControllerIfNeeded()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// As of iOS 15.2, `UIHostingController` now renders updated content asynchronously, and as such
|
|
157
|
+
// this view will get sized incorrectly with the previous content when reused unless we invoke
|
|
158
|
+
// this semi-private API. We couldn't find any other method to get the view to resize
|
|
159
|
+
// synchronously after updating `rootView`, but hopefully this will become a internal API soon so
|
|
160
|
+
// we can remove this call.
|
|
161
|
+
viewController._render(seconds: 0)
|
|
162
|
+
|
|
163
|
+
// This is required to ensure that views with new content are properly resized.
|
|
164
|
+
viewController.view.invalidateIntrinsicContentSize()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
override func layoutMarginsDidChange() {
|
|
168
|
+
super.layoutMarginsDidChange()
|
|
169
|
+
|
|
170
|
+
let margins = layoutMargins
|
|
171
|
+
switch effectiveUserInterfaceLayoutDirection {
|
|
172
|
+
case .rightToLeft:
|
|
173
|
+
epoxyEnvironment.layoutMargins = .init(
|
|
174
|
+
top: margins.top,
|
|
175
|
+
leading: margins.right,
|
|
176
|
+
bottom: margins.bottom,
|
|
177
|
+
trailing: margins.left)
|
|
178
|
+
case .leftToRight:
|
|
179
|
+
fallthrough
|
|
180
|
+
@unknown default:
|
|
181
|
+
epoxyEnvironment.layoutMargins = .init(
|
|
182
|
+
top: margins.top,
|
|
183
|
+
leading: margins.left,
|
|
184
|
+
bottom: margins.bottom,
|
|
185
|
+
trailing: margins.right)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Allow the layout margins update to fully propagate through to the SwiftUI View before
|
|
189
|
+
// invalidating the layout.
|
|
190
|
+
DispatchQueue.main.async {
|
|
191
|
+
self.viewController.view.invalidateIntrinsicContentSize()
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
func handleWillDisplay(animated: Bool) {
|
|
196
|
+
guard state != .appeared, window != nil else { return }
|
|
197
|
+
transition(to: .appearing(animated: animated))
|
|
198
|
+
transition(to: .appeared)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
func handleDidEndDisplaying(animated: Bool) {
|
|
202
|
+
guard state != .disappeared else { return }
|
|
203
|
+
transition(to: .disappearing(animated: animated))
|
|
204
|
+
transition(to: .disappeared)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// MARK: Private
|
|
208
|
+
|
|
209
|
+
private let viewController: EpoxySwiftUIHostingController<EpoxyHostingWrapper<RootView>>
|
|
210
|
+
private let epoxyContent: EpoxyHostingContent<RootView>
|
|
211
|
+
private let epoxyEnvironment = EpoxyHostingEnvironment()
|
|
212
|
+
private var dataID: AnyHashable
|
|
213
|
+
private var state: AppearanceState = .disappeared
|
|
214
|
+
|
|
215
|
+
/// Updates the appearance state of the `viewController`.
|
|
216
|
+
private func transition(to state: AppearanceState) {
|
|
217
|
+
guard state != self.state else { return }
|
|
218
|
+
|
|
219
|
+
// See "Handling View-Related Notifications" section for the state machine diagram.
|
|
220
|
+
// https://developer.apple.com/documentation/uikit/uiviewcontroller
|
|
221
|
+
switch (to: state, from: self.state) {
|
|
222
|
+
case (to: .appearing(let animated), from: .disappeared):
|
|
223
|
+
viewController.beginAppearanceTransition(true, animated: animated)
|
|
224
|
+
addViewControllerIfNeeded()
|
|
225
|
+
case (to: .disappearing(let animated), from: .appeared):
|
|
226
|
+
viewController.beginAppearanceTransition(false, animated: animated)
|
|
227
|
+
case (to: .disappeared, from: .disappearing):
|
|
228
|
+
removeViewControllerIfNeeded()
|
|
229
|
+
case (to: .appeared, from: .appearing):
|
|
230
|
+
viewController.endAppearanceTransition()
|
|
231
|
+
case (to: .disappeared, from: .appeared):
|
|
232
|
+
viewController.beginAppearanceTransition(false, animated: true)
|
|
233
|
+
removeViewControllerIfNeeded()
|
|
234
|
+
case (to: .appeared, from: .disappearing(let animated)):
|
|
235
|
+
viewController.beginAppearanceTransition(true, animated: animated)
|
|
236
|
+
viewController.endAppearanceTransition()
|
|
237
|
+
case (to: .disappeared, from: .appearing(let animated)):
|
|
238
|
+
viewController.beginAppearanceTransition(false, animated: animated)
|
|
239
|
+
removeViewControllerIfNeeded()
|
|
240
|
+
case (to: .appeared, from: .disappeared):
|
|
241
|
+
viewController.beginAppearanceTransition(true, animated: false)
|
|
242
|
+
addViewControllerIfNeeded()
|
|
243
|
+
viewController.endAppearanceTransition()
|
|
244
|
+
case (to: .appearing(let animated), from: .appeared):
|
|
245
|
+
viewController.beginAppearanceTransition(false, animated: animated)
|
|
246
|
+
viewController.beginAppearanceTransition(true, animated: animated)
|
|
247
|
+
case (to: .appearing(let animated), from: .disappearing):
|
|
248
|
+
viewController.beginAppearanceTransition(true, animated: animated)
|
|
249
|
+
case (to: .disappearing(let animated), from: .disappeared):
|
|
250
|
+
viewController.beginAppearanceTransition(true, animated: animated)
|
|
251
|
+
addViewControllerIfNeeded()
|
|
252
|
+
viewController.beginAppearanceTransition(false, animated: animated)
|
|
253
|
+
case (to: .disappearing(let animated), from: .appearing):
|
|
254
|
+
viewController.beginAppearanceTransition(false, animated: animated)
|
|
255
|
+
case (to: .appearing, from: .appearing),
|
|
256
|
+
(to: .appeared, from: .appeared),
|
|
257
|
+
(to: .disappearing, from: .disappearing),
|
|
258
|
+
(to: .disappeared, from: .disappeared):
|
|
259
|
+
// This should never happen since we guard on identical states.
|
|
260
|
+
EpoxyLogger.shared.assertionFailure("Impossible state change from \(self.state) to \(state)")
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
self.state = state
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private func addViewControllerIfNeeded() {
|
|
267
|
+
// This isn't great, and means that we're going to add this view controller as a child view
|
|
268
|
+
// controller of a view controller somewhere else in the hierarchy, which the author of that
|
|
269
|
+
// view controller may not be expecting. However there's not really a better pathway forward
|
|
270
|
+
// here without requiring a view controller instance to be passed all the way through, which is
|
|
271
|
+
// both burdensome and error-prone.
|
|
272
|
+
guard let nextViewController = superview?.next(UIViewController.self) else {
|
|
273
|
+
EpoxyLogger.shared.assertionFailure(
|
|
274
|
+
"""
|
|
275
|
+
Unable to add a UIHostingController view, could not locate a UIViewController in the \
|
|
276
|
+
responder chain for view with ID \(dataID) of type \(RootView.self).
|
|
277
|
+
""")
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
guard viewController.parent !== nextViewController else { return }
|
|
282
|
+
|
|
283
|
+
// If in a different parent, we need to first remove from it before we add.
|
|
284
|
+
if viewController.parent != nil {
|
|
285
|
+
removeViewControllerIfNeeded()
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
addViewController(to: nextViewController)
|
|
289
|
+
|
|
290
|
+
state = .appeared
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private func addViewController(to parent: UIViewController) {
|
|
294
|
+
viewController.willMove(toParent: parent)
|
|
295
|
+
|
|
296
|
+
parent.addChild(viewController)
|
|
297
|
+
|
|
298
|
+
addSubview(viewController.view)
|
|
299
|
+
|
|
300
|
+
// Get the view controller's view to be sized correctly so that we don't have to wait for
|
|
301
|
+
// autolayout to perform a pass to do so.
|
|
302
|
+
viewController.view.frame = bounds
|
|
303
|
+
|
|
304
|
+
viewController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
305
|
+
NSLayoutConstraint.activate([
|
|
306
|
+
viewController.view.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
307
|
+
viewController.view.topAnchor.constraint(equalTo: topAnchor),
|
|
308
|
+
viewController.view.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
309
|
+
viewController.view.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
310
|
+
])
|
|
311
|
+
|
|
312
|
+
viewController.didMove(toParent: parent)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private func removeViewControllerIfNeeded() {
|
|
316
|
+
guard viewController.parent != nil else { return }
|
|
317
|
+
|
|
318
|
+
viewController.willMove(toParent: nil)
|
|
319
|
+
viewController.view.removeFromSuperview()
|
|
320
|
+
viewController.removeFromParent()
|
|
321
|
+
viewController.didMove(toParent: nil)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// MARK: - AppearanceState
|
|
326
|
+
|
|
327
|
+
/// The appearance state of a `EpoxySwiftUIHostingController` contained within a
|
|
328
|
+
/// `EpoxySwiftUIHostingView`.
|
|
329
|
+
private enum AppearanceState: Equatable {
|
|
330
|
+
case appearing(animated: Bool)
|
|
331
|
+
case appeared
|
|
332
|
+
case disappearing(animated: Bool)
|
|
333
|
+
case disappeared
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// MARK: - UIResponder
|
|
337
|
+
|
|
338
|
+
extension UIResponder {
|
|
339
|
+
/// Recursively traverses the responder chain upwards from this responder to its next responder
|
|
340
|
+
/// until the a responder of the given type is located, else returns `nil`.
|
|
341
|
+
@nonobjc
|
|
342
|
+
fileprivate func next<ResponderType>(_ type: ResponderType.Type) -> ResponderType? {
|
|
343
|
+
self as? ResponderType ?? next?.next(type)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// MARK: - EpoxyHostingContent
|
|
348
|
+
|
|
349
|
+
/// The object that is used to communicate changes in the root view to the
|
|
350
|
+
/// `EpoxySwiftUIHostingController`.
|
|
351
|
+
@available(iOS 13.0, tvOS 13.0, *)
|
|
352
|
+
final class EpoxyHostingContent<RootView: View>: ObservableObject {
|
|
353
|
+
|
|
354
|
+
// MARK: Lifecycle
|
|
355
|
+
|
|
356
|
+
init(rootView: RootView) {
|
|
357
|
+
_rootView = .init(wrappedValue: rootView)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// MARK: Internal
|
|
361
|
+
|
|
362
|
+
@Published var rootView: RootView
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// MARK: - EpoxyHostingEnvironment
|
|
366
|
+
|
|
367
|
+
/// The object that is used to communicate values to SwiftUI views within an
|
|
368
|
+
/// `EpoxySwiftUIHostingController`, e.g. layout margins.
|
|
369
|
+
@available(iOS 13.0, tvOS 13.0, *)
|
|
370
|
+
final class EpoxyHostingEnvironment: ObservableObject {
|
|
371
|
+
@Published var layoutMargins = EdgeInsets()
|
|
372
|
+
@Published var intrinsicContentSizeInvalidator = EpoxyIntrinsicContentSizeInvalidator(invalidate: { })
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// MARK: - EpoxyHostingWrapper
|
|
376
|
+
|
|
377
|
+
/// The wrapper view that is used to communicate values to SwiftUI views within an
|
|
378
|
+
/// `EpoxySwiftUIHostingController`, e.g. layout margins.
|
|
379
|
+
@available(iOS 13.0, tvOS 13.0, *)
|
|
380
|
+
struct EpoxyHostingWrapper<Content: View>: View {
|
|
381
|
+
@ObservedObject var content: EpoxyHostingContent<Content>
|
|
382
|
+
@ObservedObject var environment: EpoxyHostingEnvironment
|
|
383
|
+
|
|
384
|
+
var body: some View {
|
|
385
|
+
content.rootView
|
|
386
|
+
.environment(\.epoxyLayoutMargins, environment.layoutMargins)
|
|
387
|
+
.environment(\.epoxyIntrinsicContentSizeInvalidator, environment.intrinsicContentSizeInvalidator)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
#endif
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Created by matthew_cheok on 11/19/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import SwiftUI
|
|
5
|
+
|
|
6
|
+
// MARK: - EpoxyIntrinsicContentSizeInvalidator
|
|
7
|
+
|
|
8
|
+
/// Allows the SwiftUI view contained in an Epoxy model to request the invalidation of
|
|
9
|
+
/// the container's intrinsic content size.
|
|
10
|
+
///
|
|
11
|
+
/// ```
|
|
12
|
+
/// @Environment(\.epoxyIntrinsicContentSizeInvalidator) var invalidateIntrinsicContentSize
|
|
13
|
+
///
|
|
14
|
+
/// var body: some View {
|
|
15
|
+
/// ...
|
|
16
|
+
/// .onChange(of: size) {
|
|
17
|
+
/// invalidateIntrinsicContentSize()
|
|
18
|
+
/// }
|
|
19
|
+
/// }
|
|
20
|
+
/// ```
|
|
21
|
+
struct EpoxyIntrinsicContentSizeInvalidator {
|
|
22
|
+
let invalidate: () -> Void
|
|
23
|
+
|
|
24
|
+
func callAsFunction() {
|
|
25
|
+
invalidate()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// MARK: - EnvironmentValues
|
|
30
|
+
|
|
31
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
32
|
+
extension EnvironmentValues {
|
|
33
|
+
/// A means of invalidating the intrinsic content size of the parent `EpoxySwiftUIHostingView`.
|
|
34
|
+
var epoxyIntrinsicContentSizeInvalidator: EpoxyIntrinsicContentSizeInvalidator {
|
|
35
|
+
get { self[EpoxyIntrinsicContentSizeInvalidatorKey.self] }
|
|
36
|
+
set { self[EpoxyIntrinsicContentSizeInvalidatorKey.self] = newValue }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// MARK: - EpoxyIntrinsicContentSizeInvalidatorKey
|
|
41
|
+
|
|
42
|
+
private struct EpoxyIntrinsicContentSizeInvalidatorKey: EnvironmentKey {
|
|
43
|
+
static let defaultValue = EpoxyIntrinsicContentSizeInvalidator(invalidate: { })
|
|
44
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Created by eric_horacek on 10/8/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import SwiftUI
|
|
5
|
+
|
|
6
|
+
// MARK: - View
|
|
7
|
+
|
|
8
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
9
|
+
extension View {
|
|
10
|
+
/// Applies the layout margins from the parent `EpoxySwiftUIHostingView` to this `View`, if there
|
|
11
|
+
/// are any.
|
|
12
|
+
///
|
|
13
|
+
/// Can be used to have a background in SwiftUI underlap the safe area within a bar installer, for
|
|
14
|
+
/// example.
|
|
15
|
+
///
|
|
16
|
+
/// These margins are propagated via the `EnvironmentValues.epoxyLayoutMargins`.
|
|
17
|
+
func epoxyLayoutMargins() -> some View {
|
|
18
|
+
modifier(EpoxyLayoutMarginsPadding())
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// MARK: - EnvironmentValues
|
|
23
|
+
|
|
24
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
25
|
+
extension EnvironmentValues {
|
|
26
|
+
/// The layout margins of the parent `EpoxySwiftUIHostingView`, else zero if there is none.
|
|
27
|
+
var epoxyLayoutMargins: EdgeInsets {
|
|
28
|
+
get { self[EpoxyLayoutMarginsKey.self] }
|
|
29
|
+
set { self[EpoxyLayoutMarginsKey.self] = newValue }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: - EpoxyLayoutMarginsKey
|
|
34
|
+
|
|
35
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
36
|
+
private struct EpoxyLayoutMarginsKey: EnvironmentKey {
|
|
37
|
+
static let defaultValue = EdgeInsets()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// MARK: - EpoxyLayoutMarginsPadding
|
|
41
|
+
|
|
42
|
+
/// A view modifier that applies the layout margins from an enclosing `EpoxySwiftUIHostingView` to
|
|
43
|
+
/// the modified `View`.
|
|
44
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
45
|
+
private struct EpoxyLayoutMarginsPadding: ViewModifier {
|
|
46
|
+
@Environment(\.epoxyLayoutMargins) var epoxyLayoutMargins
|
|
47
|
+
|
|
48
|
+
func body(content: Content) -> some View {
|
|
49
|
+
content.padding(epoxyLayoutMargins)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Created by eric_horacek on 9/13/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import SwiftUI
|
|
5
|
+
|
|
6
|
+
// MARK: - StyledView
|
|
7
|
+
|
|
8
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
9
|
+
extension StyledView where Self: ContentConfigurableView & BehaviorsConfigurableView {
|
|
10
|
+
/// Returns a SwiftUI `View` representing this `EpoxyableView`.
|
|
11
|
+
///
|
|
12
|
+
/// To perform additional configuration of the `EpoxyableView` instance, call `configure` on the
|
|
13
|
+
/// returned SwiftUI `View`:
|
|
14
|
+
/// ```
|
|
15
|
+
/// MyView.swiftUIView(…)
|
|
16
|
+
/// .configure { context in
|
|
17
|
+
/// context.view.doSomething()
|
|
18
|
+
/// }
|
|
19
|
+
/// ```
|
|
20
|
+
///
|
|
21
|
+
/// To configure the sizing behavior of the `EpoxyableView` instance, call `sizing` on the
|
|
22
|
+
/// returned SwiftUI `View`:
|
|
23
|
+
/// ```
|
|
24
|
+
/// MyView.swiftUIView(…).sizing(.intrinsicSize)
|
|
25
|
+
/// ```
|
|
26
|
+
static func swiftUIView(
|
|
27
|
+
content: Content,
|
|
28
|
+
style: Style,
|
|
29
|
+
behaviors: Behaviors? = nil)
|
|
30
|
+
-> SwiftUIView<Self, (content: Content, style: Style)>
|
|
31
|
+
{
|
|
32
|
+
SwiftUIView(storage: (content: content, style: style)) {
|
|
33
|
+
let view = Self(style: style)
|
|
34
|
+
view.setContent(content, animated: false)
|
|
35
|
+
return view
|
|
36
|
+
}
|
|
37
|
+
.configure { context in
|
|
38
|
+
// We need to create a new view instance when the style changes.
|
|
39
|
+
if context.oldStorage.style != style {
|
|
40
|
+
context.view = Self(style: style)
|
|
41
|
+
context.view.setContent(content, animated: context.animated)
|
|
42
|
+
}
|
|
43
|
+
// Otherwise, if the just the content changes, we need to update it.
|
|
44
|
+
else if context.oldStorage.content != content {
|
|
45
|
+
context.view.setContent(content, animated: context.animated)
|
|
46
|
+
context.container.invalidateIntrinsicContentSize()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
context.view.setBehaviors(behaviors)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
55
|
+
extension StyledView
|
|
56
|
+
where
|
|
57
|
+
Self: ContentConfigurableView & BehaviorsConfigurableView,
|
|
58
|
+
Style == Never
|
|
59
|
+
{
|
|
60
|
+
/// Returns a SwiftUI `View` representing this `EpoxyableView`.
|
|
61
|
+
///
|
|
62
|
+
/// To perform additional configuration of the `EpoxyableView` instance, call `configure` on the
|
|
63
|
+
/// returned SwiftUI `View`:
|
|
64
|
+
/// ```
|
|
65
|
+
/// MyView.swiftUIView(…)
|
|
66
|
+
/// .configure { context in
|
|
67
|
+
/// context.view.doSomething()
|
|
68
|
+
/// }
|
|
69
|
+
/// ```
|
|
70
|
+
///
|
|
71
|
+
/// To configure the sizing behavior of the `EpoxyableView` instance, call `sizing` on the
|
|
72
|
+
/// returned SwiftUI `View`:
|
|
73
|
+
/// ```
|
|
74
|
+
/// MyView.swiftUIView(…).sizing(.intrinsicSize)
|
|
75
|
+
/// ```
|
|
76
|
+
static func swiftUIView(
|
|
77
|
+
content: Content,
|
|
78
|
+
behaviors: Behaviors? = nil)
|
|
79
|
+
-> SwiftUIView<Self, Content>
|
|
80
|
+
{
|
|
81
|
+
SwiftUIView(storage: content) {
|
|
82
|
+
let view = Self()
|
|
83
|
+
view.setContent(content, animated: false)
|
|
84
|
+
return view
|
|
85
|
+
}
|
|
86
|
+
.configure { context in
|
|
87
|
+
// We need to update the content of the existing view when the content is updated.
|
|
88
|
+
if context.oldStorage != content {
|
|
89
|
+
context.view.setContent(content, animated: context.animated)
|
|
90
|
+
context.container.invalidateIntrinsicContentSize()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
context.view.setBehaviors(behaviors)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
99
|
+
extension StyledView
|
|
100
|
+
where
|
|
101
|
+
Self: ContentConfigurableView & BehaviorsConfigurableView,
|
|
102
|
+
Content == Never
|
|
103
|
+
{
|
|
104
|
+
/// Returns a SwiftUI `View` representing this `EpoxyableView`.
|
|
105
|
+
///
|
|
106
|
+
/// To perform additional configuration of the `EpoxyableView` instance, call `configure` on the
|
|
107
|
+
/// returned SwiftUI `View`:
|
|
108
|
+
/// ```
|
|
109
|
+
/// MyView.swiftUIView(…)
|
|
110
|
+
/// .configure { context in
|
|
111
|
+
/// context.view.doSomething()
|
|
112
|
+
/// }
|
|
113
|
+
/// ```
|
|
114
|
+
///
|
|
115
|
+
/// To configure the sizing behavior of the `EpoxyableView` instance, call `sizing` on the
|
|
116
|
+
/// returned SwiftUI `View`:
|
|
117
|
+
/// ```
|
|
118
|
+
/// MyView.swiftUIView(…).sizing(.intrinsicSize)
|
|
119
|
+
/// ```
|
|
120
|
+
/// The sizing defaults to `.automatic`.
|
|
121
|
+
static func swiftUIView(
|
|
122
|
+
style: Style,
|
|
123
|
+
behaviors: Behaviors? = nil)
|
|
124
|
+
-> SwiftUIView<Self, Style>
|
|
125
|
+
{
|
|
126
|
+
SwiftUIView(storage: style) {
|
|
127
|
+
Self(style: style)
|
|
128
|
+
}
|
|
129
|
+
.configure { context in
|
|
130
|
+
// We need to create a new view instance when the style changes.
|
|
131
|
+
if context.oldStorage != style {
|
|
132
|
+
context.view = Self(style: style)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
context.view.setBehaviors(behaviors)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
|
|
141
|
+
extension StyledView
|
|
142
|
+
where
|
|
143
|
+
Self: ContentConfigurableView & BehaviorsConfigurableView,
|
|
144
|
+
Content == Never,
|
|
145
|
+
Style == Never
|
|
146
|
+
{
|
|
147
|
+
/// Returns a SwiftUI `View` representing this `EpoxyableView`.
|
|
148
|
+
///
|
|
149
|
+
/// To perform additional configuration of the `EpoxyableView` instance, call `configure` on the
|
|
150
|
+
/// returned SwiftUI `View`:
|
|
151
|
+
/// ```
|
|
152
|
+
/// MyView.swiftUIView(…)
|
|
153
|
+
/// .configure { context in
|
|
154
|
+
/// context.view.doSomething()
|
|
155
|
+
/// }
|
|
156
|
+
/// ```
|
|
157
|
+
///
|
|
158
|
+
/// To configure the sizing behavior of the `EpoxyableView` instance, call `sizing` on the
|
|
159
|
+
/// returned SwiftUI `View`:
|
|
160
|
+
/// ```
|
|
161
|
+
/// MyView.swiftUIView(…).sizing(.intrinsicSize)
|
|
162
|
+
/// ```
|
|
163
|
+
/// The sizing defaults to `.automatic`.
|
|
164
|
+
static func swiftUIView(behaviors: Behaviors? = nil) -> SwiftUIView<Self, Void> {
|
|
165
|
+
SwiftUIView {
|
|
166
|
+
Self()
|
|
167
|
+
}
|
|
168
|
+
.configure { context in
|
|
169
|
+
context.view.setBehaviors(behaviors)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|