将view存储在array中, 每次增加或者删除view的时候, 去遍历数组, 然后重新排序
// - 定义的结构体和枚举 :
//
// QIEContainerBgViewHeader.h
// QEZB
//
// Created by 李超群 on 2020/6/15.
// Copyright © 2020 zhou. All rights reserved.
//
#ifndef QIEContainerBgViewHeader_h
#define QIEContainerBgViewHeader_h
#pragma mark - 定义位置的结构体
typedef struct{
CGFloat horMargin;
CGFloat verMargin;
CGFloat horPadding;
CGFloat verPadding;
} QIEPosition;
CG_INLINE QIEPosition
QIEPositionMake(CGFloat horMargin, CGFloat verMargin, CGFloat horPadding, CGFloat verPadding){
QIEPosition pos;
pos.horMargin = horMargin;
pos.verMargin = verMargin;
pos.horPadding = horPadding;
pos.verPadding = verPadding;
return pos;
}
#pragma mark - 开始位置的类型
typedef NS_ENUM(NSInteger, QIELayoutType) {
QIELayoutTypeTopLeft,
QIELayoutTypeTopRight,
QIELayoutTypeBottomLeft,
QIELayoutTypeBottomRight,
};
#pragma mark - 行和列的结构体
typedef NSInteger QIEViewColumn;
typedef NSInteger QIEViewRow;
#pragma mark - 普通直播间的chatview
typedef NS_ENUM(QIEViewRow, QIEViewLandspaceChatViewRow) {
/** 领取鹅蛋 */
QIEViewRowEDan = 0,
/** 宝箱的行 */
QIEViewRowGiftBox = 1,
/** 大神推单的行 */
QIEViewRowGuessExpert = 2,
/** 广告的行 */
QIEViewRowAdView = 3,
/** 抽奖的行 */
QIEViewRowLuckyDraw = 4,
/** 行的最大值, 此枚举中定义的值不能超过这个值 */
QIEViewRowMax = 5
};
typedef NS_ENUM(QIEViewColumn, QIEViewLandspaceChatViewColumn) {
/** 宝箱的列 */
QIEViewColumnGiftBox = 0,
/** 列的最大值, 此枚举中定义的值不能超过这个值 */
QIEViewColumnMax,
};
#pragma mark - 女神直播间的BottomView
typedef NS_ENUM(QIEViewRow, QIEViewPortraitLiveRoomBottomViewRow) {
QIEViewPortraitLiveRoomBottomViewRow0 = 0,
/** 行的最大值, 此枚举中定义的值不能超过这个值 */
QIEViewPortraitLiveRoomBottomViewRowMax,
};
typedef NS_ENUM(QIEViewColumn, QIEViewPortraitLiveRoomBottomViewColumn) {
QIEViewPortraitLiveRoomBottomViewColumn0 = 0,
QIEViewPortraitLiveRoomBottomViewColumn10 = 10,
QIEViewPortraitLiveRoomBottomViewColumn20 = 20,
QIEViewPortraitLiveRoomBottomViewColumn30 = 30,
/** 列的最大值, 此枚举中定义的值不能超过这个值 */
QIEViewPortraitLiveRoomBottomViewColumnMax,
};
#pragma mark - 女神手播的BottomView
typedef NS_ENUM(QIEViewRow, QIEViewPortraitStartLiveBottomViewRow) {
QIEViewPortraitStartLiveBottomViewRow0 = 0,
/** 行的最大值, 此枚举中定义的值不能超过这个值 */
QIEViewPortraitStartLiveBottomViewRowMax,
};
typedef NS_ENUM(QIEViewColumn, QIEViewPortraitStartLiveBottomViewColumn) {
QIEViewPortraitStartLiveBottomViewColumn0 = 0,
QIEViewPortraitStartLiveBottomViewColumn10 = 10,
QIEViewPortraitStartLiveBottomViewColumn20 = 20,
QIEViewPortraitStartLiveBottomViewColumn30 = 30,
QIEViewPortraitStartLiveBottomViewColumn40 = 40,
/** 列的最大值, 此枚举中定义的值不能超过这个值 */
QIEViewPortraitStartLiveBottomViewColumnMax,
};
#endif /* QIEContainerBgViewHeader_h */
// - 声明
//
// QIEDynamicLayoutBgView.h
// AFNetworking
//
// Created by 李超群 on 2020/5/9.
//
#import "QIEContainerBgView.h"
#import "QIEDynamicLayoutBgViewHeader.h"
@interface QIEDynamicLayoutBgView : QIEContainerBgView
/** layoutType : 开始布局的位置; margin : 距离边框的位置; padding : view之间的间距; */
-(instancetype)initWithLayoutType:(QIELayoutType)layoutType
position:(QIEPosition)position
maxRow:(NSInteger)maxRow
maxColumn:(NSInteger)maxColumn;
/** 为某一行增加一个 view */
- (void)showSubView:(UIView *)subView row:(QIEViewRow)row column:(QIEViewColumn)column shouldForcPosition:(BOOL)shouldForcPosition;
/** 去掉某一行的某个 view */
- (void)hiddenSubView:(UIView *)subView;
@end
// - 实现 :
//
// QIEDynamicLayoutBgView.m
// AFNetworking
//
// Created by 李超群 on 2020/5/9.
//
#import "QIEDynamicLayoutBgView.h"
#import
#ifndef QIEKeypath
#define QIEKeypath(OBJ, PATH) @(((void)(NO && ((void)OBJ.PATH, NO)), # PATH))
#endif
@interface UIView (DYPosition)
/** 行 */
@property (nonatomic, assign) QIEViewRow row;
/** 列 */
@property (nonatomic, assign) QIEViewColumn column;
@end
@implementation UIView (DYPosition)
NSString *rowKey = @"rowKey";
NSString *columnKey = @"columnKey";
- (void)setRow:(QIEViewRow)row{
objc_setAssociatedObject(self, [rowKey UTF8String], [NSNumber numberWithInteger:row], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(QIEViewRow)row{
return [objc_getAssociatedObject(self, [rowKey UTF8String]) integerValue];
}
- (void)setColumn:(QIEViewColumn)column{
objc_setAssociatedObject(self, [columnKey UTF8String], [NSNumber numberWithInteger:column], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (QIEViewColumn)column{
return [objc_getAssociatedObject(self, [columnKey UTF8String]) integerValue];
}
@end
@interface QIEDynamicLayoutViewConfig : NSObject
/** 行 */
@property (nonatomic, assign) QIEViewRow row;
/** 列 */
@property (nonatomic, assign) QIEViewColumn column;
/** 尺寸 */
@property (nonatomic, assign) CGSize viewSize;
/** 是否强制显示在这个位置 */
@property (nonatomic, assign) BOOL shouldForcPosition;
/** 绑定的view */
@property (nonatomic, weak) UIView *view;
@end
@implementation QIEDynamicLayoutViewConfig
- (BOOL)isEqual:(QIEDynamicLayoutViewConfig *)object{
return self.view == object.view;
}
@end
@interface QIEDynamicLayoutBgView ()
/** 所有的row的数组 */
@property (nonatomic, strong) NSMutableArray *> *rowsArray;
/** 被暂时隐藏的view的config的集合 */
@property (nonatomic, strong) NSMutableArray *backupConfigArray;
/** 超过规定的枚举值 */
@property (nonatomic, assign) int exceedCount;
@property (nonatomic, assign) QIELayoutType layoutType;
@property (nonatomic, assign) QIEPosition position;
@property (nonatomic, assign) NSInteger maxRow;
@property (nonatomic, assign) NSInteger maxColumn;
@end
@implementation QIEDynamicLayoutBgView
/** layoutType : 开始布局的位置; margin : 距离边框的位置; padding : view之间的间距; */
-(instancetype)initWithLayoutType:(QIELayoutType)layoutType
position:(QIEPosition)position
maxRow:(NSInteger)maxRow
maxColumn:(NSInteger)maxColumn{
if (self = [super init]){
self.layoutType = layoutType;
self.position = position;
self.maxRow = maxRow;
self.maxColumn = maxColumn;
self.rowsArray = [NSMutableArray array];
self.backupConfigArray = [NSMutableArray array];
for (int i = 0; i < self.maxRow; i++) {
NSMutableArray * configsArray = [NSMutableArray array];
[self.rowsArray addObject:configsArray];
}
}
return self;
}
/** 为某一行增加一个 view */
- (void)showSubView:(UIView *)subView row:(QIEViewRow)row column:(QIEViewColumn)column shouldForcPosition:(BOOL)shouldForcPosition{
if (!subView) return;
// - 设置view的属性值
QIEDynamicLayoutViewConfig *config = [[QIEDynamicLayoutViewConfig alloc]init];
config.row = row;
config.column = column;
config.view = subView;
config.shouldForcPosition = shouldForcPosition;
config.viewSize = subView.frame.size;
[self addSubview:config.view];
// - 测试环境bug检查
#if DEBUG
if (!shouldForcPosition) {
if (row >= self.maxRow || column >= self.maxColumn) {
@throw [NSException exceptionWithName:@"程序被终止" reason:@"传入的view的row和column超过了最大的限制" userInfo:nil];
}
NSMutableArray *debugArray = [self.rowsArray objectAtIndex:row];
for (QIEDynamicLayoutViewConfig *pConfig in debugArray) {
if (pConfig.row == row && pConfig.column == column && subView != pConfig.view && !pConfig.shouldForcPosition) {
@throw [NSException exceptionWithName:@"程序被终止" reason:@"传入的view的row和column已经存在了" userInfo:nil];
break;
}
}
}
#endif
// - 添加view
NSMutableArray *configArray = [self.rowsArray objectAtIndex:row];
// - 正式环境如果view的column重复了 就重新赋值, 把新的view向后移动
NSArray *columeArray = [configArray valueForKeyPath:QIEKeypath(config, column)];
// - 如果已经包含了相同位置的view, 判断是否强制显示在这个位置, 如果是强制显示, 隐藏之前的view, 否则把新加进来的view, 移动到之前的位置
for (int idx = 0; idx < columeArray.count; idx++) {
NSNumber *obj = columeArray[idx];
if (obj.intValue == column) {
QIEDynamicLayoutViewConfig *tempConfig = configArray[idx];
if (!config.shouldForcPosition && !tempConfig.shouldForcPosition) {
self.exceedCount++;
config.column = self.exceedCount + self.maxColumn;
}else{
QIEDynamicLayoutViewConfig *removedConfig = config.shouldForcPosition ? tempConfig : config;
removedConfig.view.hidden = YES;
[configArray removeObject:removedConfig];
[self.backupConfigArray containsObject:removedConfig] ?: [self.backupConfigArray insertObject:removedConfig atIndex:0];
if (removedConfig == config) {
subView.row = config.row;
subView.column = config.column;
return;
}
}
break;
}
}
// - 添加并布局view
if (![configArray containsObject:config]) {
[configArray addObject:config];
subView.row = config.row;
subView.column = config.column;
config.view.hidden = NO;
[self relayoutSubviews];
}
return;
}
/** 去掉某一行的某个 view */
- (void)hiddenSubView:(UIView *)subView{
if (!subView) return;
// -
QIEDynamicLayoutViewConfig *config = [[QIEDynamicLayoutViewConfig alloc]init];
config.view = subView;
config.view.hidden = YES;
// - 从布局数组中移除view
for (NSMutableArray * configArray in self.rowsArray) {
if ([configArray containsObject:config]) {
[configArray removeObject:config];
break;
}
}
// - 在备用数组中查询, 如果存在这个的view, 就移除这个view, 如果存在相同位置的view, 就显示这个view;
if ([self.backupConfigArray containsObject:config]) {
[self.backupConfigArray removeObject:config];
}else{
for (QIEDynamicLayoutViewConfig * obj in self.backupConfigArray) {
if (obj.row == subView.row && obj.column == subView.column) {
[self showSubView:obj.view row:obj.row column:obj.column shouldForcPosition:obj.shouldForcPosition];
[self.backupConfigArray removeObject:obj];
return;
}
};
}
[self relayoutSubviews];
}
/** 确定每个view的位置 */
-(void)layoutViewPositionWithlastVerView:(UIView *)lastVerView lastHorView:(UIView *)lastHorView config:(QIEDynamicLayoutViewConfig *)config row:(NSInteger)row column:(NSInteger)column{
CGFloat viewVerMargin = (row == 0) ? self.position.verMargin : self.position.verPadding;
CGFloat viewHorMargin = (column == 0) ? self.position
.horMargin : self.position.horPadding;
if (self.layoutType == QIELayoutTypeTopRight) {
[config.view mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(config.viewSize);
column == 0 ? make.right.equalTo(self).offset(-viewHorMargin) : make.right.equalTo(lastHorView.mas_left).offset(-viewHorMargin);
row == 0 ? make.top.equalTo(self).offset(viewVerMargin) : make.top.equalTo(lastVerView.mas_bottom).offset(viewVerMargin);
}];
}else if(self.layoutType == QIELayoutTypeTopLeft){
[config.view mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(config.viewSize);
column == 0 ? make.left.equalTo(self).offset(viewHorMargin) : make.left.equalTo(lastHorView.mas_right).offset(viewHorMargin);
row == 0 ? make.top.equalTo(self).offset(viewVerMargin) : make.top.equalTo(lastVerView.mas_bottom).offset(viewVerMargin);
}];
}else if(self.layoutType == QIELayoutTypeBottomRight){
[config.view mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(config.viewSize);
column == 0 ? make.right.equalTo(self).offset(-viewHorMargin) : make.right.equalTo(lastHorView.mas_left).offset(-viewHorMargin);
row == 0 ? make.bottom.equalTo(self).offset(-viewVerMargin) : make.bottom.equalTo(lastVerView.mas_top).offset(-viewVerMargin);
}];
}else if(self.layoutType == QIELayoutTypeBottomLeft){
[config.view mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(config.viewSize);
column == 0 ? make.left.equalTo(self).offset(viewHorMargin) : make.left.equalTo(lastHorView.mas_right).offset(viewHorMargin);
row == 0 ? make.bottom.equalTo(self).offset(-viewVerMargin) : make.bottom.equalTo(lastVerView.mas_top).offset(-viewVerMargin);
}];
}
}
/** 重新排序布局 */
-(void)relayoutSubviews{
// - 移除掉没有数据的行
NSMutableArray *> *tempArray = [NSMutableArray array];
[self.rowsArray enumerateObjectsUsingBlock:^(NSMutableArray * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.count) {
[tempArray addObject:obj];
}
}];
/** 先排序 然后循环设置布局 */
__block UIView *lastVerView = nil;
[tempArray enumerateObjectsUsingBlock:^(NSMutableArray *rowArray, NSUInteger row, BOOL * _Nonnull stop) {
/** 遍历每一行的时候, 排序每个view */
[rowArray sortUsingComparator:^NSComparisonResult(QIEDynamicLayoutViewConfig *config1, QIEDynamicLayoutViewConfig *config2) {
return (config1.column < config2.column) ? NSOrderedAscending : NSOrderedDescending;
}];
/** 布局 */
__block UIView *lastHorView = nil;
[rowArray enumerateObjectsUsingBlock:^(QIEDynamicLayoutViewConfig *config, NSUInteger column, BOOL * _Nonnull stop) {
// - 确定每个view的位置
[self layoutViewPositionWithlastVerView:lastVerView lastHorView:lastHorView config:config row:row column:column];
lastHorView = config.view;
if (column == rowArray.count - 1) lastVerView = config.view;
}];
}];
}
/** 移除时候先把这个view hidden */
-(void)willRemoveSubview:(UIView *)subview{
if (self.superview) {
[self hiddenSubView:subview];
}
}
/** 添加的按钮默认是hidden的 */
- (void)addSubview:(UIView *)view{
[super addSubview:view];
view.hidden = YES;
}
@end