Skip to content

Commit

Permalink
Update LottieView to display placeholder using overlay instead of ZSt…
Browse files Browse the repository at this point in the history
…ack (airbnb#2289)
  • Loading branch information
miguel-jimenez-0529 authored and Igor Moroz committed May 22, 2024
1 parent 22913c3 commit 1dd696d
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 22 deletions.
4 changes: 4 additions & 0 deletions Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
08E3599F2A56004100141956 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 08E3599E2A56004100141956 /* Lottie */; };
08E6CF822A86C35B00A6D92F /* LottieSwitchRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E6CF812A86C35B00A6D92F /* LottieSwitchRow.swift */; };
08E6CF842A86C49300A6D92F /* ControlsDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E6CF832A86C49300A6D92F /* ControlsDemoView.swift */; };
ABD0BC842B5B451C003D7587 /* SwiftUIInteroperabilityDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD0BC832B5B451C003D7587 /* SwiftUIInteroperabilityDemoView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -32,6 +33,7 @@
08E6CF832A86C49300A6D92F /* ControlsDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsDemoView.swift; sourceTree = "<group>"; };
2E0F2FB627602C1500B65DE3 /* .. */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ..; sourceTree = "<group>"; };
2E3EEB372763C68C00287EEA /* Samples */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Samples; path = ../Tests/Samples; sourceTree = "<group>"; };
ABD0BC832B5B451C003D7587 /* SwiftUIInteroperabilityDemoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUIInteroperabilityDemoView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -49,6 +51,7 @@
08E359902A55FFC400141956 /* Example */ = {
isa = PBXGroup;
children = (
ABD0BC832B5B451C003D7587 /* SwiftUIInteroperabilityDemoView.swift */,
08E359912A55FFC400141956 /* ExampleApp.swift */,
085D97832A5DF76C00C78D18 /* AnimationListView.swift */,
085D97862A5E0DB600C78D18 /* AnimationPreviewView.swift */,
Expand Down Expand Up @@ -165,6 +168,7 @@
buildActionMask = 2147483647;
files = (
0820D59B2A8ACE64007D705C /* LottieButtonRow.swift in Sources */,
ABD0BC842B5B451C003D7587 /* SwiftUIInteroperabilityDemoView.swift in Sources */,
08E359942A55FFC400141956 /* LottieViewLayoutDemoView.swift in Sources */,
08E359922A55FFC400141956 /* ExampleApp.swift in Sources */,
085D97872A5E0DB600C78D18 /* AnimationPreviewView.swift in Sources */,
Expand Down
10 changes: 8 additions & 2 deletions Example/Example/AnimationListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct AnimationListView: View {
Text(item.name)
}

case .animationList, .controlsDemo:
case .animationList, .controlsDemo, .swiftUIInteroperability:
Text(item.name)
.frame(height: 50)
}
Expand All @@ -50,6 +50,8 @@ struct AnimationListView: View {
AnimationListView(content: listContent)
case .controlsDemo:
ControlsDemoView()
case .swiftUIInteroperability:
SwiftUIInteroperabilityDemoView()
}
}
}
Expand All @@ -70,7 +72,7 @@ struct AnimationListView: View {
guard let url = urls.first else { return nil }
return await LottieAnimation.loadedFrom(url: url)?.animationSource

case .animationList, .controlsDemo:
case .animationList, .controlsDemo, .swiftUIInteroperability:
return nil
}
}
Expand Down Expand Up @@ -101,6 +103,7 @@ extension AnimationListView {
case animation(name: String, path: String)
case remoteAnimations(name: String, urls: [URL])
case controlsDemo
case swiftUIInteroperability
}

var items: [Item] {
Expand Down Expand Up @@ -155,6 +158,7 @@ extension AnimationListView {
return [
.animationList(.remoteAnimationsDemo),
.controlsDemo,
.swiftUIInteroperability,
]
}
}
Expand All @@ -168,6 +172,8 @@ extension AnimationListView.Item {
return content.name
case .controlsDemo:
return "Controls Demo"
case .swiftUIInteroperability:
return "SwiftUI Interoperability Demo"
}
}
}
Expand Down
131 changes: 131 additions & 0 deletions Example/Example/SwiftUIInteroperabilityDemoView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Created by miguel_jimenez on 1/19/24.
// Copyright © 2024 Airbnb Inc. All rights reserved.

import Lottie
import SwiftUI

// MARK: - SwiftUIInteroperabilityDemoView

struct SwiftUIInteroperabilityDemoView: View {

var body: some View {
List {
Demo(name: "On appear offset animation") {
OnAppearOffsetAnimation()
}

Demo(name: "Placeholder size inheritance") {
PlaceholderSizeInheritance()
}
}
.navigationTitle("SwiftUI Interoperability Demo")
}
}

// MARK: - Demo

struct Demo<Content: View>: View {

// MARK: Lifecycle

init(name: String, @ViewBuilder content: () -> Content) {
self.name = name
self.content = content()
}

// MARK: Internal

let name: String
let content: Content

var body: some View {
VStack(alignment: .leading) {
Text(name)
.frame(alignment: .top)
Spacer()
HStack(alignment: .center) {
Button(show ? "Hide" : "Show") {
show.toggle()
}
if show {
content
}
}
Spacer()
}
.frame(height: 150)
}

// MARK: Private

@State private var show = false

}

// MARK: - OnAppearOffsetAnimation

/// Demonstrates how `LottieView` is animated by the `.offset` modifier.
struct OnAppearOffsetAnimation: View {

@State private var demo1Appeared = false

var body: some View {
LottieView {
try await DotLottieFile.named("Samples/DotLottie/multiple_animations.lottie")
} placeholder: {
LoadingIndicator()
}
.looping()
.resizable()
.frame(width: 100, height: 100)
.offset(x: demo1Appeared ? 0 : 300)
.onAppear {
withAnimation {
demo1Appeared = true
}
}
}
}

// MARK: - PlaceholderSizeInheritance

/// Demonstrates how the placeholder's `Rectangle` get's its size from it's parent.
struct PlaceholderSizeInheritance: View {

var body: some View {
HStack(alignment: .top) {
LottieView {
await LottieAnimation
.loadedFrom(url: URL(string: "https://a0.muscache.com/pictures/96699af6-b73e-499f-b0f5-3c862ae7d126.json")!)
} placeholder: {
Rectangle()
.fill(.red)
.cornerRadius(20)
}
.resizable()
.frame(width: 100, height: 100)

LottieView {
await LottieAnimation
.loadedFrom(url: URL(string: "https://a0.muscache.com/pictures/96699af6-b73e-499f-b0f5-3c862ae7d126.json")!)
} placeholder: {
Rectangle()
.fill(.red)
.cornerRadius(10)
}
.resizable()
.frame(width: 50, height: 50)

LottieView {
await LottieAnimation
.loadedFrom(url: URL(string: "https://a0.muscache.com/pictures/96699af6-b73e-499f-b0f5-3c862ae7d126.json")!)
} placeholder: {
Rectangle()
.fill(.red)
.cornerRadius(3)
}
.resizable()
.frame(width: 10, height: 10)
}
}
}
51 changes: 31 additions & 20 deletions Sources/Public/Animation/LottieView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,26 +115,24 @@ public struct LottieView<Placeholder: View>: UIViewConfiguringSwiftUIView {
// MARK: Public

public var body: some View {
ZStack {
if let animationSource {
LottieAnimationView.swiftUIView {
defer { animationDidLoad?(animationSource) }
return LottieAnimationView(
animationSource: animationSource,
imageProvider: imageProviderConfiguration?.imageProvider,
textProvider: textProvider,
fontProvider: fontProvider,
configuration: configuration,
logger: logger)
}
.sizing(sizing)
.configure { context in
applyCurrentAnimationConfiguration(to: context.view)
}
.configurations(configurations)
} else {
placeholder?()
}
LottieAnimationView.swiftUIView {
LottieAnimationView(
animationSource: animationSource,
imageProvider: imageProviderConfiguration?.imageProvider,
textProvider: textProvider,
fontProvider: fontProvider,
configuration: configuration,
logger: logger)
}
.sizing(sizing)
.configure { context in
applyCurrentAnimationConfiguration(to: context.view)
}
.configurations(configurations)
.opacity(animationSource == nil ? 0 : 1)
.overlay {
placeholder?()
.opacity(animationSource == nil ? 1 : 0)
}
.onAppear {
loadAnimationIfNecessary()
Expand Down Expand Up @@ -565,4 +563,17 @@ public struct LottieView<Placeholder: View>: UIViewConfiguringSwiftUIView {
}
}
}

@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
extension View {

/// The `.overlay` modifier that uses a `ViewBuilder` is available in iOS 15+, this helper function helps us to use the same API in older OSs
fileprivate func overlay(
@ViewBuilder content: () -> some View)
-> some View
{
overlay(content(), alignment: .center)
}
}

#endif

0 comments on commit 1dd696d

Please sign in to comment.