3
3
// StateController.swift
4
4
// https://github.com/ekazaev/ChatLayout
5
5
//
6
- // Created by Eugene Kazaev in 2020-2021 .
6
+ // Created by Eugene Kazaev in 2020-2022 .
7
7
// Distributed under the MIT license.
8
8
//
9
9
@@ -99,17 +99,33 @@ final class StateController {
99
99
func layoutAttributesForElements( in rect: CGRect ,
100
100
state: ModelState ,
101
101
ignoreCache: Bool = false ) -> [ ChatLayoutAttributes ] {
102
+ let predicate : ( ChatLayoutAttributes ) -> ComparisonResult = { attributes in
103
+ if attributes. frame. intersects ( rect) {
104
+ return . orderedSame
105
+ }
106
+ if attributes. frame. minY > rect. maxY {
107
+ return . orderedDescending
108
+ }
109
+ return . orderedAscending
110
+ }
111
+
102
112
if !ignoreCache,
103
113
let cachedAttributesState = cachedAttributesState,
104
114
cachedAttributesState. rect. contains ( rect) {
105
- return cachedAttributesState. attributes. filter { $0 . frame . intersects ( rect ) }
115
+ return cachedAttributesState. attributes. binarySearchRange ( predicate : predicate )
106
116
} else {
107
- let totalRect = rect. inset ( by: UIEdgeInsets ( top: - rect. height / 2 , left: - rect. width / 2 , bottom: - rect. height / 2 , right: - rect. width / 2 ) )
117
+ let totalRect : CGRect
118
+ switch state {
119
+ case . beforeUpdate:
120
+ totalRect = rect. inset ( by: UIEdgeInsets ( top: - rect. height / 2 , left: - rect. width / 2 , bottom: - rect. height / 2 , right: - rect. width / 2 ) )
121
+ case . afterUpdate:
122
+ totalRect = rect
123
+ }
108
124
let attributes = allAttributes ( at: state, visibleRect: totalRect)
109
125
if !ignoreCache {
110
126
cachedAttributesState = ( rect: totalRect, attributes: attributes)
111
127
}
112
- let visibleAttributes = attributes . filter { $0 . frame . intersects ( rect ) }
128
+ let visibleAttributes = rect != totalRect ? attributes . binarySearchRange ( predicate : predicate ) : attributes
113
129
return visibleAttributes
114
130
}
115
131
}
@@ -650,10 +666,10 @@ final class StateController {
650
666
651
667
// Find if any of the items of the section is visible
652
668
if [ ComparisonResult . orderedSame, . orderedDescending] . contains ( predicate ( itemIndex: section. items. count - 1 ) ) ,
653
- let firstMatchIndex = Array ( 0 ... section. items. count - 1 ) . binarySearch ( predicate: predicate) {
669
+ let firstMatchingIndex = Array ( 0 ... section. items. count - 1 ) . binarySearch ( predicate: predicate) {
654
670
// Find first item that is visible
655
- startingIndex = firstMatchIndex
656
- for itemIndex in ( 0 ..< firstMatchIndex ) . reversed ( ) {
671
+ startingIndex = firstMatchingIndex
672
+ for itemIndex in ( 0 ..< firstMatchingIndex ) . reversed ( ) {
657
673
let itemPath = ItemPath ( item: itemIndex, section: sectionIndex)
658
674
guard let itemFrame = itemFrame ( for: itemPath, kind: . cell, at: state, isFinal: true ) else {
659
675
continue
@@ -725,6 +741,7 @@ final class StateController {
725
741
return self . itemAttributes ( for: path, kind: kind, predefinedFrame: frame, at: state)
726
742
}
727
743
} else {
744
+ // Debug purposes only.
728
745
var attributes = [ ChatLayoutAttributes] ( )
729
746
attributes. reserveCapacity ( layout. sections. count * 1000 )
730
747
layout. sections. enumerated ( ) . forEach { sectionIndex, section in
@@ -835,7 +852,7 @@ final class StateController {
835
852
836
853
}
837
854
838
- private extension RandomAccessCollection where Index == Int {
855
+ extension RandomAccessCollection where Index == Int {
839
856
840
857
func binarySearch( predicate: ( Element ) -> ComparisonResult ) -> Index ? {
841
858
var lowerBound = startIndex
@@ -854,4 +871,29 @@ private extension RandomAccessCollection where Index == Int {
854
871
return nil
855
872
}
856
873
874
+ func binarySearchRange( predicate: ( Element ) -> ComparisonResult ) -> [ Element ] {
875
+ guard let firstMatchingIndex = binarySearch ( predicate: predicate) else {
876
+ return [ ]
877
+ }
878
+
879
+ var startingIndex = firstMatchingIndex
880
+ for index in ( 0 ..< firstMatchingIndex) . reversed ( ) {
881
+ let attributes = self [ index]
882
+ guard predicate ( attributes) == . orderedSame else {
883
+ break
884
+ }
885
+ startingIndex = index
886
+ }
887
+
888
+ var lastIndex = firstMatchingIndex
889
+ for index in ( firstMatchingIndex + 1 ) ..< count {
890
+ let attributes = self [ index]
891
+ guard predicate ( attributes) == . orderedSame else {
892
+ break
893
+ }
894
+ lastIndex = index
895
+ }
896
+ return Array ( self [ startingIndex... lastIndex] )
897
+ }
898
+
857
899
}
0 commit comments