lottie-ios 4.3.2 → 4.3.4
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/Lottie.xcodeproj/project.pbxproj +20 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +4 -4
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +204 -81
- package/Package.swift +1 -0
- package/README.md +1 -1
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +98 -30
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +1 -1
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +1 -3
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +2 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +2 -1
- package/Sources/Private/EmbeddedLibraries/LRUCache/LRUCache.swift +256 -0
- package/Sources/Private/EmbeddedLibraries/LRUCache/README.md +24 -0
- package/Sources/Private/EmbeddedLibraries/README.md +14 -0
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +11 -5
- package/Sources/Private/MainThread/LayerContainers/Utility/InvertedMatteLayer.swift +0 -1
- package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +5 -2
- package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +2 -2
- package/Sources/Private/Model/Layers/LayerModel.swift +5 -0
- package/Sources/Private/Utility/Helpers/Binding+Map.swift +2 -0
- package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +2 -0
- package/Sources/Public/Animation/LottieAnimationLayer.swift +5 -0
- package/Sources/Public/Animation/LottieAnimationView.swift +12 -1
- package/Sources/Public/Animation/LottieView.swift +2 -0
- package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +10 -6
- package/Sources/Public/Controls/LottieButton.swift +2 -1
- package/Sources/Public/Controls/LottieSwitch.swift +2 -0
- package/Sources/Public/DotLottie/Cache/DotLottieCache.swift +8 -4
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +12 -5
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +5 -2
- package/lottie-ios.podspec +2 -1
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Created by matthew_cheok on 11/19/21.
|
|
2
2
|
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
6
7
|
// MARK: - EpoxyIntrinsicContentSizeInvalidator
|
|
@@ -42,3 +43,4 @@ extension EnvironmentValues {
|
|
|
42
43
|
private struct EpoxyIntrinsicContentSizeInvalidatorKey: EnvironmentKey {
|
|
43
44
|
static let defaultValue = EpoxyIntrinsicContentSizeInvalidator(invalidate: { })
|
|
44
45
|
}
|
|
46
|
+
#endif
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Created by eric_horacek on 10/8/21.
|
|
2
2
|
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
6
7
|
// MARK: - View
|
|
@@ -49,3 +50,4 @@ private struct EpoxyLayoutMarginsPadding: ViewModifier {
|
|
|
49
50
|
content.padding(epoxyLayoutMargins)
|
|
50
51
|
}
|
|
51
52
|
}
|
|
53
|
+
#endif
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Created by eric_horacek on 6/22/22.
|
|
2
2
|
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
6
7
|
// MARK: - MeasuringViewRepresentable
|
|
@@ -126,3 +127,4 @@ extension MeasuringViewRepresentable {
|
|
|
126
127
|
#endif
|
|
127
128
|
}
|
|
128
129
|
#endif
|
|
130
|
+
#endif
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Created by Bryn Bodayle on 1/24/22.
|
|
2
2
|
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
6
7
|
// MARK: - SwiftUIMeasurementContainer
|
|
@@ -450,3 +451,4 @@ extension CGSize {
|
|
|
450
451
|
height: height == ViewType.noIntrinsicMetric ? fallback.height : height)
|
|
451
452
|
}
|
|
452
453
|
}
|
|
454
|
+
#endif
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Created by eric_horacek on 9/8/22.
|
|
2
2
|
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
6
7
|
// MARK: - SwiftUIView
|
|
@@ -146,3 +147,4 @@ extension SwiftUIView {
|
|
|
146
147
|
fileprivate(set) var storage: Storage
|
|
147
148
|
}
|
|
148
149
|
}
|
|
150
|
+
#endif
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Created by eric_horacek on 3/3/22.
|
|
2
2
|
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
6
7
|
// MARK: - ViewTypeProtocol + swiftUIView
|
|
@@ -38,3 +39,4 @@ protocol ViewTypeProtocol: ViewType { }
|
|
|
38
39
|
// MARK: - ViewType + ViewTypeProtocol
|
|
39
40
|
|
|
40
41
|
extension ViewType: ViewTypeProtocol { }
|
|
42
|
+
#endif
|
package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Created by eric_horacek on 3/4/22.
|
|
2
2
|
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
6
7
|
// MARK: - UIViewConfiguringSwiftUIView
|
|
@@ -41,3 +42,4 @@ extension UIViewConfiguringSwiftUIView {
|
|
|
41
42
|
return copy
|
|
42
43
|
}
|
|
43
44
|
}
|
|
45
|
+
#endif
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Created by Cal Stephens on 6/26/23.
|
|
2
2
|
// Copyright © 2023 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
|
-
|
|
6
6
|
#if canImport(UIKit)
|
|
7
7
|
import UIKit
|
|
8
8
|
|
|
@@ -49,3 +49,4 @@ extension ViewRepresentableType {
|
|
|
49
49
|
typealias RepresentableViewType = NSViewType
|
|
50
50
|
}
|
|
51
51
|
#endif
|
|
52
|
+
#endif
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
//
|
|
2
|
+
// LRUCache.swift
|
|
3
|
+
// LRUCache
|
|
4
|
+
//
|
|
5
|
+
// Version 1.0.2
|
|
6
|
+
//
|
|
7
|
+
// Created by Nick Lockwood on 05/08/2021.
|
|
8
|
+
// Copyright © 2021 Nick Lockwood. All rights reserved.
|
|
9
|
+
//
|
|
10
|
+
// Distributed under the permissive MIT license
|
|
11
|
+
// Get the latest version from here:
|
|
12
|
+
//
|
|
13
|
+
// https://github.com/nicklockwood/LRUCache
|
|
14
|
+
//
|
|
15
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
16
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
17
|
+
// in the Software without restriction, including without limitation the rights
|
|
18
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
19
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
20
|
+
// furnished to do so, subject to the following conditions:
|
|
21
|
+
//
|
|
22
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
23
|
+
// copies or substantial portions of the Software.
|
|
24
|
+
//
|
|
25
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
29
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
31
|
+
// SOFTWARE.
|
|
32
|
+
//
|
|
33
|
+
|
|
34
|
+
import Foundation
|
|
35
|
+
|
|
36
|
+
#if os(iOS) || os(tvOS)
|
|
37
|
+
import UIKit
|
|
38
|
+
|
|
39
|
+
/// Notification that cache should be cleared
|
|
40
|
+
let LRUCacheMemoryWarningNotification: NSNotification.Name =
|
|
41
|
+
UIApplication.didReceiveMemoryWarningNotification
|
|
42
|
+
|
|
43
|
+
#else
|
|
44
|
+
|
|
45
|
+
/// Notification that cache should be cleared
|
|
46
|
+
let LRUCacheMemoryWarningNotification: NSNotification.Name =
|
|
47
|
+
.init("LRUCacheMemoryWarningNotification")
|
|
48
|
+
|
|
49
|
+
#endif
|
|
50
|
+
|
|
51
|
+
// MARK: - LRUCache
|
|
52
|
+
|
|
53
|
+
final class LRUCache<Key: Hashable, Value> {
|
|
54
|
+
|
|
55
|
+
// MARK: Lifecycle
|
|
56
|
+
|
|
57
|
+
/// Initialize the cache with the specified `totalCostLimit` and `countLimit`
|
|
58
|
+
init(
|
|
59
|
+
totalCostLimit: Int = .max,
|
|
60
|
+
countLimit: Int = .max,
|
|
61
|
+
notificationCenter: NotificationCenter = .default)
|
|
62
|
+
{
|
|
63
|
+
self.totalCostLimit = totalCostLimit
|
|
64
|
+
self.countLimit = countLimit
|
|
65
|
+
self.notificationCenter = notificationCenter
|
|
66
|
+
|
|
67
|
+
token = notificationCenter.addObserver(
|
|
68
|
+
forName: LRUCacheMemoryWarningNotification,
|
|
69
|
+
object: nil,
|
|
70
|
+
queue: nil)
|
|
71
|
+
{ [weak self] _ in
|
|
72
|
+
self?.removeAllValues()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
deinit {
|
|
77
|
+
if let token = token {
|
|
78
|
+
notificationCenter.removeObserver(token)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// MARK: Internal
|
|
83
|
+
|
|
84
|
+
/// The current total cost of values in the cache
|
|
85
|
+
private(set) var totalCost = 0
|
|
86
|
+
|
|
87
|
+
/// The maximum total cost permitted
|
|
88
|
+
var totalCostLimit: Int {
|
|
89
|
+
didSet { clean() }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// The maximum number of values permitted
|
|
93
|
+
var countLimit: Int {
|
|
94
|
+
didSet { clean() }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// MARK: Private
|
|
98
|
+
|
|
99
|
+
private var values: [Key: Container] = [:]
|
|
100
|
+
private unowned(unsafe) var head: Container?
|
|
101
|
+
private unowned(unsafe) var tail: Container?
|
|
102
|
+
private let lock: NSLock = .init()
|
|
103
|
+
private var token: AnyObject?
|
|
104
|
+
private let notificationCenter: NotificationCenter
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
extension LRUCache {
|
|
109
|
+
/// The number of values currently stored in the cache
|
|
110
|
+
var count: Int {
|
|
111
|
+
values.count
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// Is the cache empty?
|
|
115
|
+
var isEmpty: Bool {
|
|
116
|
+
values.isEmpty
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Returns all values in the cache from oldest to newest
|
|
120
|
+
var allValues: [Value] {
|
|
121
|
+
lock.lock()
|
|
122
|
+
defer { lock.unlock() }
|
|
123
|
+
var values = [Value]()
|
|
124
|
+
var next = head
|
|
125
|
+
while let container = next {
|
|
126
|
+
values.append(container.value)
|
|
127
|
+
next = container.next
|
|
128
|
+
}
|
|
129
|
+
return values
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Insert a value into the cache with optional `cost`
|
|
133
|
+
func setValue(_ value: Value?, forKey key: Key, cost: Int = 0) {
|
|
134
|
+
guard let value = value else {
|
|
135
|
+
removeValue(forKey: key)
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
lock.lock()
|
|
139
|
+
if let container = values[key] {
|
|
140
|
+
container.value = value
|
|
141
|
+
totalCost -= container.cost
|
|
142
|
+
container.cost = cost
|
|
143
|
+
remove(container)
|
|
144
|
+
append(container)
|
|
145
|
+
} else {
|
|
146
|
+
let container = Container(
|
|
147
|
+
value: value,
|
|
148
|
+
cost: cost,
|
|
149
|
+
key: key)
|
|
150
|
+
values[key] = container
|
|
151
|
+
append(container)
|
|
152
|
+
}
|
|
153
|
+
totalCost += cost
|
|
154
|
+
lock.unlock()
|
|
155
|
+
clean()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Remove a value from the cache and return it
|
|
159
|
+
@discardableResult
|
|
160
|
+
func removeValue(forKey key: Key) -> Value? {
|
|
161
|
+
lock.lock()
|
|
162
|
+
defer { lock.unlock() }
|
|
163
|
+
guard let container = values.removeValue(forKey: key) else {
|
|
164
|
+
return nil
|
|
165
|
+
}
|
|
166
|
+
remove(container)
|
|
167
|
+
totalCost -= container.cost
|
|
168
|
+
return container.value
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// Fetch a value from the cache
|
|
172
|
+
func value(forKey key: Key) -> Value? {
|
|
173
|
+
lock.lock()
|
|
174
|
+
defer { lock.unlock() }
|
|
175
|
+
if let container = values[key] {
|
|
176
|
+
remove(container)
|
|
177
|
+
append(container)
|
|
178
|
+
return container.value
|
|
179
|
+
}
|
|
180
|
+
return nil
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// Remove all values from the cache
|
|
184
|
+
func removeAllValues() {
|
|
185
|
+
lock.lock()
|
|
186
|
+
values.removeAll()
|
|
187
|
+
head = nil
|
|
188
|
+
tail = nil
|
|
189
|
+
lock.unlock()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
extension LRUCache {
|
|
194
|
+
|
|
195
|
+
// MARK: Fileprivate
|
|
196
|
+
|
|
197
|
+
fileprivate final class Container {
|
|
198
|
+
|
|
199
|
+
// MARK: Lifecycle
|
|
200
|
+
|
|
201
|
+
init(value: Value, cost: Int, key: Key) {
|
|
202
|
+
self.value = value
|
|
203
|
+
self.cost = cost
|
|
204
|
+
self.key = key
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// MARK: Internal
|
|
208
|
+
|
|
209
|
+
var value: Value
|
|
210
|
+
var cost: Int
|
|
211
|
+
let key: Key
|
|
212
|
+
unowned(unsafe) var prev: Container?
|
|
213
|
+
unowned(unsafe) var next: Container?
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// MARK: Private
|
|
218
|
+
|
|
219
|
+
// Remove container from list (must be called inside lock)
|
|
220
|
+
private func remove(_ container: Container) {
|
|
221
|
+
if head === container {
|
|
222
|
+
head = container.next
|
|
223
|
+
}
|
|
224
|
+
if tail === container {
|
|
225
|
+
tail = container.prev
|
|
226
|
+
}
|
|
227
|
+
container.next?.prev = container.prev
|
|
228
|
+
container.prev?.next = container.next
|
|
229
|
+
container.next = nil
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Append container to list (must be called inside lock)
|
|
233
|
+
private func append(_ container: Container) {
|
|
234
|
+
assert(container.next == nil)
|
|
235
|
+
if head == nil {
|
|
236
|
+
head = container
|
|
237
|
+
}
|
|
238
|
+
container.prev = tail
|
|
239
|
+
tail?.next = container
|
|
240
|
+
tail = container
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Remove expired values (must be called outside lock)
|
|
244
|
+
private func clean() {
|
|
245
|
+
lock.lock()
|
|
246
|
+
defer { lock.unlock() }
|
|
247
|
+
while
|
|
248
|
+
totalCost > totalCostLimit || count > countLimit,
|
|
249
|
+
let container = head
|
|
250
|
+
{
|
|
251
|
+
remove(container)
|
|
252
|
+
values.removeValue(forKey: container.key)
|
|
253
|
+
totalCost -= container.cost
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
## LRUCache
|
|
2
|
+
|
|
3
|
+
This directory includes the source code of the LRUCache library, from the following release:
|
|
4
|
+
https://github.com/nicklockwood/LRUCache/releases/tag/1.0.4
|
|
5
|
+
|
|
6
|
+
Lottie is distributed via multiple package managers (SPM, Cocoapods, Carthage, and NPM),
|
|
7
|
+
each with different packaging and compilation requirements.
|
|
8
|
+
|
|
9
|
+
Due to limitations of these package managers, we can't depend on / import
|
|
10
|
+
a separate LRUCache module / library. Instead, we include the source
|
|
11
|
+
directly within the Lottie library and compile everything as a single unit.
|
|
12
|
+
|
|
13
|
+
### Update instructions
|
|
14
|
+
|
|
15
|
+
From time to time we may need to update to a more recent version of LRUCache.
|
|
16
|
+
When doing this, follow these steps:
|
|
17
|
+
|
|
18
|
+
1. Download the latest release from https://github.com/nicklockwood/LRUCache
|
|
19
|
+
and replace the source code in this directory with the updated code.
|
|
20
|
+
|
|
21
|
+
2. Update the URL at the top of this file to indicate what release is being used.
|
|
22
|
+
|
|
23
|
+
3. Change all of the `public` symbols defined in this module to instead be `internal`
|
|
24
|
+
to prevent Lottie from exposing any EpoxyCore APIs.
|
|
@@ -5,6 +5,7 @@ This directory includes the source code of libraries that are embedded within lo
|
|
|
5
5
|
This includes:
|
|
6
6
|
- ZipFoundation (https://github.com/weichsel/ZIPFoundation)
|
|
7
7
|
- EpoxyCore (https://github.com/airbnb/epoxy-ios)
|
|
8
|
+
- LRUCache (https://github.com/nicklockwood/LRUCache)
|
|
8
9
|
|
|
9
10
|
Lottie is distributed via multiple package managers (SPM, Cocoapods, Carthage, and NPM),
|
|
10
11
|
each with different packaging and compilation requirements.
|
|
@@ -25,3 +26,16 @@ When doing this, follow these steps:
|
|
|
25
26
|
|
|
26
27
|
3. Change all of the `public` symbols defined in the module to instead be `internal`
|
|
27
28
|
to prevent Lottie from exposing any APIs from other libraries.
|
|
29
|
+
|
|
30
|
+
### Adding a new dependencies
|
|
31
|
+
|
|
32
|
+
1. Create a subdirectory in `EmbeddedLibraries` for the new dependency.
|
|
33
|
+
|
|
34
|
+
2. Add the dependency to the list at the top of this file.
|
|
35
|
+
|
|
36
|
+
3. Add a `README.md` to the directory for the new library, using the same formatting as the `README.md` file used by other dependencies.
|
|
37
|
+
|
|
38
|
+
4. Exclude the new `README.md` file from the lottie-ios package by adding it to the `exclude:` list in `Package.swift`.
|
|
39
|
+
|
|
40
|
+
5. Change all of the `public` symbols defined in the module to instead be `internal`
|
|
41
|
+
to prevent Lottie from exposing any APIs from other libraries.
|
|
@@ -22,11 +22,11 @@ private final class CachedImageProvider: AnimationImageProvider {
|
|
|
22
22
|
// MARK: Public
|
|
23
23
|
|
|
24
24
|
public func imageForAsset(asset: ImageAsset) -> CGImage? {
|
|
25
|
-
if let image = imageCache.
|
|
25
|
+
if let image = imageCache.value(forKey: asset.id) {
|
|
26
26
|
return image
|
|
27
27
|
}
|
|
28
28
|
if let image = imageProvider.imageForAsset(asset: asset) {
|
|
29
|
-
imageCache.
|
|
29
|
+
imageCache.setValue(image, forKey: asset.id)
|
|
30
30
|
return image
|
|
31
31
|
}
|
|
32
32
|
return nil
|
|
@@ -34,13 +34,19 @@ private final class CachedImageProvider: AnimationImageProvider {
|
|
|
34
34
|
|
|
35
35
|
// MARK: Internal
|
|
36
36
|
|
|
37
|
-
let imageCache: NSCache<NSString, CGImage> = .init()
|
|
38
|
-
let imageProvider: AnimationImageProvider
|
|
39
|
-
|
|
40
37
|
func contentsGravity(for asset: ImageAsset) -> CALayerContentsGravity {
|
|
41
38
|
imageProvider.contentsGravity(for: asset)
|
|
42
39
|
}
|
|
43
40
|
|
|
41
|
+
// MARK: Private
|
|
42
|
+
|
|
43
|
+
/// The underlying storage of this cache.
|
|
44
|
+
/// - We use the `LRUCache` library instead of `NSCache`, because `NSCache`
|
|
45
|
+
/// clears all cached values when the app is backgrounded instead of
|
|
46
|
+
/// only when the app receives a memory warning notification.
|
|
47
|
+
private var imageCache = LRUCache<String, CGImage>()
|
|
48
|
+
private let imageProvider: AnimationImageProvider
|
|
49
|
+
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
extension AnimationImageProvider {
|
|
@@ -9,6 +9,9 @@ protocol ClassFamily: Decodable {
|
|
|
9
9
|
/// The discriminator key.
|
|
10
10
|
static var discriminator: Discriminator { get }
|
|
11
11
|
|
|
12
|
+
/// The "unknown" fallback case if the type discriminator could not be parsed successfully.
|
|
13
|
+
static var unknown: Self { get }
|
|
14
|
+
|
|
12
15
|
/// Returns the class type of the object corresponding to the value.
|
|
13
16
|
func getType() -> AnyObject.Type
|
|
14
17
|
}
|
|
@@ -34,7 +37,7 @@ extension KeyedDecodingContainer {
|
|
|
34
37
|
var tmpContainer = container
|
|
35
38
|
while !container.isAtEnd {
|
|
36
39
|
let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self)
|
|
37
|
-
let family: U = try typeContainer.
|
|
40
|
+
let family: U = (try? typeContainer.decodeIfPresent(U.self, forKey: U.discriminator)) ?? .unknown
|
|
38
41
|
if let type = family.getType() as? T.Type {
|
|
39
42
|
list.append(try tmpContainer.decode(type))
|
|
40
43
|
}
|
|
@@ -60,7 +63,7 @@ extension KeyedDecodingContainer {
|
|
|
60
63
|
var tmpContainer = container
|
|
61
64
|
while !container.isAtEnd {
|
|
62
65
|
let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self)
|
|
63
|
-
let family: U = try typeContainer.
|
|
66
|
+
let family: U = (try? typeContainer.decodeIfPresent(U.self, forKey: U.discriminator)) ?? .unknown
|
|
64
67
|
if let type = family.getType() as? T.Type {
|
|
65
68
|
list.append(try tmpContainer.decode(type))
|
|
66
69
|
}
|
|
@@ -50,8 +50,8 @@ class EffectValue: Codable, DictionaryInitializable {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
required init(dictionary: [String: Any]) throws {
|
|
53
|
-
type =
|
|
54
|
-
name = try dictionary.value(for: CodingKeys.name) ?? "Effect"
|
|
53
|
+
type = (try? dictionary.value(for: CodingKeys.type)).flatMap(EffectValueType.init(rawValue:)) ?? .unknown
|
|
54
|
+
name = (try? dictionary.value(for: CodingKeys.name)) ?? "Effect"
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// MARK: Internal
|
|
@@ -27,6 +27,8 @@ extension LayerType: ClassFamily {
|
|
|
27
27
|
return ShapeLayerModel.self
|
|
28
28
|
case .text:
|
|
29
29
|
return TextLayerModel.self
|
|
30
|
+
case .unknown:
|
|
31
|
+
return LayerModel.self
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -40,6 +42,7 @@ public enum LayerType: Int, Codable {
|
|
|
40
42
|
case null
|
|
41
43
|
case shape
|
|
42
44
|
case text
|
|
45
|
+
case unknown
|
|
43
46
|
|
|
44
47
|
public init(from decoder: Decoder) throws {
|
|
45
48
|
self = try LayerType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .null
|
|
@@ -241,6 +244,8 @@ extension Array where Element == LayerModel {
|
|
|
241
244
|
return try ShapeLayerModel(dictionary: dictionary)
|
|
242
245
|
case .text:
|
|
243
246
|
return try TextLayerModel(dictionary: dictionary)
|
|
247
|
+
case .unknown:
|
|
248
|
+
return try LayerModel(dictionary: dictionary)
|
|
244
249
|
case .none:
|
|
245
250
|
return nil
|
|
246
251
|
}
|
|
@@ -1134,11 +1134,16 @@ public class LottieAnimationLayer: CALayer {
|
|
|
1134
1134
|
}
|
|
1135
1135
|
|
|
1136
1136
|
fileprivate func makeAnimationLayer(usingEngine renderingEngine: RenderingEngineOption) {
|
|
1137
|
+
/// Disable the default implicit crossfade animation Core Animation creates
|
|
1138
|
+
/// when adding or removing sublayers.
|
|
1139
|
+
actions = ["sublayers": NSNull()]
|
|
1140
|
+
|
|
1137
1141
|
/// Remove current animation if any
|
|
1138
1142
|
removeCurrentAnimation()
|
|
1139
1143
|
|
|
1140
1144
|
if let oldAnimation = animationLayer {
|
|
1141
1145
|
oldAnimation.removeFromSuperlayer()
|
|
1146
|
+
rootAnimationLayer = nil
|
|
1142
1147
|
}
|
|
1143
1148
|
|
|
1144
1149
|
guard let animation = animation else {
|
|
@@ -368,6 +368,13 @@ open class LottieAnimationView: LottieAnimationViewBase {
|
|
|
368
368
|
|
|
369
369
|
// MARK: Public
|
|
370
370
|
|
|
371
|
+
/// Whether or not transform and position changes of the view should animate alongside
|
|
372
|
+
/// any existing animation context.
|
|
373
|
+
/// - Defaults to `true` which will grab the current animation context and animate position and
|
|
374
|
+
/// transform changes matching the current context's curve and duration.
|
|
375
|
+
/// `false` will cause transform and position changes to happen unanimated
|
|
376
|
+
public var animateLayoutChangesWithCurrentCoreAnimationContext = true
|
|
377
|
+
|
|
371
378
|
/// The configuration that this `LottieAnimationView` uses when playing its animation
|
|
372
379
|
public var configuration: LottieConfiguration {
|
|
373
380
|
get { lottieAnimationLayer.configuration }
|
|
@@ -933,7 +940,11 @@ open class LottieAnimationView: LottieAnimationViewBase {
|
|
|
933
940
|
// If layout is changed without animation, explicitly set animation duration to 0.0
|
|
934
941
|
// inside CATransaction to avoid unwanted artifacts.
|
|
935
942
|
/// Check if any animation exist on the view's layer, and match it.
|
|
936
|
-
if
|
|
943
|
+
if
|
|
944
|
+
let key = lottieAnimationLayer.animationKeys()?.first,
|
|
945
|
+
let animation = lottieAnimationLayer.animation(forKey: key),
|
|
946
|
+
animateLayoutChangesWithCurrentCoreAnimationContext
|
|
947
|
+
{
|
|
937
948
|
// The layout is happening within an animation block. Grab the animation data.
|
|
938
949
|
|
|
939
950
|
let positionKey = "LayoutPositionAnimation"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Created by Bryn Bodayle on 1/20/22.
|
|
2
2
|
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
3
|
|
|
4
|
+
#if canImport(SwiftUI)
|
|
4
5
|
import SwiftUI
|
|
5
6
|
|
|
6
7
|
// MARK: - LottieView
|
|
@@ -480,3 +481,4 @@ public struct LottieView<Placeholder: View>: UIViewConfiguringSwiftUIView {
|
|
|
480
481
|
loadAnimationIfNecessary()
|
|
481
482
|
}
|
|
482
483
|
}
|
|
484
|
+
#endif
|
|
@@ -12,7 +12,7 @@ import Foundation
|
|
|
12
12
|
/// Once `cacheSize` is reached, animations can be ejected.
|
|
13
13
|
/// The default size of the cache is 100.
|
|
14
14
|
///
|
|
15
|
-
/// This cache implementation also responds to memory pressure
|
|
15
|
+
/// This cache implementation also responds to memory pressure.
|
|
16
16
|
public class DefaultAnimationCache: AnimationCacheProvider, @unchecked Sendable {
|
|
17
17
|
|
|
18
18
|
// MARK: Lifecycle
|
|
@@ -26,7 +26,7 @@ public class DefaultAnimationCache: AnimationCacheProvider, @unchecked Sendable
|
|
|
26
26
|
/// The global shared Cache.
|
|
27
27
|
public static let sharedCache = DefaultAnimationCache()
|
|
28
28
|
|
|
29
|
-
/// The
|
|
29
|
+
/// The maximum number of animations that can be stored in the cache.
|
|
30
30
|
public var cacheSize: Int {
|
|
31
31
|
get { cache.countLimit }
|
|
32
32
|
set { cache.countLimit = newValue }
|
|
@@ -34,20 +34,24 @@ public class DefaultAnimationCache: AnimationCacheProvider, @unchecked Sendable
|
|
|
34
34
|
|
|
35
35
|
/// Clears the Cache.
|
|
36
36
|
public func clearCache() {
|
|
37
|
-
cache.
|
|
37
|
+
cache.removeAllValues()
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
public func animation(forKey key: String) -> LottieAnimation? {
|
|
41
|
-
cache.
|
|
41
|
+
cache.value(forKey: key)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
public func setAnimation(_ animation: LottieAnimation, forKey key: String) {
|
|
45
|
-
cache.
|
|
45
|
+
cache.setValue(animation, forKey: key)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// MARK: Private
|
|
49
49
|
|
|
50
50
|
private static let defaultCacheCountLimit = 100
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
/// The underlying storage of this cache.
|
|
53
|
+
/// - We use the `LRUCache` library instead of `NSCache`, because `NSCache`
|
|
54
|
+
/// clears all cached values when the app is backgrounded instead of
|
|
55
|
+
/// only when the app receives a memory warning notification.
|
|
56
|
+
private let cache = LRUCache<String, LottieAnimation>()
|
|
53
57
|
}
|