Skip to content

Commit

Permalink
Improve SwiftUI async animation loading APIs (#2127)
Browse files Browse the repository at this point in the history
Co-authored-by: Cal Stephens <[email protected]>
  • Loading branch information
miguel-jimenez-0529 and calda authored Aug 3, 2023
1 parent 25a50f2 commit 6126d6e
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 146 deletions.
6 changes: 0 additions & 6 deletions Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
2E9E77AF27602BD400C84BA3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E9E77AE27602BD400C84BA3 /* AppDelegate.swift */; };
2EC6E5082763D981002E091C /* LinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EC6E5072763D981002E091C /* LinkView.swift */; };
2EC6E5102763E79F002E091C /* AnimationPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EC6E50F2763E79F002E091C /* AnimationPreviewViewController.swift */; };
AB3278112A71A86E00A9C9F1 /* RemoteAnimationDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB82EA9A2A7090B400AEBB48 /* RemoteAnimationDemoView.swift */; };
AB82EA9B2A7090B400AEBB48 /* RemoteAnimationDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB82EA9A2A7090B400AEBB48 /* RemoteAnimationDemoView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -69,7 +67,6 @@
2E9E77BC27602BD400C84BA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
2EC6E5072763D981002E091C /* LinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkView.swift; sourceTree = "<group>"; };
2EC6E50F2763E79F002E091C /* AnimationPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationPreviewViewController.swift; sourceTree = "<group>"; };
AB82EA9A2A7090B400AEBB48 /* RemoteAnimationDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteAnimationDemoView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -104,7 +101,6 @@
08E359932A55FFC400141956 /* LottieViewLayoutDemoView.swift */,
08E359972A55FFC600141956 /* Example.entitlements */,
607FACD11AFB9204008FA782 /* Products */,
AB82EA9A2A7090B400AEBB48 /* RemoteAnimationDemoView.swift */,
);
path = Example;
sourceTree = "<group>";
Expand Down Expand Up @@ -288,7 +284,6 @@
08E359922A55FFC400141956 /* ExampleApp.swift in Sources */,
085D97872A5E0DB600C78D18 /* AnimationPreviewView.swift in Sources */,
085D97852A5DF94C00C78D18 /* AnimationListView.swift in Sources */,
AB3278112A71A86E00A9C9F1 /* RemoteAnimationDemoView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -300,7 +295,6 @@
2E1670CC2784F9C1009CDED3 /* AnimatedSwitchRow.swift in Sources */,
2E97E3052767E7C600FE22C3 /* Configuration.swift in Sources */,
2E1670C32784F009009CDED3 /* ControlsDemoViewController.swift in Sources */,
AB82EA9B2A7090B400AEBB48 /* RemoteAnimationDemoView.swift in Sources */,
2E1670CA2784F123009CDED3 /* AnimatedButtonRow.swift in Sources */,
2E362A1E2762BA06006AE7D2 /* SampleListViewController.swift in Sources */,
2E9E77AF27602BD400C84BA3 /* AppDelegate.swift in Sources */,
Expand Down
132 changes: 105 additions & 27 deletions Example/Example/AnimationListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,67 @@ struct AnimationListView: View {

// MARK: Internal

let directory: String
enum Content: Hashable, Sendable {
case directory(_ directory: String)
case custom(name: String, items: [Item])
}

var content: Content

var body: some View {
List {
ForEach(items, id: \.self) { item in
NavigationLink(value: item) {
switch item {
case .animation(let animationName, _):
case .animation, .remoteAnimations:
HStack {
LottieView(animation: .named(animationName, subdirectory: directory))
.currentProgress(0.5)
.imageProvider(.exampleAppSampleImages)
.frame(width: 50, height: 50)
.padding(EdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8))

Text(animationName)
LottieView {
try await makeThumbnailAnimation(for: item)
}
.currentProgress(0.5)
.imageProvider(.exampleAppSampleImages)
.frame(width: 50, height: 50)
.padding(EdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8))

Text(item.name)
}

case .subdirectory(let subdirectoryURL):
Text(subdirectoryURL.lastPathComponent)
.frame(height: 50)
case .remoteDemo:
Text("Remote animations")
case .animationList:
Text(item.name)
.frame(height: 50)
}
}
.navigationDestination(for: Item.self) { item in
switch item {
case .animation(_, let animationPath):
AnimationPreviewView(animationSource: .local(animationPath: animationPath))
case .subdirectory(let subdirectoryURL):
AnimationListView(directory: "\(directory)/\(subdirectoryURL.lastPathComponent)")
case .remoteDemo:
// View is already contained in a nav stack
RemoteAnimationsDemoView(wrapInNavStack: false)
case .remoteAnimations(let name, let urls):
AnimationPreviewView(animationSource: .remote(urls: urls, name: name))
case .animationList(let listContent):
AnimationListView(content: listContent)
}
}
}
}
.navigationTitle(directory)
.navigationTitle(content.name)
}

func makeThumbnailAnimation(for item: Item) async throws -> LottieAnimationSource? {
switch item {
case .animation(let animationName, _):
if animationName.hasSuffix(".lottie") {
return try await DotLottieFile.named(animationName, subdirectory: directory).animationSource
} else {
return LottieAnimation.named(animationName, subdirectory: directory)?.animationSource
}

case .remoteAnimations(_, let urls):
guard let url = urls.first else { return nil }
return await LottieAnimation.loadedFrom(url: url)?.animationSource

case .animationList:
return nil
}
}

// MARK: Private
Expand All @@ -58,22 +79,37 @@ struct AnimationListView: View {
directory == "Samples"
}

private var directory: String {
switch content {
case .directory(let directory):
return directory
case .custom:
return "n/a"
}
}

}

extension AnimationListView {

// MARK: Internal

enum Item: Hashable {
case subdirectory(URL)
enum Item: Hashable, Sendable {
case animationList(AnimationListView.Content)
case animation(name: String, path: String)
case remoteDemo
case remoteAnimations(name: String, urls: [URL])
}

var items: [Item] {
animations.map { .animation(name: $0.name, path: $0.path) }
+ subdirectoryURLs.map { .subdirectory($0) }
+ customDemos
switch content {
case .directory:
return animations.map { .animation(name: $0.name, path: $0.path) }
+ subdirectoryURLs.map { .animationList(.directory("\(directory)/\($0.lastPathComponent)")) }
+ customDemos

case .custom(_, let items):
return items
}
}

// MARK: Private
Expand Down Expand Up @@ -111,6 +147,48 @@ extension AnimationListView {
}

private var customDemos: [Item] {
isTopLevel ? [.remoteDemo] : []
guard isTopLevel else { return [] }

return [
.animationList(.remoteAnimationsDemo),
]
}
}

extension AnimationListView.Item {
var name: String {
switch self {
case .animation(let animationName, _), .remoteAnimations(let animationName, _):
return animationName
case .animationList(let content):
return content.name
}
}
}

extension AnimationListView.Content {
static var remoteAnimationsDemo: AnimationListView.Content {
.custom(
name: "Remote Animations",
items: [
.remoteAnimations(
name: "Rooms Animation",
urls: [URL(string: "https://a0.muscache.com/pictures/96699af6-b73e-499f-b0f5-3c862ae7d126.json")!]),
.remoteAnimations(
name: "Multiple Animations",
urls: [
URL(string: "https://a0.muscache.com/pictures/a7c140ee-6818-4a8a-b3b1-0c785054a611.json")!,
URL(string: "https://a0.muscache.com/pictures/96699af6-b73e-499f-b0f5-3c862ae7d126.json")!,
]),
])
}

var name: String {
switch self {
case .directory(let directory):
return directory.components(separatedBy: "/").last ?? directory
case .custom(let name, _):
return name
}
}
}
17 changes: 8 additions & 9 deletions Example/Example/AnimationPreviewView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ struct AnimationPreviewView: View {
var body: some View {
VStack {
LottieView {
try await lottieSource()
try await loadAnimation()
} placeholder: {
LoadingIndicator()
.frame(width: 50, height: 50)
}
.imageProvider(.exampleAppSampleImages)
.resizable()
.loadAnimationTrigger($currentURLIndex)
.reloadAnimationTrigger(currentURLIndex, showPlaceholder: false)
.looping()
.currentProgress(animationPlaying ? nil : sliderValue)
.getRealtimeAnimationProgress(animationPlaying ? $sliderValue : nil)
Expand Down Expand Up @@ -86,18 +86,17 @@ struct AnimationPreviewView: View {
@State private var sliderValue: AnimationProgressTime = 0
@State private var currentURLIndex: Int

private func lottieSource() async throws -> LottieAnimationSource? {
private func loadAnimation() async throws -> LottieAnimationSource? {
switch animationSource {
case .local(let name):
if let animation = LottieAnimation.named(name) {
return .lottieAnimation(animation)
if name.hasSuffix(".lottie") {
return try await DotLottieFile.named(name).animationSource
} else {
let lottie = try await DotLottieFile.named(name)
return .dotLottieFile(lottie)
return LottieAnimation.named(name)?.animationSource
}

case .remote:
let animation = await LottieAnimation.loadedFrom(url: urls[currentURLIndex])
return animation.map(LottieAnimationSource.lottieAnimation)
return await LottieAnimation.loadedFrom(url: urls[currentURLIndex])?.animationSource
}
}

Expand Down
2 changes: 1 addition & 1 deletion Example/Example/ExampleApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct ExampleApp: App {
var body: some Scene {
WindowGroup {
NavigationStack {
AnimationListView(directory: "Samples")
AnimationListView(content: .directory("Samples"))
}
}
}
Expand Down
70 changes: 0 additions & 70 deletions Example/Example/RemoteAnimationDemoView.swift

This file was deleted.

12 changes: 0 additions & 12 deletions Example/iOS/ViewControllers/SampleListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ final class SampleListViewController: CollectionViewController {

if isTopLevel {
demoLinks
remoteAnimationLinks
}
}

Expand Down Expand Up @@ -125,17 +124,6 @@ final class SampleListViewController: CollectionViewController {
}
}

@ItemModelBuilder
private var remoteAnimationLinks: [ItemModeling] {
LinkView.itemModel(
dataID: "Remote animations",
content: .init(animationName: nil, title: "Remote animations"))
.didSelect { [weak self] _ in
let remoteAnimationsDemo = UIHostingController(rootView: RemoteAnimationsDemoView(wrapInNavStack: true))
self?.present(remoteAnimationsDemo, animated: true)
}
}

private func configureSettingsMenu() {
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "Settings",
Expand Down
Loading

0 comments on commit 6126d6e

Please sign in to comment.