@@ -36,27 +36,8 @@ final class ShapeLayer: BaseCompositionLayer {
3636 private let shapeLayer : ShapeLayerModel
3737
3838 private func setUpGroups( context: LayerContext ) throws {
39- // If the layer has a `Repeater`, the `Group`s are duplicated and offset
40- // based on the copy count of the repeater.
41- if let repeater = shapeLayer. items. first ( where: { $0 is Repeater } ) as? Repeater {
42- try setUpRepeater ( repeater, context: context)
43- } else {
44- let shapeItems = shapeLayer. items. map { ShapeItemLayer . Item ( item: $0, groupPath: [ ] ) }
45- try setupGroups ( from: shapeItems, parentGroup: nil , parentGroupPath: [ ] , context: context)
46- }
47- }
48-
49- private func setUpRepeater( _ repeater: Repeater , context: LayerContext ) throws {
50- let items = shapeLayer. items. filter { !( $0 is Repeater ) }
51- let copyCount = Int ( try repeater. copies. exactlyOneKeyframe ( context: context, description: " repeater copies " ) . value)
52-
53- for index in 0 ..< copyCount {
54- let shapeItems = items. map { ShapeItemLayer . Item ( item: $0, groupPath: [ ] ) }
55- for groupLayer in try makeGroupLayers ( from: shapeItems, parentGroup: nil , parentGroupPath: [ ] , context: context) {
56- let repeatedLayer = RepeaterLayer ( repeater: repeater, childLayer: groupLayer, index: index)
57- addSublayer ( repeatedLayer)
58- }
59- }
39+ let shapeItems = shapeLayer. items. map { ShapeItemLayer . Item ( item: $0, groupPath: [ ] ) }
40+ try setupGroups ( from: shapeItems, parentGroup: nil , parentGroupPath: [ ] , context: context)
6041 }
6142
6243}
@@ -181,6 +162,9 @@ final class GroupLayer: BaseAnimationLayer {
181162}
182163
183164extension CALayer {
165+
166+ // MARK: Fileprivate
167+
184168 /// Sets up `GroupLayer`s for each `Group` in the given list of `ShapeItem`s
185169 /// - Each `Group` item becomes its own `GroupLayer` sublayer.
186170 /// - Other `ShapeItem` are applied to all sublayers
@@ -191,21 +175,79 @@ extension CALayer {
191175 context: LayerContext )
192176 throws
193177 {
194- let groupLayers = try makeGroupLayers (
195- from: items,
196- parentGroup: parentGroup,
197- parentGroupPath: parentGroupPath,
198- context: context)
199-
200- for groupLayer in groupLayers {
201- addSublayer ( groupLayer)
178+ // If the layer has any `Repeater`s, set up each repeater
179+ // and then handle any remaining groups like normal.
180+ if items. contains ( where: { $0. item is Repeater } ) {
181+ let repeaterGroupings = items. split ( whereSeparator: { $0. item is Repeater } )
182+
183+ // Iterate through the groupings backwards to preserve the expected rendering order
184+ for repeaterGrouping in repeaterGroupings. reversed ( ) {
185+ // Each repeater applies to the previous items in the list
186+ if let repeater = repeaterGrouping. trailingSeparator? . item as? Repeater {
187+ try setUpRepeater (
188+ repeater,
189+ items: repeaterGrouping. grouping,
190+ parentGroup: parentGroup,
191+ parentGroupPath: parentGroupPath,
192+ context: context)
193+ }
194+
195+ // Any remaining items after the last repeater are handled like normal
196+ else {
197+ try setupGroups (
198+ from: repeaterGrouping. grouping,
199+ parentGroup: parentGroup,
200+ parentGroupPath: parentGroupPath,
201+ context: context)
202+ }
203+ }
204+ }
205+
206+ else {
207+ let groupLayers = try makeGroupLayers (
208+ from: items,
209+ parentGroup: parentGroup,
210+ parentGroupPath: parentGroupPath,
211+ context: context)
212+
213+ for groupLayer in groupLayers {
214+ addSublayer ( groupLayer)
215+ }
216+ }
217+ }
218+
219+ // MARK: Private
220+
221+ /// Sets up this layer using the given `Repeater`
222+ private func setUpRepeater(
223+ _ repeater: Repeater ,
224+ items allItems: [ ShapeItemLayer . Item ] ,
225+ parentGroup: Group ? ,
226+ parentGroupPath: [ String ] ,
227+ context: LayerContext )
228+ throws
229+ {
230+ let items = allItems. filter { !( $0. item is Repeater ) }
231+ let copyCount = Int ( try repeater. copies. exactlyOneKeyframe ( context: context, description: " repeater copies " ) . value)
232+
233+ for index in 0 ..< copyCount {
234+ let groupLayers = try makeGroupLayers (
235+ from: items,
236+ parentGroup: parentGroup,
237+ parentGroupPath: parentGroupPath,
238+ context: context)
239+
240+ for groupLayer in groupLayers {
241+ let repeatedLayer = RepeaterLayer ( repeater: repeater, childLayer: groupLayer, index: index)
242+ addSublayer ( repeatedLayer)
243+ }
202244 }
203245 }
204246
205247 /// Creates a `GroupLayer` for each `Group` in the given list of `ShapeItem`s
206248 /// - Each `Group` item becomes its own `GroupLayer` sublayer.
207249 /// - Other `ShapeItem` are applied to all sublayers
208- fileprivate func makeGroupLayers(
250+ private func makeGroupLayers(
209251 from items: [ ShapeItemLayer . Item ] ,
210252 parentGroup: Group ? ,
211253 parentGroupPath: [ String ] ,
@@ -346,6 +388,32 @@ extension Collection {
346388
347389 return ( trueElements, falseElements)
348390 }
391+
392+ /// Splits this collection into an array of grouping separated by the given separator.
393+ /// For example, `[A, B, C]` split by `B` returns an array with two elements:
394+ /// 1. `(grouping: [A], trailingSeparator: B)`
395+ /// 2. `(grouping: [C], trailingSeparator: nil)`
396+ func split( whereSeparator separatorPredicate: ( Element ) -> Bool )
397+ -> [ ( grouping: [ Element ] , trailingSeparator: Element ? ) ]
398+ {
399+ guard !isEmpty else { return [ ] }
400+
401+ var groupings : [ ( grouping: [ Element ] , trailingSeparator: Element ? ) ] = [ ]
402+
403+ for element in self {
404+ if groupings. isEmpty || groupings. last? . trailingSeparator != nil {
405+ groupings. append ( ( grouping: [ ] , trailingSeparator: nil ) )
406+ }
407+
408+ if separatorPredicate ( element) {
409+ groupings [ groupings. indices. last!] . trailingSeparator = element
410+ } else {
411+ groupings [ groupings. indices. last!] . grouping. append ( element)
412+ }
413+ }
414+
415+ return groupings
416+ }
349417}
350418
351419// MARK: - ShapeRenderGroup
0 commit comments