@@ -11,16 +11,16 @@ import Foundation
1111/// Namespace for some animation utilities methods
1212struct AnimationUtils {
1313 private init ( ) { }
14-
14+
1515 /// The intermediate steps of the 4 step animation
1616 enum AnimationStep {
1717 /// The first intermediate step
1818 case firstIntermediate
19-
19+
2020 /// The second intermediate step
2121 case secondIntermediate
2222 }
23-
23+
2424 /**
2525 Merges `descriptions` with `other` using a strategy that depends on the step.
2626
@@ -42,72 +42,21 @@ struct AnimationUtils {
4242 - parameter step: the step of the process
4343 - returns: an array of descriptions with elements merged following the algorithm described above
4444
45- */
45+ */
4646 static func mergedDescriptions(
4747 _ descriptions: [ AnyNodeDescription ] ,
4848 _ other: [ AnyNodeDescription ] ,
4949 step: AnimationStep ) -> [ AnyNodeDescription ] {
50-
51- let firstArray : [ AnyNodeDescription ]
52- let secondArray : [ AnyNodeDescription ]
53-
50+
5451 switch step {
5552 case . firstIntermediate:
56- firstArray = descriptions
57- secondArray = other
58-
53+ return self . mergedDescriptions ( descriptions, with: other)
54+
5955 case . secondIntermediate:
60- firstArray = other
61- secondArray = descriptions
62- }
63-
64- // lookup for first array
65- var firstArrayLookup = [ Int: Int] ( )
66- for (index, item) in firstArray. enumerated ( ) {
67- firstArrayLookup [ item. replaceKey] = index
68- }
69-
70- // lookup for second array
71- var secondArrayLookup = [ Int: Int] ( )
72- for (index, item) in secondArray. enumerated ( ) {
73- secondArrayLookup [ item. replaceKey] = index
74- }
75-
76- var result = firstArray
77- var added = 0
78- var firstArrayMaxPosition = - 1
79-
80- for item in secondArray {
81- let position = firstArrayLookup [ item. replaceKey]
82-
83- if let position = position {
84- // we already have the item
85- firstArrayMaxPosition = max ( firstArrayMaxPosition, position)
86- continue
87- }
88-
89- result. insert ( item, at: added + firstArrayMaxPosition + 1 )
90- added = added + 1
56+ return self . mergedDescriptions ( other, with: descriptions)
9157 }
92-
93- // merge also children, if needed
94- result = result. map { description in
95- guard var propsWithChildren = description. anyProps as? Childrenable else {
96- return description
97- }
98-
99- let secondItemChildren = secondArrayLookup [ description. replaceKey]
100- . flatMap ( { secondArray [ $0] } )
101- . flatMap ( { $0 as? AnyNodeDescriptionWithChildren } )
102- . flatMap ( { $0. children } )
103-
104- propsWithChildren. children = mergedDescriptions ( propsWithChildren. children, secondItemChildren ?? [ ] , step: step)
105- return type ( of: description) . init ( anyProps: propsWithChildren as! AnyNodeDescriptionProps )
106- }
107-
108- return result
10958 }
110-
59+
11160 /**
11261 Updates the descriptions using an instance of `ChildrenAnimations`.
11362
@@ -124,63 +73,124 @@ struct AnimationUtils {
12473 - parameter targetChildren: the target children used to define when apply a transformation
12574 - parameter step: the step of the process
12675 - returns: an array of updated descriptions
127- */
76+ */
12877 static func updatedDescriptions(
12978 for descriptions: [ AnyNodeDescription ] ,
13079 using childrenAnimation: AnyChildrenAnimations ,
13180 targetChildren: [ AnyNodeDescription ] ,
13281 step: AnimationStep ) -> [ AnyNodeDescription ] {
133-
82+
13483 return descriptions. map { ( item: AnyNodeDescription ) -> AnyNodeDescription in
13584 let itemReplaceKey = item. replaceKey
13685 let index = targetChildren. index { $0. replaceKey == itemReplaceKey }
13786 var item = item
138-
87+
13988 if index == nil {
14089 // the item is missing in the comparison, update it
14190 item = self . updatedDescription ( for: item, using: childrenAnimation, step: step)
14291 }
143-
92+
14493 if var propsWithChildren = item. anyProps as? Childrenable {
14594 // the item has children, let's manage also the children
14695 let children = propsWithChildren. children
14796 let target = ( item as? AnyNodeDescriptionWithChildren ) . flatMap ( { $0. children } )
148-
97+
14998 propsWithChildren. children = updatedDescriptions (
15099 for: children,
151100 using: childrenAnimation,
152101 targetChildren: target ?? [ ] ,
153102 step: step
154103 )
155-
104+
156105 return type ( of: item) . init ( anyProps: propsWithChildren as! AnyNodeDescriptionProps )
157106 }
158-
107+
159108 // nothing to do
160109 return item
161110 }
162111 }
163-
112+
164113 /**
165114 Updates the description using an instance of `ChildrenAnimations`.
166115
167116 - parameter description: the original description
168117 - parameter childrenAnimation: the animations to use
169118 - parameter step: the step of the process
170119 - returns: the updated description
171- */
120+ */
172121 private static func updatedDescription(
173122 for description: AnyNodeDescription ,
174123 using childrenAnimation: AnyChildrenAnimations ,
175124 step: AnimationStep ) -> AnyNodeDescription {
176-
125+
177126 let animation = childrenAnimation [ description]
178127 let transformers = step == . firstIntermediate ? animation. entryTransformers : animation. leaveTransformers
179-
128+
180129 let newProps = transformers. reduce ( description. anyProps, { ( props, transformer) -> AnyNodeDescriptionProps in
181130 return transformer ( props)
182131 } )
183-
132+
184133 return type ( of: description) . init ( anyProps: newProps)
185134 }
135+
136+ /**
137+ Merge two `AnyNodeDescription` arrays. The first array has the priority
138+ if two elements are the same. The method will propagate the merge to also
139+ the children of the node descriptions, if the element implements
140+ the `Childrenable` protocol
141+
142+ - parameter firstArray: the first array
143+ - parameter secondArray: the second array
144+ - returns: the merged array
145+ */
146+ private static func mergedDescriptions(
147+ _ firstArray: [ AnyNodeDescription ] ,
148+ with secondArray: [ AnyNodeDescription ] ) -> [ AnyNodeDescription ] {
149+
150+ // lookup for first array
151+ var firstArrayLookup = [ Int: Int] ( )
152+ for (index, item) in firstArray. enumerated ( ) {
153+ firstArrayLookup [ item. replaceKey] = index
154+ }
155+
156+ // lookup for second array
157+ var secondArrayLookup = [ Int: Int] ( )
158+ for (index, item) in secondArray. enumerated ( ) {
159+ secondArrayLookup [ item. replaceKey] = index
160+ }
161+
162+ var result = firstArray
163+ var added = 0
164+ var firstArrayMaxPosition = - 1
165+
166+ for item in secondArray {
167+ let position = firstArrayLookup [ item. replaceKey]
168+
169+ if let position = position {
170+ // we already have the item
171+ firstArrayMaxPosition = max ( firstArrayMaxPosition, position)
172+ continue
173+ }
174+
175+ result. insert ( item, at: added + firstArrayMaxPosition + 1 )
176+ added = added + 1
177+ }
178+
179+ // merge also children, if needed
180+ result = result. map { description in
181+ guard var propsWithChildren = description. anyProps as? Childrenable else {
182+ return description
183+ }
184+
185+ let secondItemChildren = secondArrayLookup [ description. replaceKey]
186+ . flatMap ( { secondArray [ $0] } )
187+ . flatMap ( { $0 as? AnyNodeDescriptionWithChildren } )
188+ . flatMap ( { $0. children } )
189+
190+ propsWithChildren. children = self . mergedDescriptions ( propsWithChildren. children, with: secondItemChildren ?? [ ] )
191+ return type ( of: description) . init ( anyProps: propsWithChildren as! AnyNodeDescriptionProps )
192+ }
193+
194+ return result
195+ }
186196}
0 commit comments