Skip to content

Commit 96d0957

Browse files
committed
Add cross-platform SwiftUI wrapper for AnimatedSwitch
1 parent 8b89a43 commit 96d0957

File tree

10 files changed

+485
-36
lines changed

10 files changed

+485
-36
lines changed

Example/Example.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
08E359922A55FFC400141956 /* ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E359912A55FFC400141956 /* ExampleApp.swift */; };
1717
08E359942A55FFC400141956 /* LottieViewLayoutDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E359932A55FFC400141956 /* LottieViewLayoutDemoView.swift */; };
1818
08E3599F2A56004100141956 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 08E3599E2A56004100141956 /* Lottie */; };
19+
08E6CF822A86C35B00A6D92F /* LottieSwitchRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E6CF812A86C35B00A6D92F /* LottieSwitchRow.swift */; };
20+
08E6CF842A86C49300A6D92F /* ControlsDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E6CF832A86C49300A6D92F /* ControlsDemoView.swift */; };
1921
2E0F2FBE27602CB300B65DE3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2E0F2FBC27602CB300B65DE3 /* LaunchScreen.storyboard */; };
2022
2E1670C32784F009009CDED3 /* ControlsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E1670C22784F009009CDED3 /* ControlsDemoViewController.swift */; };
2123
2E1670CA2784F123009CDED3 /* AnimatedButtonRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E1670C92784F123009CDED3 /* AnimatedButtonRow.swift */; };
@@ -54,6 +56,8 @@
5456
08E359912A55FFC400141956 /* ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleApp.swift; sourceTree = "<group>"; };
5557
08E359932A55FFC400141956 /* LottieViewLayoutDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieViewLayoutDemoView.swift; sourceTree = "<group>"; };
5658
08E359972A55FFC600141956 /* Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example.entitlements; sourceTree = "<group>"; };
59+
08E6CF812A86C35B00A6D92F /* LottieSwitchRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieSwitchRow.swift; sourceTree = "<group>"; };
60+
08E6CF832A86C49300A6D92F /* ControlsDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsDemoView.swift; sourceTree = "<group>"; };
5761
2E0F2FB627602C1500B65DE3 /* .. */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ..; sourceTree = "<group>"; };
5862
2E0F2FBD27602CB300B65DE3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
5963
2E1670C22784F009009CDED3 /* ControlsDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsDemoViewController.swift; sourceTree = "<group>"; };
@@ -101,6 +105,8 @@
101105
08E359932A55FFC400141956 /* LottieViewLayoutDemoView.swift */,
102106
08E359972A55FFC600141956 /* Example.entitlements */,
103107
607FACD11AFB9204008FA782 /* Products */,
108+
08E6CF812A86C35B00A6D92F /* LottieSwitchRow.swift */,
109+
08E6CF832A86C49300A6D92F /* ControlsDemoView.swift */,
104110
);
105111
path = Example;
106112
sourceTree = "<group>";
@@ -283,7 +289,9 @@
283289
08E359942A55FFC400141956 /* LottieViewLayoutDemoView.swift in Sources */,
284290
08E359922A55FFC400141956 /* ExampleApp.swift in Sources */,
285291
085D97872A5E0DB600C78D18 /* AnimationPreviewView.swift in Sources */,
292+
08E6CF822A86C35B00A6D92F /* LottieSwitchRow.swift in Sources */,
286293
085D97852A5DF94C00C78D18 /* AnimationListView.swift in Sources */,
294+
08E6CF842A86C49300A6D92F /* ControlsDemoView.swift in Sources */,
287295
);
288296
runOnlyForDeploymentPostprocessing = 0;
289297
};
@@ -430,6 +438,7 @@
430438
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
431439
CODE_SIGN_STYLE = Automatic;
432440
DEBUG_INFORMATION_FORMAT = dwarf;
441+
DEVELOPMENT_TEAM = "";
433442
GCC_C_LANGUAGE_STANDARD = gnu11;
434443
INFOPLIST_FILE = iOS/Info.plist;
435444
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
@@ -461,6 +470,7 @@
461470
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
462471
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
463472
CODE_SIGN_STYLE = Automatic;
473+
DEVELOPMENT_TEAM = "";
464474
GCC_C_LANGUAGE_STANDARD = gnu11;
465475
INFOPLIST_FILE = iOS/Info.plist;
466476
IPHONEOS_DEPLOYMENT_TARGET = 16.0;

Example/Example/AnimationListView.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ struct AnimationListView: View {
3535
Text(item.name)
3636
}
3737

38-
case .animationList:
38+
case .animationList, .controlsDemo:
3939
Text(item.name)
4040
.frame(height: 50)
4141
}
@@ -48,6 +48,8 @@ struct AnimationListView: View {
4848
AnimationPreviewView(animationSource: .remote(urls: urls, name: name))
4949
case .animationList(let listContent):
5050
AnimationListView(content: listContent)
51+
case .controlsDemo:
52+
ControlsDemoView()
5153
}
5254
}
5355
}
@@ -68,7 +70,7 @@ struct AnimationListView: View {
6870
guard let url = urls.first else { return nil }
6971
return await LottieAnimation.loadedFrom(url: url)?.animationSource
7072

71-
case .animationList:
73+
case .animationList, .controlsDemo:
7274
return nil
7375
}
7476
}
@@ -98,6 +100,7 @@ extension AnimationListView {
98100
case animationList(AnimationListView.Content)
99101
case animation(name: String, path: String)
100102
case remoteAnimations(name: String, urls: [URL])
103+
case controlsDemo
101104
}
102105

103106
var items: [Item] {
@@ -151,6 +154,7 @@ extension AnimationListView {
151154

152155
return [
153156
.animationList(.remoteAnimationsDemo),
157+
.controlsDemo,
154158
]
155159
}
156160
}
@@ -162,6 +166,8 @@ extension AnimationListView.Item {
162166
return animationName
163167
case .animationList(let content):
164168
return content.name
169+
case .controlsDemo:
170+
return "Controls Demo"
165171
}
166172
}
167173
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Created by Cal Stephens on 8/11/23.
2+
// Copyright © 2023 Airbnb Inc. All rights reserved.
3+
4+
import Lottie
5+
import SwiftUI
6+
7+
// MARK: - ControlsDemoView
8+
9+
struct ControlsDemoView: View {
10+
11+
var body: some View {
12+
List {
13+
LottieSwitchRow(
14+
animationName: "Samples/Switch",
15+
title: "Switch",
16+
onTimeRange: 0.5...1.0,
17+
offTimeRange: 0.0...0.5)
18+
19+
LottieSwitchRow(
20+
animationName: "Samples/Switch",
21+
title: "Switch (Custom Colors)",
22+
onTimeRange: 0.5...1.0,
23+
offTimeRange: 0.0...0.5,
24+
colorValueProviders: [
25+
"Checkmark Outlines.Group 1.Stroke 1.Color": [Keyframe(.black)],
26+
"Checkmark Outlines 2.Group 1.Stroke 1.Color": [Keyframe(.black)],
27+
"X Outlines.Group 1.Stroke 1.Color": [Keyframe(.black)],
28+
"Switch Outline Outlines.Fill 1.Color": [
29+
Keyframe(value: LottieColor.black, time: 0),
30+
Keyframe(value: LottieColor(r: 0.76, g: 0.76, b: 0.76, a: 1), time: 75),
31+
Keyframe(value: LottieColor.black, time: 150),
32+
],
33+
])
34+
35+
// TODO: Add SwiftUI support for AnimatedButton
36+
// AnimatedButtonRow(
37+
// animationName: "Samples/TwitterHeartButton",
38+
// title: "Twitter Heart Button",
39+
// playRanges: [
40+
// .init(fromMarker: "touchDownStart", toMarker: "touchDownEnd", event: .touchDown),
41+
// .init(fromMarker: "touchDownEnd", toMarker: "touchUpCancel", event: .touchUpOutside),
42+
// .init(fromMarker: "touchDownEnd", toMarker: "touchUpEnd", event: .touchUpInside),
43+
// ]))
44+
45+
LottieSwitchRow(
46+
animationName: "Samples/Issues/issue_1877",
47+
title: "Issue #1877",
48+
onTimeRange: nil, // use the default (0...1)
49+
offTimeRange: nil, // use the default (1...0)
50+
colorValueProviders: ["**.Color": [Keyframe(.black)]])
51+
}
52+
.navigationTitle("Controls Demo")
53+
}
54+
55+
}
56+
57+
extension LottieColor {
58+
static var black: LottieColor {
59+
.init(r: 0, g: 0, b: 0, a: 1)
60+
}
61+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Created by Cal Stephens on 8/11/23.
2+
// Copyright © 2023 Airbnb Inc. All rights reserved.
3+
4+
import Lottie
5+
import SwiftUI
6+
7+
// MARK: - LottieSwitchRow
8+
9+
struct LottieSwitchRow: View {
10+
11+
// MARK: Internal
12+
13+
var animationName: String
14+
var title: String
15+
var onTimeRange: ClosedRange<AnimationProgressTime>?
16+
var offTimeRange: ClosedRange<AnimationProgressTime>?
17+
var colorValueProviders: [String: [Keyframe<LottieColor>]] = [:]
18+
19+
var body: some View {
20+
HStack {
21+
LottieSwitch(animation: .named(animationName))
22+
.isOn($isOn)
23+
.onAnimation(
24+
fromProgress: onTimeRange?.lowerBound ?? 0,
25+
toProgress: onTimeRange?.upperBound ?? 1)
26+
.offAnimation(
27+
fromProgress: offTimeRange?.lowerBound ?? 1,
28+
toProgress: offTimeRange?.upperBound ?? 0)
29+
.colorValueProviders(colorValueProviders)
30+
.frame(width: 80, height: 80)
31+
32+
Text(verbatim: "\(title) (isOn=\(isOn))")
33+
}
34+
}
35+
36+
// MARK: Private
37+
38+
@State private var isOn = false
39+
}
40+
41+
extension LottieSwitch {
42+
func colorValueProviders(_ colorValueProviders: [String: [Keyframe<LottieColor>]]) -> Self {
43+
var copy = self
44+
45+
for (keypath, keyframes) in colorValueProviders {
46+
copy = copy.valueProvider(
47+
ColorValueProvider(keyframes),
48+
for: AnimationKeypath(keypath: keypath))
49+
}
50+
51+
return copy
52+
}
53+
}

0 commit comments

Comments
 (0)