1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//
//  LBKeyWordLayout.m
//  IphoneBIMe
//
//  Created by ZhangJF on 2023/3/9.
//  Copyright © 2023 ProBIM. All rights reserved.
//
#import "LBKeyWordLayout.h"
 
@interface LBKeyWordLayout()
@property (strong, nonatomic) NSCache *cache;
@end
 
@implementation LBKeyWordLayout
 
- (void)prepareLayout
{
    [super prepareLayout];
    self.cache = [NSCache new];
}
 
- (void)invalidateLayout
{
    [super invalidateLayout];
    self.cache = [NSCache new];
}
 
- (BOOL)shouldInvalidateLayoutForPreferredLayoutAttributes:(UICollectionViewLayoutAttributes *)preferredAttributes withOriginalAttributes:(UICollectionViewLayoutAttributes *)originalAttributes
{
    return YES;
}
 
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}
 
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray<UICollectionViewLayoutAttributes *> *attributes = [super layoutAttributesForElementsInRect:rect].copy;
    return [self layoutAttributesForElements:attributes];
}
 
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self attributesAtIndexPath:indexPath];
}
 
#pragma mark - Private
 
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElements:(NSArray<UICollectionViewLayoutAttributes *> *)attributes
{
    NSMutableArray<UICollectionViewLayoutAttributes *> *alignedAttributes = [NSMutableArray new];
    
    for (UICollectionViewLayoutAttributes *item in attributes) {
        if(item.representedElementKind != nil) {
            [alignedAttributes addObject:item];
        } else {
            [alignedAttributes addObject:[self layoutAttributesForItem:item atIndexPath:item.indexPath]];
        }
    }
    
    return alignedAttributes.copy;
}
 
- (UICollectionViewLayoutAttributes *)layoutAttributesForItem:(UICollectionViewLayoutAttributes *)attributes atIndexPath:(NSIndexPath *)indexPath
{
    return [self attributes:attributes atIndexPath:indexPath];
}
 
- (UICollectionViewLayoutAttributes *)attributesAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForItemAtIndexPath:indexPath].copy;
    return [self attributes:attributes atIndexPath:indexPath];
}
 
- (UICollectionViewLayoutAttributes *)attributes:(UICollectionViewLayoutAttributes *)attributes atIndexPath:(NSIndexPath *)indexPath
{
    if ([self.cache objectForKey:indexPath]) {
        return [self.cache objectForKey:indexPath];
    }
    
    NSMutableArray *itemsInRow = [NSMutableArray array];
    
    const NSInteger totalInSection = [self.collectionView numberOfItemsInSection:indexPath.section];
    const CGFloat width = CGRectGetWidth(self.collectionView.bounds);
    const CGRect rowFrame = CGRectMake(0, CGRectGetMinY(attributes.frame), width, CGRectGetHeight(attributes.frame));
    
    // Go forward to the end or the row or section items
    NSInteger index = indexPath.row;
    while(index++ < totalInSection - 1) {
        
        UICollectionViewLayoutAttributes *next = [super layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index
                                                                                                               inSection:indexPath.section]].copy;
        
        if (!CGRectIntersectsRect(next.frame, rowFrame)) {
            break;
        }
        [itemsInRow addObject:next];
    }
    
    // Current item
    [itemsInRow addObject:attributes];
    
    // Go backward to the start of the row or first item
    index = indexPath.row;
    while (index-- > 0) {
        
        UICollectionViewLayoutAttributes *prev = [super layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index
                                                                                                               inSection:indexPath.section]].copy;
        
        if (!CGRectIntersectsRect(prev.frame, rowFrame)) {
            break;
        }
        [itemsInRow addObject:prev];
    }
    
    // Total items width include spacings
    CGFloat totalWidth = self.minimumInteritemSpacing * (itemsInRow.count - 1);
    for (UICollectionViewLayoutAttributes *item in itemsInRow) {
        totalWidth += CGRectGetWidth(item.frame);
    }
    
    // Correct sorting in row
    [itemsInRow sortUsingComparator:^NSComparisonResult(UICollectionViewLayoutAttributes *obj1, UICollectionViewLayoutAttributes *obj2) {
        return obj1.indexPath.row > obj2.indexPath.row;
    }];
    
    CGRect rect = CGRectZero;
    for (UICollectionViewLayoutAttributes *item in itemsInRow) {
        
        CGRect frame = item.frame;
        CGFloat x = frame.origin.x;
        
        if (CGRectIsEmpty(rect)) {
           x = self.sectionInset.left;
        } else {
           x = CGRectGetMaxX(rect) + self.minimumInteritemSpacing;
        }
        
        frame.origin.x = x;
        item.frame = frame;
        rect = frame;
        
        [self.cache setObject:item forKey:item.indexPath];
    }
    
    [self.cache setObject:attributes forKey:indexPath];
    return attributes;
}
@end