@@ -860,6 +860,8 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate {
860860 return createRow ( newRow, at: index, cellToRemove: cellToRemove, animated: animated, completion: completion)
861861 }
862862
863+ private var rowVisibilityChangesDispatchWorkItem : DispatchWorkItem ?
864+
863865 /// Private implementation to add new row.
864866 private func createRow( _ newRow: ScrollStackRow , at index: Int ,
865867 cellToRemove: ScrollStackRow ? ,
@@ -878,7 +880,21 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate {
878880 } , completion: nil )
879881 }
880882
881- scrollViewDidScroll ( self )
883+ if rowVisibilityChangesDispatchWorkItem == nil {
884+
885+ rowVisibilityChangesDispatchWorkItem = DispatchWorkItem ( block: { [ weak self] in
886+ if let stackDelegate = self ? . stackDelegate {
887+ self ? . dispatchRowsVisibilityChangesTo ( stackDelegate)
888+ }
889+
890+ self ? . rowVisibilityChangesDispatchWorkItem = nil
891+ } )
892+
893+ /// Schedule a single `dispatchRowsVisibilityChangesTo(_:)` call.
894+ ///
895+ /// In this way, when rows are created inside a for-loop, the delegate is called only once after the `ScrollStack` has been fully laid out.
896+ DispatchQueue . main. async ( execute: rowVisibilityChangesDispatchWorkItem!)
897+ }
882898
883899 return newRow
884900 }
@@ -979,25 +995,27 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate {
979995 }
980996
981997 private func dispatchRowsVisibilityChangesTo( _ delegate: ScrollStackControllerDelegate ) {
982- delegate. scrollStackDidScroll ( self , offset: contentOffset)
983-
984998 rows. enumerated ( ) . forEach { ( idx, row) in
985999 let current = isRowVisible ( index: idx)
986- if let previous = prevVisibilityState [ row] {
987- switch ( previous, current) {
988- case ( . offscreen, . partial) , // row will become invisible
989- ( . hidden, . partial) ,
990- ( . hidden, . entire) :
991- delegate. scrollStackRowDidBecomeVisible ( self , row: row, index: idx, state: current)
992-
993- case ( . partial, . offscreen) , // row will become visible
994- ( . partial, . hidden) ,
995- ( . entire, . hidden) :
996- delegate. scrollStackRowDidBecomeHidden ( self , row: row, index: idx, state: current)
997-
998- default :
999- break
1000- }
1000+ let previous = prevVisibilityState [ row]
1001+
1002+ switch ( previous, current) {
1003+ case ( . offscreen, . partial) , // row will become visible
1004+ ( nil , . entire) ,
1005+ ( nil , . partial) ,
1006+ ( . partial, . entire) ,
1007+ ( . hidden, . partial) ,
1008+ ( . hidden, . entire) :
1009+ delegate. scrollStackRowDidBecomeVisible ( self , row: row, index: idx, state: current)
1010+
1011+ case ( . partial, . offscreen) , // row will become invisible
1012+ ( . entire, . partial) ,
1013+ ( . partial, . hidden) ,
1014+ ( . entire, . hidden) :
1015+ delegate. scrollStackRowDidBecomeHidden ( self , row: row, index: idx, state: current)
1016+
1017+ default :
1018+ break
10011019 }
10021020
10031021 // store previous state
@@ -1062,7 +1080,8 @@ open class ScrollStack: UIScrollView, UIScrollViewDelegate {
10621080 guard let stackDelegate = stackDelegate else {
10631081 return
10641082 }
1065-
1083+ stackDelegate. scrollStackDidScroll ( self , offset: contentOffset)
1084+
10661085 dispatchRowsVisibilityChangesTo ( stackDelegate)
10671086 }
10681087
0 commit comments