// // QXNobilityContentView.m // QXLive // // Created by 启星 on 2025/11/7. // #import "QXNobilityContentView.h" @interface QXNobilityContentView() @property (nonatomic, strong) NSMutableArray *privilegeTitles; @property (nonatomic, assign) NSInteger rowCount; @property (nonatomic, strong) NSMutableArray *rowHeaders; @property (nonatomic, assign) CGFloat rowHeaderWidth; @property (nonatomic, assign) CGFloat columnHeaderHeight; @property (nonatomic, assign) CGFloat cellWidth; @property (nonatomic, assign) CGFloat cellHeight; // 滚动视图 @property (nonatomic, strong) UIScrollView *verticalScrollView; // 处理垂直滚动 @property (nonatomic, strong) UIScrollView *horizontalScrollView; // 处理水平滚动 @property (nonatomic, strong) UIScrollView *contentScrollView; // 内容区域滚动 @property (nonatomic, strong) UIScrollView *rowHeaderScrollView; // 行标题垂直滚动 // 固定视图 @property (nonatomic, strong) UIView *cornerView; // 左上角 @property (nonatomic, strong) UIView *columnHeaderView; // 列标题(第一行) @property (nonatomic, strong)UILabel *cornerLabel; // 内容容器 @property (nonatomic, strong) UIView *contentContainer; @property (nonatomic, strong) UIView *rowHeaderContainer; @property (nonatomic, strong) UIView *borderView; @property (nonatomic, assign) BOOL isSyncingScroll; @property (nonatomic, assign) NSInteger currentIndex; @end @implementation QXNobilityContentView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setup]; } return self; } - (void)setup { self.clipsToBounds = YES; int itemWidth = (self.width/4); self.rowHeaderWidth = itemWidth; self.columnHeaderHeight = 50; self.cellWidth = itemWidth; self.cellHeight = 42; self.isSyncingScroll = NO; [self createScrollViews]; [self createFixedViews]; [self createContentViews]; [self bringSubviewToFront:self.horizontalScrollView]; [self bringSubviewToFront:self.rowHeaderScrollView]; [self bringSubviewToFront:self.cornerView]; [self bringSubviewToFront:self.contentScrollView]; } - (void)createScrollViews { // 主垂直滚动视图 - 占据整个视图,但只处理垂直滚动 self.verticalScrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; self.verticalScrollView.delegate = self; self.verticalScrollView.bounces = NO; self.verticalScrollView.alwaysBounceVertical = YES; self.verticalScrollView.showsVerticalScrollIndicator = YES; self.verticalScrollView.showsHorizontalScrollIndicator = NO; [self addSubview:self.verticalScrollView]; // 主水平滚动视图 - 占据整个视图,但只处理水平滚动 self.horizontalScrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; self.horizontalScrollView.delegate = self; self.horizontalScrollView.bounces = NO; self.horizontalScrollView.alwaysBounceHorizontal = YES; self.horizontalScrollView.showsHorizontalScrollIndicator = YES; self.horizontalScrollView.showsVerticalScrollIndicator = NO; [self addSubview:self.horizontalScrollView]; // 内容滚动视图 - 只显示内容区域(不包括固定行列) CGRect contentFrame = CGRectMake(self.rowHeaderWidth, self.columnHeaderHeight, self.bounds.size.width - self.rowHeaderWidth, self.bounds.size.height - self.columnHeaderHeight); self.contentScrollView = [[UIScrollView alloc] initWithFrame:contentFrame]; self.contentScrollView.delegate = self; self.contentScrollView.bounces = NO; self.contentScrollView.alwaysBounceVertical = YES; self.contentScrollView.alwaysBounceHorizontal = YES; self.contentScrollView.showsVerticalScrollIndicator = NO; self.contentScrollView.showsHorizontalScrollIndicator = NO; [self addSubview:self.contentScrollView]; // 行标题滚动视图 - 支持垂直滚动 CGRect rowHeaderFrame = CGRectMake(0, self.columnHeaderHeight, self.rowHeaderWidth, self.bounds.size.height - self.columnHeaderHeight); self.rowHeaderScrollView = [[UIScrollView alloc] initWithFrame:rowHeaderFrame]; self.rowHeaderScrollView.delegate = self; self.rowHeaderScrollView.bounces = NO; self.rowHeaderScrollView.alwaysBounceVertical = YES; self.rowHeaderScrollView.showsVerticalScrollIndicator = NO; self.rowHeaderScrollView.showsHorizontalScrollIndicator = NO; [self addSubview:self.rowHeaderScrollView]; self.borderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.cellWidth, 0)]; [self.borderView addRoundedCornersWithRadius:6]; self.borderView.layer.borderWidth = 1; self.borderView.layer.borderColor = RGB16(0xF5E9D1).CGColor; self.borderView.hidden = YES; } - (void)createFixedViews { // 左上角固定区域 - 永远固定在左上角 self.cornerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.rowHeaderWidth, self.columnHeaderHeight)]; self.cornerView.backgroundColor = [UIColor clearColor]; self.cornerView.backgroundColor = RGB16(0x2A2A4E); self.cornerView.userInteractionEnabled = NO; // 禁止交互,让滚动视图可以接收触摸 UILabel *cornerLabel = [[UILabel alloc] initWithFrame:self.cornerView.bounds]; cornerLabel.textAlignment = NSTextAlignmentCenter; cornerLabel.textColor = UIColor.whiteColor; cornerLabel.font = [UIFont boldSystemFontOfSize:14]; self.cornerLabel = cornerLabel; [self.cornerView addSubview:cornerLabel]; [self addSubview:self.cornerView]; // 列标题固定区域 - 永远固定在第一行 self.columnHeaderView = [[UIView alloc] initWithFrame:CGRectMake(self.rowHeaderWidth, 0, self.bounds.size.width - self.rowHeaderWidth, self.columnHeaderHeight)]; self.columnHeaderView.backgroundColor = [UIColor clearColor]; self.columnHeaderView.backgroundColor = RGB16(0x2A2A4E); self.columnHeaderView.userInteractionEnabled = NO; [self insertSubview:self.columnHeaderView belowSubview:self.cornerView]; } - (void)createContentViews { if (self.privilegeTitles.count == 0) { return; } NSArray *columnTitles = self.privilegeTitles; NSInteger columnCount = columnTitles.count - 1; // 减去第一列 // 计算内容总尺寸 CGFloat contentWidth = self.cellWidth * columnCount; CGFloat contentHeight = self.cellHeight * self.rowCount; // 设置滚动视图内容大小 self.verticalScrollView.contentSize = CGSizeMake(self.bounds.size.width, self.columnHeaderHeight + contentHeight); self.horizontalScrollView.contentSize = CGSizeMake(self.rowHeaderWidth + contentWidth, self.bounds.size.height); self.contentScrollView.contentSize = CGSizeMake(contentWidth, contentHeight); self.rowHeaderScrollView.contentSize = CGSizeMake(self.rowHeaderWidth, contentHeight); self.borderView.height = self.columnHeaderHeight + contentHeight; // 创建内容容器 self.contentContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, contentWidth, contentHeight)]; [self.contentScrollView addSubview:self.contentContainer]; // 创建行标题容器 self.rowHeaderContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.rowHeaderWidth, contentHeight)]; [self.rowHeaderScrollView addSubview:self.rowHeaderContainer]; // 创建列标题 [self createColumnHeaders:columnTitles]; // 创建行标题 [self createRowHeaders]; // 创建内容单元格 [self createContentCells:columnTitles]; } - (void)createColumnHeaders:(NSArray *)columnTitles { // 清除旧内容 for (UIView *view in self.columnHeaderView.subviews) { [view removeFromSuperview]; } CGFloat totalHeaderWidth = self.cellWidth * (columnTitles.count - 1); for (int i = 0; i < columnTitles.count - 1; i++) { UIView *headerCell = [[UIView alloc] initWithFrame:CGRectMake(i * self.cellWidth, 0, self.cellWidth, self.columnHeaderHeight)]; UILabel *label = [[UILabel alloc] initWithFrame:headerCell.bounds]; label.text = columnTitles[i + 1]; // 跳过第一列 label.textAlignment = NSTextAlignmentCenter; label.font = [UIFont boldSystemFontOfSize:12]; label.textColor = UIColor.whiteColor; label.numberOfLines = 0; [headerCell addSubview:label]; [self.columnHeaderView addSubview:headerCell]; } // 设置列标题容器的内容大小 self.columnHeaderView.frame = CGRectMake(self.rowHeaderWidth, 0, totalHeaderWidth, self.columnHeaderHeight); } - (void)createRowHeaders { // 清除旧内容 for (UIView *view in self.rowHeaderContainer.subviews) { [view removeFromSuperview]; } for (int i = 0; i < self.rowHeaders.count; i++) { QXUserNobility *titleData = self.rowHeaders[i]; UIView *headerCell = [[UIView alloc] initWithFrame:CGRectMake(0, i * self.cellHeight, self.rowHeaderWidth, self.cellHeight)]; // headerCell.layer.borderWidth = 0.5; // headerCell.layer.borderColor = [UIColor lightGrayColor].CGColor; UILabel *label = [[UILabel alloc] initWithFrame:headerCell.bounds]; label.text = [NSString stringWithFormat:@"%@", titleData.name]; label.textAlignment = NSTextAlignmentCenter; label.textColor = UIColor.whiteColor; label.font = [UIFont systemFontOfSize:12]; [headerCell addSubview:label]; if (i%2==0) { headerCell.backgroundColor = RGB16A(0x323252, 1); }else{ headerCell.backgroundColor = RGB16A(0x2A2A4E, 1); } // 高亮当前用户 // if (titleData.isCurrentUser) { // headerCell.backgroundColor = [UIColor colorWithRed:0.8 green:0.9 blue:1.0 alpha:1.0]; // headerCell.layer.borderWidth = 2; // headerCell.layer.borderColor = [UIColor systemBlueColor].CGColor; // } [self.rowHeaderContainer addSubview:headerCell]; } } - (void)createContentCells:(NSArray *)columnTitles { // // 清除旧内容 for (UIView *view in self.contentContainer.subviews) { [view removeFromSuperview]; } for (int col = 0; col < columnTitles.count - 1; col++) { QXNobilityLevel *model = self.model.nobility_power_list[col+1]; for (int row = 0; row < self.rowHeaders.count; row++) { // UIView *cell = [[UIView alloc] initWithFrame:CGRectMake(col * self.cellWidth, row * self.cellHeight, self.cellWidth, self.cellHeight)]; // cell.layer.borderWidth = 0.5; // cell.layer.borderColor = [UIColor lightGrayColor].CGColor; // // UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, self.cellWidth - 10, self.cellHeight)]; // label.font = [UIFont systemFontOfSize:12]; // label.numberOfLines = 0; // label.textColor = UIColor.whiteColor; QXUserNobility *md = model.nobility_list[row]; // label.text = md.status.intValue == 1?@"开":@"关"; // [cell addSubview:label];; // [self.contentContainer addSubview:cell]; QXNobilityContentColView *colView = [[QXNobilityContentColView alloc] initWithFrame:CGRectMake(col * self.cellWidth, row * self.cellHeight, self.cellWidth, self.cellHeight)]; colView.model = md; if (row%2==0) { colView.backgroundColor = RGB16A(0x323252, 1); }else{ colView.backgroundColor = RGB16A(0x2A2A4E, 1); } [self.contentContainer addSubview:colView]; } } } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (self.isSyncingScroll) return; self.isSyncingScroll = YES; if (scrollView == self.verticalScrollView) { // 垂直滚动时,同步内容区域和行标题的垂直滚动 CGFloat offsetY = scrollView.contentOffset.y; self.contentScrollView.contentOffset = CGPointMake(self.contentScrollView.contentOffset.x, offsetY); self.rowHeaderScrollView.contentOffset = CGPointMake(0, offsetY); } else if (scrollView == self.horizontalScrollView) { // 水平滚动时,同步内容区域的水平滚动 CGFloat offsetX = scrollView.contentOffset.x; self.contentScrollView.contentOffset = CGPointMake(offsetX, self.contentScrollView.contentOffset.y); } else if (scrollView == self.contentScrollView) { // 内容区域滚动时,同步主滚动视图 CGFloat offsetX = scrollView.contentOffset.x; CGFloat offsetY = scrollView.contentOffset.y; self.verticalScrollView.contentOffset = CGPointMake(self.verticalScrollView.contentOffset.x, offsetY); self.horizontalScrollView.contentOffset = CGPointMake(offsetX, self.horizontalScrollView.contentOffset.y); self.rowHeaderScrollView.contentOffset = CGPointMake(0, offsetY); } else if (scrollView == self.rowHeaderScrollView) { // 行标题滚动时,同步垂直滚动 CGFloat offsetY = scrollView.contentOffset.y; self.verticalScrollView.contentOffset = CGPointMake(self.verticalScrollView.contentOffset.x, offsetY); self.contentScrollView.contentOffset = CGPointMake(self.contentScrollView.contentOffset.x, offsetY); } // 更新固定视图的位置 [self updateFixedViewsPosition]; self.isSyncingScroll = NO; } - (void)updateFixedViewsPosition { CGFloat offsetX = self.horizontalScrollView.contentOffset.x; CGFloat offsetY = self.verticalScrollView.contentOffset.y; // 固定视图的位置应该始终在可视区域内 // 左上角:始终在 (0, 0) self.cornerView.frame = CGRectMake(0, 0, self.rowHeaderWidth, self.columnHeaderHeight); // 列标题:水平跟随滚动,垂直固定在顶部 self.columnHeaderView.frame = CGRectMake(self.rowHeaderWidth - offsetX, 0, self.columnHeaderView.frame.size.width, self.columnHeaderHeight); // 行标题滚动视图:水平固定在左侧,垂直位置由自己的contentOffset控制 self.rowHeaderScrollView.frame = CGRectMake(0, self.columnHeaderHeight, self.rowHeaderWidth, self.bounds.size.height - self.columnHeaderHeight); } -(void)setModel:(QXNobilityModel *)model{ _model = model; [self.privilegeTitles removeAllObjects]; [self.rowHeaders removeAllObjects]; for (int i = 0; i < model.nobility_power_list.count;i++ ) { QXNobilityLevel*md = model.nobility_power_list[i]; [self.privilegeTitles addObject:md.name]; self.rowCount = md.nobility_list.count; if (i == 0) { self.cornerLabel.text = md.name; [self.rowHeaders addObjectsFromArray:md.nobility_list]; } } self.currentIndex = 3; } - (void)reloadData { [self createContentViews]; [self scrollToCurrentUser]; } - (void)scrollToCurrentUser { if (self.currentIndex == -1) { return; } // self.borderView.hidden = NO; // self.borderView.x = ((self.width/4)*(self.currentIndex)); // [self.horizontalScrollView addSubview:self.borderView]; // [self.horizontalScrollView bringSubviewToFront:self.borderView]; if (self.currentIndex<3) { return; } [self.horizontalScrollView setContentOffset:CGPointMake(((self.width/4)*(self.currentIndex-2)),0 ) animated:YES]; } -(NSMutableArray *)privilegeTitles{ if (!_privilegeTitles) { _privilegeTitles = [NSMutableArray array]; } return _privilegeTitles; } -(NSMutableArray *)rowHeaders{ if (!_rowHeaders) { _rowHeaders = [NSMutableArray array]; } return _rowHeaders; } @end @implementation QXNobilityContentColView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self initSubviews]; } return self; } -(void)setModel:(QXUserNobility *)model{ if (model.type.intValue == 1) { if (model.status.intValue == 1) { self.imageView.image = [UIImage imageNamed:@"nobility_power_on"]; }else{ self.imageView.image = [UIImage imageNamed:@"nobility_power_off"]; } }else{ if ([model.nick_name_color_name isExist]) { self.titleLabel.textColor = [UIColor colorWithHexString:model.nick_name_color]; self.titleLabel.text = model.nick_name_color_name; } } } -(void)initSubviews{ self.colorView = [[UIView alloc] init]; [self.colorView addRoundedCornersWithRadius:11]; [self addSubview:self.colorView]; [self.colorView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.centerY.equalTo(self); make.width.height.mas_equalTo(22); }]; self.titleLabel = [[UILabel alloc] init]; self.titleLabel.textColor = RGB16(0xffffff); self.titleLabel.font = [UIFont systemFontOfSize:14]; self.titleLabel.textAlignment = NSTextAlignmentCenter; [self addSubview:self.titleLabel]; [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; self.imageView = [[UIImageView alloc] init]; self.imageView.contentMode = UIViewContentModeScaleToFill; [self addSubview:self.imageView]; [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.centerY.equalTo(self); make.width.height.mas_equalTo(22); }]; } @end