当前位置:   article > 正文

封装了一个iOS水平方向瀑布流布局

封装了一个iOS水平方向瀑布流布局

首先查看效果图
请添加图片描述
是支持多分区的

思路就是和竖直方向上的瀑布流布局是一样的,
只不过这里记录的列是水平方向的,同时要记录下
当前最小的x 所在的列,其实原理和竖直方向上的是相同的
,下面贴出代码

父类layout中的代码

//
//  LBCollectionViewBaseLayout.m
//  TEXT
//
//  Created by mac on 2024/5/12.
//  Copyright © 2024 刘博. All rights reserved.
//

#import "LBCollectionViewBaseLayout.h"
#import "LBCellFakeView.h"
#import "LBCollectionViewLayoutAttributes.h"

typedef NS_ENUM(NSUInteger, LBScrollDirection) {
    LBScrollDirectionStay, //不滚动
    LBScrollDirectionTop, //滚动到顶部
    LBScrollDirectionBottom, //滚动到底部
};

@interface LBCollectionViewBaseLayout () <UIGestureRecognizerDelegate>

//关于拖动的参数
@property (nonatomic, strong) LBCellFakeView *cellFakeView;
@property (nonatomic, strong) UILongPressGestureRecognizer *longPress;
@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;
@property (nonatomic, assign) CGPoint fakeCellCenter;
@property (nonatomic, assign) CGPoint panTranslation;
@property (nonatomic, assign) LBScrollDirection continousScrollDirection;
@property (nonatomic, strong) CADisplayLink *displayLink;

@end

@implementation LBCollectionViewBaseLayout

{
    BOOL _isNeedReCalculateAllLayout;
}

- (instancetype)init
{
    if (self = [super init]) {
        self.isFloor = YES;
        self.canDrag = NO;
        self.header_suppension = NO;
        self.layoutType = LBLayoutTypeTypeFillLayout;
        self.columnCount = 1;
        self.columnSortType = LBColumnSortTypeMinHeight;
        self.fixTop = 0;
        self.xBeyong = 3;
        _isNeedReCalculateAllLayout = YES;
        _headerAttributesArray = [NSMutableArray array];
        [self addObserver:self forKeyPath:@"collectionView" options:NSKeyValueObservingOptionNew context:nil];
    }
    return self;
}

#pragma mark -  当尺寸有所变化时, 重新刷新

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return self.header_suppension;
}

- (void)invalidateLayoutWithContext:(UICollectionViewLayoutInvalidationContext *)context
{
    /***
     外部掉用reloadData 或者变更任意数据时则认为需要进行全量布局的刷新
     好处时候在外部变量变更数据时内部布局会及时刷新
     劣势是在你上拉加载某一页时,布局会全部整体重新计算一遍,并非只计算新增的布局
     */
    _isNeedReCalculateAllLayout = context.invalidateEverything ||
    context.invalidateDataSourceCounts;
    [super invalidateLayoutWithContext:context];
}

//注册所有的背景view(传入类名)
- (void)registerDecorationView:(NSArray<NSString *> *)classNames
{
    for (NSString *className in classNames) {
        if (className.length > 0) {
            [self registerClass:NSClassFromString(className) forDecorationViewOfKind:className];
        }
    }
}

- (void)dealloc
{
    [self removeObserver:self forKeyPath:@"collectionView"];
}

#pragma mark - 所有cell和view 的布局属性
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
    if (!self.attributesArray ||
        self.collectionView.numberOfSections == 0) {
        return [super layoutAttributesForElementsInRect:rect];
    }
    if (self.header_suppension) {
        //只有在headerAttributesArray 里面查找需要悬浮的属性
        for (UICollectionViewLayoutAttributes *attribute in self.headerAttributesArray) {
            if (![attribute.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
                continue;
            }
            NSInteger section = attribute.indexPath.section;
            CGRect frame = attribute.frame;
            BOOL isNeedChangeFrame = NO;
            if (section == 0) {
                if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
                    CGFloat offsetY = self.collectionView.contentOffset.y + self.fixTop;
                    if (offsetY > 0 && offsetY < [self.collectionHeightsArray[0] floatValue]) {
                        frame.origin.y = offsetY;
                        attribute.zIndex = 1000 + section;
                        attribute.frame = frame;
                        isNeedChangeFrame = YES;
                    }
                } else {
                    CGFloat offsetX = self.collectionView.contentOffset.x + self.fixTop;
                    if (offsetX > 0 && offsetX < [self.collectionHeightsArray[0] floatValue]) {
                        frame.origin.x = offsetX;
                        attribute.zIndex = 1000 + section;
                        attribute.frame = frame;
                        isNeedChangeFrame = YES;
                    }
                }
            } else {
                if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
                    CGFloat offsetY = self.collectionView.contentOffset.y + self.fixTop;
                    if (offsetY > [self.collectionHeightsArray[section - 1] floatValue] &&
                        offsetY < [self.collectionHeightsArray[section] floatValue]) {
                        frame.origin.y = offsetY;
                        attribute.zIndex = 1000 + section;
                        attribute.frame = frame;
                        isNeedChangeFrame = YES;
                    }
                } else {
                    CGFloat offsetX = self.collectionView.contentOffset.x + self.fixTop;
                    if (offsetX > [self.collectionHeightsArray[section - 1] floatValue] &&
                        offsetX < [self.collectionHeightsArray[section] floatValue]) {
                        frame.origin.x = offsetX;
                        attribute.zIndex = 1000 + section;
                        attribute.frame = frame;
                        isNeedChangeFrame = YES;
                    }
                }
            }
            if (isNeedChangeFrame) {
                /**
                 这里需要注意,在悬浮情况下,改变了headeatt的frame,
                 在滑出header又滑回来时,headeAttr已经被修改过,需要改回原始值,
                 否则header无法正确回归
                 */
                if ([attribute isKindOfClass:[LBCollectionViewLayoutAttributes class]]) {
                    attribute.frame = ((LBCollectionViewLayoutAttributes *)attribute).originalFrame;
                }
            }
        }
    }
    return self.attributesArray;
}

#pragma mark - 以下是拖动排序代码

- (void)setCanDrag:(BOOL)canDrag
{
    _canDrag = canDrag;
    if (canDrag) {
        if (self.longPress == nil && self.panGesture == nil) {
            [self setUpGestureRecognizers];
        }
    } else {
        [self.collectionView removeGestureRecognizer:self.longPress];
        self.longPress.delegate = nil;
        self.longPress = nil;
        [self.collectionView removeGestureRecognizer:self.panGesture];
        self.panGesture.delegate = self;
        self.panGesture = nil;
    }
}

#pragma mark - observe

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"collectionView"]) {
        if (self.canDrag) {
            [self setUpGestureRecognizers];
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

- (void)setUpGestureRecognizers
{
    if (self.collectionView == nil) {
        return;
    }
    self.longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
    self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    self.longPress.delegate = self;
    self.panGesture.delegate = self;
    self.panGesture.maximumNumberOfTouches = 1;
    NSArray *gestures = [self.collectionView gestureRecognizers];
    __weak typeof(LBCollectionViewBaseLayout *) weakSelf = self;
    [gestures enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[UILongPressGestureRecognizer class]]) {
            [(UILongPressGestureRecognizer *)obj requireGestureRecognizerToFail:weakSelf.longPress];
        }
    }];
    [self.collectionView addGestureRecognizer:self.longPress];
    [self.collectionView addGestureRecognizer:self.panGesture];
}

#pragma mark - gesture

- (void)handleLongPress:(UILongPressGestureRecognizer *)longPress
{
    CGPoint location = [longPress locationInView:self.collectionView];
    NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location];
    if (_cellFakeView != nil) {
        indexPath = self.cellFakeView.indexPath;
    }
    
    if (indexPath != nil) {
        return;
    }
    
    switch (longPress.state) {
        case UIGestureRecognizerStateBegan:
        {
            self.collectionView.scrollsToTop = NO;
            UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
            
            self.cellFakeView = [[LBCellFakeView alloc] initWithCell:cell];
            self.cellFakeView.indexPath = indexPath;
            self.cellFakeView.originCenter = cell.center;
            self.cellFakeView.cellFrame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
            
            [self.collectionView addSubview:self.cellFakeView];
            self.fakeCellCenter = self.cellFakeView.center;
            [self invalidateLayout];
            [self.cellFakeView pushForwardView];
        }
            break;
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateEnded:
        {
            [self cancelDrag:indexPath];
        }
            
        default:
            break;
    }
}

//pan gesture

- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
    _panTranslation = [pan translationInView:self.collectionView];
    if (_cellFakeView != nil) {
        switch (pan.state) {
            case UIGestureRecognizerStateChanged:
                {
                    CGPoint center = _cellFakeView.center;
                    center.x = self.fakeCellCenter.x + self.panTranslation.x;
                    center.y = self.fakeCellCenter.y + self.panTranslation.y;
                    self.cellFakeView.center = center;
                    [self beginScrollIfNeeded];
                    [self moveItemIfNeeded];
                }
                break;
            case UIGestureRecognizerStateCancelled:
            case UIGestureRecognizerStateEnded:
            {
                [self invalidateDisplayLink];
            }
                break;
            default:
                break;
        }
    }
}

#pragma mark - gesturedelegate

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    CGPoint location = [gestureRecognizer locationInView:self.collectionView];
    NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location];
    if (!indexPath) {
        return NO;
    }
    
    if ([gestureRecognizer isEqual:self.longPress]) {
        return (self.collectionView.panGestureRecognizer.state == UIGestureRecognizerStatePossible ||
                self.collectionView.panGestureRecognizer.state == UIGestureRecognizerStateFailed);
    } else if ([gestureRecognizer isEqual:self.panGesture]) {
        return (self.longPress.state != UIGestureRecognizerStatePossible &&
                self.longPress.state != UIGestureRecognizerStateFailed);
    }
    return YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([self.panGesture isEqual:gestureRecognizer]) {
        return [self.longPress isEqual:otherGestureRecognizer];
    } else if ([self.collectionView.panGestureRecognizer isEqual:gestureRecognizer]) {
        return (self.longPress.state != UIGestureRecognizerStatePossible &&
                self.longPress.state != UIGestureRecognizerStateFailed);
    }
    return YES;
}

- (void)cancelDrag:(NSIndexPath *)indexPath
{
    if (self.cellFakeView == nil) {
        return;
    }
    
    self.collectionView.scrollsToTop = YES;
    self.fakeCellCenter = CGPointZero;
    [self invalidateDisplayLink];
    __weak typeof (LBCollectionViewBaseLayout *) weakSelf = self;
    [self.cellFakeView pushBackView:^{
        [weakSelf.cellFakeView removeFromSuperview];
        weakSelf.cellFakeView = nil;
        [weakSelf invalidateLayout];
    }];
}

- (void)moveItemIfNeeded
{
    NSIndexPath *atIndexPath = nil;
    NSIndexPath *toIndexPath = nil;
    __weak typeof (LBCollectionViewBaseLayout *) weakSelf = self;
    if (self.cellFakeView) {
        atIndexPath = self.cellFakeView.indexPath;
        toIndexPath = [self.collectionView indexPathForItemAtPoint:self.cellFakeView.center];
    }
    if (atIndexPath.section != toIndexPath.section) {
        return;
    }
    if (atIndexPath == nil || toIndexPath == nil) {
        return;
    }
    
    if ([atIndexPath isEqual:toIndexPath]) {
        return;
    }
    
    UICollectionViewLayoutAttributes *attribute = nil;
    
    for (LBCollectionViewLayoutAttributes *attr in weakSelf.attributesArray) {
        if (attr.indexPath.section == toIndexPath.section && attr.indexPath.item == toIndexPath.item
            && ![attr.representedElementKind isEqualToString: UICollectionElementKindSectionHeader]
            && ![attr.representedElementKind isEqualToString: UICollectionElementKindSectionFooter]) {
            attribute = attr;
            break;;
        }
    }
    
    if (attribute != nil) {
        [self.collectionView performBatchUpdates:^{
            weakSelf.cellFakeView.indexPath = toIndexPath;
            weakSelf.cellFakeView.cellFrame = attribute.frame;
            [weakSelf.cellFakeView changeBoundsIfNeeded:attribute.bounds];
            [weakSelf.collectionView moveItemAtIndexPath:atIndexPath toIndexPath:toIndexPath];
            if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]) {
                [weakSelf.delegate collectionview:weakSelf.collectionView layout:weakSelf didMoveCell:atIndexPath toIndexPath:toIndexPath];
            }
        } completion:nil];
    }
}

- (void)setUpDisPlayLink
{
    if (_displayLink) {
        return;
    }
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(continuousScroll)];
    [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)invalidateDisplayLink
{
    _continousScrollDirection = LBScrollDirectionStay;
    [_displayLink invalidate];
    _displayLink = nil;
}

- (void)continuousScroll
{
    if(_cellFakeView == nil) {
        return;
    }
    CGFloat percentage = [self calcTriggerPercentage];
    CGFloat scrollRate = [self scrollValueWithSpeed:10 andPercentage:percentage];
    
    CGFloat offset = 0;
    CGFloat insetTop = 0;
    CGFloat insetEnd = 0;
    CGFloat length = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
    
    CGFloat contentLength = self.scrollDirection == UICollectionViewScrollDirectionVertical ?
    self.collectionView.contentSize.height : self.collectionView.contentSize.width;
    
    if (contentLength + insetTop + insetEnd <= length) {
        return;
    }
    
    if (offset + scrollRate <=  - insetTop) {
        scrollRate = - insetEnd - offset;
    } else if (offset + scrollRate >= contentLength + insetEnd - length) {
        scrollRate = contentLength + insetEnd - length - offset;
    }
    
    __weak typeof (LBCollectionViewBaseLayout *) weakSelf = self;
    [self.collectionView performBatchUpdates:^{
        if (weakSelf.scrollDirection == UICollectionViewScrollDirectionVertical) {
            CGPoint point = weakSelf.fakeCellCenter;
            point.y += scrollRate;
            weakSelf.fakeCellCenter = point;
            CGPoint center = weakSelf.cellFakeView.center;
            center.y = weakSelf.fakeCellCenter.y + weakSelf.panTranslation.y;
            weakSelf.cellFakeView.center = center;
            CGPoint contentOffset = weakSelf.collectionView.contentOffset;
            contentOffset.y += scrollRate;
            weakSelf.collectionView.contentOffset = contentOffset;
        } else {
            CGPoint point = weakSelf.fakeCellCenter;
            point.x += scrollRate;
            weakSelf.fakeCellCenter = point;
            CGPoint center = weakSelf.cellFakeView.center;
            center.x = weakSelf.fakeCellCenter.x + weakSelf.panTranslation.x;
            weakSelf.cellFakeView.center = center;
            CGPoint contentOffset = weakSelf.collectionView.contentOffset;
            contentOffset.x += scrollRate;
            weakSelf.collectionView.contentOffset = contentOffset;
        }
    } completion:^(BOOL finished) {
        
    }];

}

- (CGFloat)calcTriggerPercentage
{
    if (_cellFakeView == nil) {
        return 0;
    }
    
    CGFloat offset = 0;
    CGFloat offsetEnd = 0 + self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
    CGFloat insetTop = 0;
    CGFloat triggerInsetTop = 0;
    CGFloat triggerInsetEnd = 0;
    CGFloat paddingTop = 0;
    CGFloat paddingEnd = 0;
    CGFloat percentage = 0;
    if (self.continousScrollDirection == LBScrollDirectionTop) {
        if (self.cellFakeView) {
            percentage = 1 - (((self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.origin.y : self.cellFakeView.frame.origin.x) - (offset + paddingTop)) / triggerInsetTop);
        }
    } else if (self.continousScrollDirection == LBScrollDirectionBottom) {
        if (self.cellFakeView) {
            percentage = 1.0 - (((insetTop + offsetEnd - paddingEnd) - ((self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.origin.y : self.cellFakeView.frame.origin.x) + (self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.size.height : self.cellFakeView.frame.size.width) + insetTop)) / triggerInsetEnd);
        }
    }
    percentage = fmin(1.0f, percentage);
    percentage = fmax(0, percentage);
    return percentage;
}

- (void)beginScrollIfNeeded
{
    if (self.cellFakeView == nil) {
        return;
    }
    CGFloat offset = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.contentOffset.y : self.collectionView.contentOffset.x;
    CGFloat triggerInsetTop = self.scrollDirection == UICollectionViewScrollDirectionVertical ?
    self.collectionView.contentInset.top : self.collectionView.contentInset.left;
    CGFloat triggerInsetEnd = self.scrollDirection == UICollectionViewScrollDirectionVertical ?
    self.collectionView.contentInset.bottom : self.collectionView.contentInset.right;
    CGFloat paddingTop = 0;
    CGFloat paddingend = 0;
    CGFloat length = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
    CGFloat fakeCellTopEdge = self.scrollDirection == UICollectionViewScrollDirectionVertical ? CGRectGetMinY(self.cellFakeView.frame) : CGRectGetMinX(self.cellFakeView.frame);
    CGFloat fakeCellEndEdge = self.scrollDirection == UICollectionViewScrollDirectionVertical ? CGRectGetMaxY(self.cellFakeView.frame) : CGRectGetMaxX(self.cellFakeView.frame);
    if (fakeCellTopEdge <= offset + paddingTop + triggerInsetTop) {
        self.continousScrollDirection = LBScrollDirectionTop;
        [self setUpDisPlayLink];
    } else if (fakeCellEndEdge >= offset + length - paddingend - triggerInsetEnd) {
        self.continousScrollDirection = LBScrollDirectionBottom;
        [self setUpDisPlayLink];
    } else {
        [self invalidateDisplayLink];
    }
}

#pragma mark - getter

- (CGFloat)scrollValueWithSpeed:(CGFloat)speed 
                  andPercentage:(CGFloat)percentage
{
    CGFloat value = 0.0f;
    switch (self.continousScrollDirection) {
        case LBScrollDirectionStay:
            return 0;
            break;
        case LBScrollDirectionTop:
            value = -speed;
            break;
        case LBScrollDirectionBottom:
            value = speed;
            break;
        default:
            return 0;
            break;
    }
    CGFloat proofedPercentage = fmax(fmin(1, percentage), 0);
    return value * proofedPercentage;
}

- (void)forceSetIsNeedReCaculateAllLayout:(BOOL)isNeedReCaculateAllLayout
{
    _isNeedReCalculateAllLayout = isNeedReCaculateAllLayout;
}

@end

  • 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
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531

子类layout 中的代码

//
//  LBHorizontalLayout.m
//  TEXT
//
//  Created by mac on 2024/5/18.
//  Copyright © 2024 刘博. All rights reserved.
//

#import "LBHorizontalLayout.h"
#import "LBCollectionReusableView.h"
#import "LBCollectionViewLayoutAttributes.h"
#import "LBCollectionViewBackgroundViewLayoutAttributes.h"

@implementation LBHorizontalLayout

#pragma mark - 初始化属性

- (instancetype)init
{
    if (self = [super init]) {
        self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    }
    return self;
}

- (void)prepareLayout
{
    [super prepareLayout];
    
    if (!self.isNeedReCalculateAllLayout) {
        //不需要重新计算
        return;
    }
    
    CGFloat totalHeight = self.collectionView.frame.size.height;
    CGFloat x = 0;
    CGFloat y = 0;
    CGFloat headerW = 0;
    CGFloat footerW = 0;
    UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
    CGFloat minimumLineSpacing = 0;
    CGFloat minimumInterItemSpacing = 0;
    NSInteger sectionCount = [self.collectionView numberOfSections];
    self.attributesArray = [NSMutableArray array];
    self.collectionHeightsArray = [NSMutableArray array];
    for (int index = 0; index < sectionCount; index ++) {
        NSInteger itemCount = [self.collectionView numberOfItemsInSection:index];
        if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
            headerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForHeaderInSection:index].width;
        } else {
            headerW = self.headerReferenceSize.width;
        }
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
            footerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:index].width;
        } else {
            footerW = self.footerReferenceSize.width;
        }
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
            edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:index];
        } else {
            edgeInsets = self.sectionInset;
        }
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:)]) {
            minimumLineSpacing = [self.delegate collectionView:self.collectionView layout:self minimumLineSpacingForSectionAtIndex:index];
        } else {
            minimumLineSpacing = self.minimumLineSpacing;
        }
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]) {
            minimumInterItemSpacing = [self.delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:index];
        } else {
            minimumInterItemSpacing = self.minimumInteritemSpacing;
        }
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:registerBackView:)]) {
            NSString *className = [self.delegate collectionView:self.collectionView layout:self registerBackView:index];
            if (className != nil && className.length > 0) {
                NSAssert([[NSClassFromString(className) alloc] init] != nil, @"代理collectionView:layout:registerBackView:里面必须返回有效的类名");
                [self registerClass:NSClassFromString(className) forDecorationViewOfKind:className];
            } else {
                [self registerClass:[LBCollectionReusableView class] forDecorationViewOfKind:@"LBCollectionReusableView"];
            }
        } else {
            [self registerClass:[LBCollectionReusableView class] forDecorationViewOfKind:@"LBCollectionReusableView"];
        }
        x = [self maxHeightWithSection:index];
        y = edgeInsets.top;
        
        if (headerW > 0) {
            NSIndexPath *headerIndexPath = [NSIndexPath indexPathForItem:0 inSection:index];
            LBCollectionViewLayoutAttributes *headerAttr = [LBCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:headerIndexPath];
            /*注意,因为这里是水平布局,所以self.collectionView.frame.size.height 是一个固定的很小的
             值
             */
            headerAttr.frame = CGRectMake(x, 0, headerW, self.collectionView.frame.size.height);
            [headerAttr setValue:[NSValue valueWithCGRect:headerAttr.frame] forKey:@"orginalFrame"];
            [self.attributesArray addObject:headerAttr];
            [self.headerAttributesArray addObject:headerAttr];
        }
        x += headerW;
        CGFloat itemStartX = x;
        CGFloat lastX = x;
        if (itemCount > 0) {
            x += edgeInsets.left;
            if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:typeOfLayout:)]) {
                self.layoutType = [self.delegate collectionView:self.collectionView layout:self typeOfLayout:index];
            }
            NSAssert((self.layoutType == LBLayoutTypeLabelVerticalLayout ||
                      self.layoutType == LBLayoutTypeColumnLayout ||
                      self.layoutType == LBLayoutTypeAbsoluteLayout), @"横向布局暂时只支持 LBLayoutTypeLabelVerticalLayout, LBLayoutTypeColumnLayout, LBLayoutTypeAbsoluteLayout");
            
            //NSInteger columnCount = 1;
            if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:columnCountOfSection:)]) {
                self.columnCount = [self.delegate collectionView:self.collectionView layout:self columnCountOfSection:index];
            }
            
            //定义一个列高数组,记录每一列的总高度
            CGFloat *columnWidths = (CGFloat *)malloc(self.columnCount * sizeof(CGFloat));
            //cell的高度
            CGFloat itemHeight = 0.0;
            if (self.layoutType == LBLayoutTypeColumnLayout) {
                for (int i = 0; i < self.columnCount; i ++) {
                    if (i == 0 && self.topLeftGap > 0) {
                        columnWidths[i] = self.topLeftGap + x;
                    } else if (i == 1 && self.bottomLeftGap > 0) {
                        columnWidths[i] = self.bottomLeftGap;
                    } else {
                        columnWidths[i] = x;
                    }
                }
                itemHeight = (totalHeight - edgeInsets.top - edgeInsets.bottom - minimumLineSpacing * (self.columnCount - 1)) / self.columnCount;
            }
            NSInteger lastColumnIndex = 0;
            NSMutableArray *arrayOfAbsolute = [NSMutableArray array]; //储存绝对定位布局的额数组
            
            for (int i = 0; i < itemCount; i ++) {
                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:index];
                CGSize itemSize = CGSizeZero;
                if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:sizeForItemAtIndexPath:)]) {
                    itemSize = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
                } else {
                    itemSize = self.itemSize;
                }
                
                LBCollectionViewLayoutAttributes *attributes = [LBCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
                
                NSInteger preRow = self.attributesArray.count - 1;
                switch (self.layoutType) {
#pragma mark - 纵向标签布局处理
                    case LBLayoutTypeLabelVerticalLayout:
                    {
                        if (preRow >= 0) {
                            if (i > 0) {
                                LBCollectionViewLayoutAttributes *preAttr = self.attributesArray[preRow];
                                y = preAttr.frame.origin.y + preAttr.frame.size.height + minimumInterItemSpacing;
                                if (y + itemSize.height > totalHeight - edgeInsets.bottom) {
                                    y = edgeInsets.top;
                                    x += itemSize.width + minimumLineSpacing;
                                }
                            }
                        }
                        attributes.frame = CGRectMake(x, y, itemSize.width, itemSize.height);
                    }
                        break;
                    case LBLayoutTypeLabelHorizontalLayout: {
                        
                    }
                        break;
#pragma mark - 列布局处理 |横向标签布局处理
                    case LBLayoutTypeColumnLayout:
                    {
                        CGFloat max = CGFLOAT_MAX;
                        NSInteger column = 0;
                        if (self.columnSortType == LBColumnSortTypeSequence) {
                            column = lastColumnIndex;
                        } else {
                            for (int i = 0; i < self.columnCount; i ++) {
                                if (columnWidths[i] < max) {
                                    max = columnWidths[i];
                                    column = i;
                                }
                            }
                        }
                        CGFloat itemX = columnWidths[column];
                        CGFloat itemY = edgeInsets.top + (itemHeight + minimumInterItemSpacing) * column;
                        if (self.manulHeight) {
                            attributes.frame = CGRectMake(itemX, itemY, itemSize.width, itemSize.height);
                        } else {
                            attributes.frame = CGRectMake(itemX, itemY, itemSize.width, itemHeight);
                        }
                        NSLog(@"哈哈哈这里的布局%@", NSStringFromCGRect(attributes.frame));
                        if (self.manulHeight) {
                            if (itemSize.height > itemHeight) {
                                CGFloat delta = itemSize.height - itemHeight;
                                if (delta > minimumInterItemSpacing) {
                                    NSInteger k = ceil(delta/(itemHeight + minimumInterItemSpacing));
                                    for (int i = 0; i < k ; i ++) {
                                        if ((column + i + 1) < self.columnCount) {
                                            columnWidths[column + i + 1] += (itemSize.width + minimumLineSpacing);
                                        }
                                    }
                                }
                            }
                        }
                        columnWidths[column] += (itemSize.width + minimumLineSpacing);
                        lastColumnIndex ++;
                        if (lastColumnIndex >= self.columnCount) {
                            lastColumnIndex = 0;
                        }
                    }
                        break;
                    case LBLayoutTypeAbsoluteLayout:
                    {
                        CGRect itemFrame = CGRectZero;
                        if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:rectOfItem:)]) {
                            itemFrame = [self.delegate collectionView:self.collectionView layout:self rectOfItem:indexPath];
                        }
                        CGFloat absolute_x = x + itemFrame.origin.x;
                        CGFloat absolute_y = edgeInsets.top + itemFrame.origin.y;
                        CGFloat absolute_h = itemFrame.size.height;
                        
                        if ((absolute_y + absolute_h > self.collectionView.frame.size.height - edgeInsets.bottom) &&
                            (absolute_y < self.collectionView.frame.size
                             .height - edgeInsets.top)) {
                                 absolute_h -= (absolute_y + absolute_h - (self.collectionView.frame.size.height - edgeInsets.bottom));
                             }
                        
                        CGFloat absolute_w = itemFrame.size.width;
                        attributes.frame = CGRectMake(absolute_x, absolute_y, absolute_w, absolute_h);
                        [arrayOfAbsolute addObject:attributes];
                    }
                        break;
                        
                    default:
                        break;
                }
                
                if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:transformOfItem:)]) {
                    attributes.transform3D = [self.delegate collectionView:self.collectionView layout:self transformOfItem:indexPath];
                }
                if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:zIndexOfItem:)]) {
                    attributes.zIndex = [self.delegate collectionView:self.collectionView layout:self zIndexOfItem:indexPath];
                }
                if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:alphaOfItem:)]) {
                    attributes.alpha = [self.delegate collectionView:self.collectionView layout:self alphaOfItem:indexPath];
                }
                attributes.indexPath = indexPath;
                if (self.layoutType != LBLayoutTypePercentLayout) {
                    [self.attributesArray addObject:attributes];
                }
                
                if(self.layoutType == LBLayoutTypeColumnLayout) {
                    CGFloat max = 0;
                    for (int i = 0; i < self.columnCount; i ++) {
                        if (columnWidths[i] > max) {
                            max = columnWidths[i];
                        }
                    }
                    lastX = max;
                } else if (self.layoutType == LBLayoutTypeAbsoluteLayout) {
                    if (i == itemCount - 1) {
                        for (LBCollectionViewLayoutAttributes *attr in arrayOfAbsolute) {
                            if (lastX < attr.frame.origin.x + attr.frame.size.width) {
                                lastX = attr.frame.origin.x + attr.frame.size.width;
                            }
                        }
                    }
                } else {
                    lastX = attributes.frame.origin.x + attributes.frame.size.width;
                }
            }
            free(columnWidths);
        }
        
        if (self.layoutType == LBLayoutTypeColumnLayout) {
            if (itemCount > 0) {
                lastX -= minimumLineSpacing;
            }
        }
        
        if (itemCount > 0) {
            lastX += edgeInsets.right;
        }
        
        //添加页脚属性
        if (footerW > 0) {
            NSIndexPath *footerIndexPath = [NSIndexPath indexPathForItem:0 inSection:index];
            LBCollectionViewLayoutAttributes *footerAttr = [LBCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:footerIndexPath];
            footerAttr.frame = CGRectMake(lastX, 0, footerW, self.collectionView.frame.size.height);
            [self.attributesArray addObject:footerAttr];
            lastX += footerW;
        }
        
        //添加背景视图
        CGFloat backWidth = lastX - itemStartX + ([self isAttachToBottom:index] ? headerW : 0) - ([self isAttachToTop:index] ? 0: footerW);
        if (backWidth < 0) {
            backWidth = 0;
        }
        if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:registerBackView:)]) {
            NSString *className = [self.delegate collectionView:self.collectionView layout:self registerBackView:index];
            if (className != nil && className.length > 0) {
                LBCollectionViewBackgroundViewLayoutAttributes *attr = [LBCollectionViewBackgroundViewLayoutAttributes layoutAttributesForDecorationViewOfKind:className withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
                attr.frame = CGRectMake([self isAttachToTop:index] ? itemStartX - headerW: itemStartX, 0, backWidth, self.collectionView.frame.size.height);
                attr.zIndex = - 1000;
                if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backgroundViewMethodForSection:)]) {
                    if ([self.delegate collectionView:self.collectionView layout:self backgroundViewMethodForSection:index] != nil) {
                        [attr callMethod:[self.delegate collectionView:self.collectionView layout:self backgroundViewMethodForSection:index]];
                    }
                }
                [self.attributesArray addObject:attr];
            } else {
                LBCollectionViewLayoutAttributes *attr = [LBCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"LBCollectionReusableView" withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
                attr.frame = CGRectMake([self isAttachToTop:index] ? itemStartX - headerW : itemStartX, 0, backWidth, self.collectionView.frame.size.height);
                attr.color = self.collectionView.backgroundColor;
                if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backColorForSection:)]) {
                    attr.color = [self.delegate collectionView:self.collectionView layout:self backColorForSection:index];
                }
                
                if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backImageForSection:)]) {
                    attr.image = [self.delegate collectionView:self.collectionView layout:self backImageForSection:index];
                }
                attr.zIndex = - 1000;
                [self.attributesArray addObject:attr];
            }
        } else {
            LBCollectionViewLayoutAttributes *attr = [LBCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"LBCollectionReusableView" withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
            attr.frame = CGRectMake([self isAttachToTop:index] ? itemStartX - headerW : itemStartX, 0, backWidth, self.collectionView.frame.size.height);
            attr.color = self.collectionView.backgroundColor;
            if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backColorForSection:)]) {
                attr.color = [self.delegate collectionView:self.collectionView layout:self backColorForSection:index];
            }
            
            if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backImageForSection:)]) {
                attr.image = [self.delegate collectionView:self.collectionView layout:self backImageForSection:index];
            }
            attr.zIndex = - 1000;
            [self.attributesArray addObject:attr];
        }
        self.collectionHeightsArray[index] = [NSNumber numberWithFloat:lastX];
    }
    [self forceSetIsNeedReCaculateAllLayout:NO];
}

#pragma mark - 内容size

- (CGSize)collectionViewContentSize
{
    if (self.collectionHeightsArray.count < 0) {
        return CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height);
    }
    
    CGFloat footerW = 0;
    if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
        footerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:self.collectionHeightsArray.count - 1].width;
    } else {
        footerW = self.footerReferenceSize.width;
    }
    
    UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
    if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
        edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:self.collectionHeightsArray.count - 1];
    } else {
        edgeInsets = self.sectionInset;
    }
    return CGSizeMake([self.collectionHeightsArray[self.collectionHeightsArray.count - 1] floatValue], self.collectionView.frame.size.height);
}

- (BOOL)isAttachToTop:(NSInteger)section
{
    if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:attachToTop:)]) {
        return [self.delegate collectionView:self.collectionView layout:self attachToTop:section];
    }
    return NO;
}

- (BOOL)isAttachToBottom:(NSInteger)section
{
    if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:attachToBottom:)]) {
        return [self.delegate collectionView:self.collectionView layout:self attachToBottom:section];
    }
    return NO;
}
/// 每个区的初始x坐标
/// - Parameter section: 区索引
- (CGFloat)maxHeightWithSection:(NSInteger)section
{
    if (section > 0) {
        return [self.collectionHeightsArray[section - 1] floatValue];
    } else {
        return 0;
    }
}

@end

  • 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
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/610849
推荐阅读
相关标签
  

闽ICP备14008679号