Skip to content

Commit

Permalink
Add cross-platform SwiftUI wrapper for AnimatedSwitch
Browse files Browse the repository at this point in the history
  • Loading branch information
calda committed Aug 11, 2023
1 parent 8b89a43 commit d96a346
Show file tree
Hide file tree
Showing 10 changed files with 489 additions and 36 deletions.
10 changes: 10 additions & 0 deletions Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
08E359922A55FFC400141956 /* ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E359912A55FFC400141956 /* ExampleApp.swift */; };
08E359942A55FFC400141956 /* LottieViewLayoutDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E359932A55FFC400141956 /* LottieViewLayoutDemoView.swift */; };
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 */; };
2E0F2FBE27602CB300B65DE3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2E0F2FBC27602CB300B65DE3 /* LaunchScreen.storyboard */; };
2E1670C32784F009009CDED3 /* ControlsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E1670C22784F009009CDED3 /* ControlsDemoViewController.swift */; };
2E1670CA2784F123009CDED3 /* AnimatedButtonRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E1670C92784F123009CDED3 /* AnimatedButtonRow.swift */; };
Expand Down Expand Up @@ -54,6 +56,8 @@
08E359912A55FFC400141956 /* ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleApp.swift; sourceTree = "<group>"; };
08E359932A55FFC400141956 /* LottieViewLayoutDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieViewLayoutDemoView.swift; sourceTree = "<group>"; };
08E359972A55FFC600141956 /* Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example.entitlements; sourceTree = "<group>"; };
08E6CF812A86C35B00A6D92F /* LottieSwitchRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieSwitchRow.swift; sourceTree = "<group>"; };
08E6CF832A86C49300A6D92F /* ControlsDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsDemoView.swift; sourceTree = "<group>"; };
2E0F2FB627602C1500B65DE3 /* .. */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ..; sourceTree = "<group>"; };
2E0F2FBD27602CB300B65DE3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
2E1670C22784F009009CDED3 /* ControlsDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsDemoViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -101,6 +105,8 @@
08E359932A55FFC400141956 /* LottieViewLayoutDemoView.swift */,
08E359972A55FFC600141956 /* Example.entitlements */,
607FACD11AFB9204008FA782 /* Products */,
08E6CF812A86C35B00A6D92F /* LottieSwitchRow.swift */,
08E6CF832A86C49300A6D92F /* ControlsDemoView.swift */,
);
path = Example;
sourceTree = "<group>";
Expand Down Expand Up @@ -283,7 +289,9 @@
08E359942A55FFC400141956 /* LottieViewLayoutDemoView.swift in Sources */,
08E359922A55FFC400141956 /* ExampleApp.swift in Sources */,
085D97872A5E0DB600C78D18 /* AnimationPreviewView.swift in Sources */,
08E6CF822A86C35B00A6D92F /* LottieSwitchRow.swift in Sources */,
085D97852A5DF94C00C78D18 /* AnimationListView.swift in Sources */,
08E6CF842A86C49300A6D92F /* ControlsDemoView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -430,6 +438,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = iOS/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
Expand Down Expand Up @@ -461,6 +470,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = iOS/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
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:
case .animationList, .controlsDemo:
Text(item.name)
.frame(height: 50)
}
Expand All @@ -48,6 +48,8 @@ struct AnimationListView: View {
AnimationPreviewView(animationSource: .remote(urls: urls, name: name))
case .animationList(let listContent):
AnimationListView(content: listContent)
case .controlsDemo:
ControlsDemoView()
}
}
}
Expand All @@ -68,7 +70,7 @@ struct AnimationListView: View {
guard let url = urls.first else { return nil }
return await LottieAnimation.loadedFrom(url: url)?.animationSource

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

var items: [Item] {
Expand Down Expand Up @@ -151,6 +154,7 @@ extension AnimationListView {

return [
.animationList(.remoteAnimationsDemo),
.controlsDemo,
]
}
}
Expand All @@ -162,6 +166,8 @@ extension AnimationListView.Item {
return animationName
case .animationList(let content):
return content.name
case .controlsDemo:
return "Controls Demo"
}
}
}
Expand Down
61 changes: 61 additions & 0 deletions Example/Example/ControlsDemoView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Created by Cal Stephens on 8/11/23.
// Copyright © 2023 Airbnb Inc. All rights reserved.

import Lottie
import SwiftUI

// MARK: - ControlsDemoView

struct ControlsDemoView: View {

var body: some View {
List {
LottieSwitchRow(
animationName: "Samples/Switch",
title: "Switch",
onTimeRange: 0.5...1.0,
offTimeRange: 0.0...0.5)

LottieSwitchRow(
animationName: "Samples/Switch",
title: "Switch (Custom Colors)",
onTimeRange: 0.5...1.0,
offTimeRange: 0.0...0.5,
colorValueProviders: [
"Checkmark Outlines.Group 1.Stroke 1.Color": [Keyframe(.black)],
"Checkmark Outlines 2.Group 1.Stroke 1.Color": [Keyframe(.black)],
"X Outlines.Group 1.Stroke 1.Color": [Keyframe(.black)],
"Switch Outline Outlines.Fill 1.Color": [
Keyframe(value: LottieColor.black, time: 0),
Keyframe(value: LottieColor(r: 0.76, g: 0.76, b: 0.76, a: 1), time: 75),
Keyframe(value: LottieColor.black, time: 150),
],
])

// TODO: Add SwiftUI support for AnimatedButton
// AnimatedButtonRow(
// animationName: "Samples/TwitterHeartButton",
// title: "Twitter Heart Button",
// playRanges: [
// .init(fromMarker: "touchDownStart", toMarker: "touchDownEnd", event: .touchDown),
// .init(fromMarker: "touchDownEnd", toMarker: "touchUpCancel", event: .touchUpOutside),
// .init(fromMarker: "touchDownEnd", toMarker: "touchUpEnd", event: .touchUpInside),
// ]))

LottieSwitchRow(
animationName: "Samples/Issues/issue_1877",
title: "Issue #1877",
onTimeRange: nil, // use the default (0...1)
offTimeRange: nil, // use the default (1...0)
colorValueProviders: ["**.Color": [Keyframe(.black)]])
}
.navigationTitle("Controls Demo")
}

}

extension LottieColor {
static var black: LottieColor {
.init(r: 0, g: 0, b: 0, a: 1)
}
}
53 changes: 53 additions & 0 deletions Example/Example/LottieSwitchRow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Created by Cal Stephens on 8/11/23.
// Copyright © 2023 Airbnb Inc. All rights reserved.

import Lottie
import SwiftUI

// MARK: - LottieSwitchRow

struct LottieSwitchRow: View {

// MARK: Internal

var animationName: String
var title: String
var onTimeRange: ClosedRange<AnimationProgressTime>?
var offTimeRange: ClosedRange<AnimationProgressTime>?
var colorValueProviders: [String: [Keyframe<LottieColor>]] = [:]

var body: some View {
HStack {
LottieSwitch(animation: .named(animationName))
.isOn($isOn)
.onAnimation(
fromProgress: onTimeRange?.lowerBound ?? 0,
toProgress: onTimeRange?.upperBound ?? 1)
.offAnimation(
fromProgress: offTimeRange?.lowerBound ?? 1,
toProgress: offTimeRange?.upperBound ?? 0)
.colorValueProviders(colorValueProviders)
.frame(width: 80, height: 80)

Text(verbatim: "\(title) (isOn=\(isOn))")
}
}

// MARK: Private

@State private var isOn = false
}

extension LottieSwitch {
func colorValueProviders(_ colorValueProviders: [String: [Keyframe<LottieColor>]]) -> Self {
var copy = self

for (keypath, keyframes) in colorValueProviders {
copy = copy.valueProvider(
ColorValueProvider(keyframes),
for: AnimationKeypath(keypath: keypath))
}

return copy
}
}
Loading

0 comments on commit d96a346

Please sign in to comment.