UICollectionViewCell刷新布局的时候显示混乱

本文探讨了UICollectionView在iOS应用开发中遇到的问题,特别是在UIcollectionViewcell删除或新增时因大小不同导致的复用问题。通过具体实例展示了如何通过代码实现正确的布局调整与数据更新。

项目中有一个问题,UIcollectionView cell  删除的时候或新增的时候,因为大小不一样,复用会出现好几项问题。 


#import "OJPNewSetPreferenceController.h"
//#import "ClassSeltecterTypeView.h"
#import "OJPNewSetCollectionViewCell.h"
#import "AddWorkCollectionViewCell.h"
#import "OJPNewSetHeaderCollectionReusableView.h"
#import "OJPNewSetCollectionViewFlowLayout.h"
#import "OJPConstants_h.h"
#import "WorkMsgModel.h"
@interface OJPNewSetPreferenceController ()<UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>
@property (nonatomic,strong)UICollectionView *collectionView;

@property (nonatomic,strong) NSMutableArray *dataArray;
//@property (nonatomic,strong)ClassSeltecterTypeView *seltecterTypeView;

@end

@implementation OJPNewSetPreferenceController

- (void)viewDidLoad {
//    self.isBack = YES;
    [super viewDidLoad];
    self.navigationItem.title = @"sss";
    self.view.backgroundColor = [UIColor whiteColor];
    
    NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:@"一这是第一个类一",@"一类二",@"一三", nil];
    NSMutableArray *arr4 = [NSMutableArray arrayWithObjects:@"二第一个类",@"二类的第二个数据",@"二类数据三",@"二类四数据",@"二类五", nil];
    NSMutableArray *arr3 = [NSMutableArray array];
    [self arrayAddSomething:[NSArray arrayWithObjects:arr1,arr4,arr3, nil]];
    
    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20)];
    label.text = @"选出你关注的零工类型,第一时间获得零工动态信息";
    label.font = [UIFont systemFontOfSize:Width(10.0f)];
    label.textAlignment = NSTextAlignmentCenter;
    label.textColor = kMainGrayTextColor;
    [self.view addSubview:label];
    
    OJPNewSetCollectionViewFlowLayout *layout = [[OJPNewSetCollectionViewFlowLayout alloc]init];
//    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
    layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
    layout.minimumLineSpacing = 20;
    layout.minimumInteritemSpacing = 10;
    
    self.collectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, kNavBarHeight + Height(30), kScreenWidth - Width(30), kScreenHeight - kNavBarHeight - Height(20)) collectionViewLayout:layout];
    self.collectionView.backgroundColor = [UIColor whiteColor];

    [self.collectionView registerClass:[OJPNewSetCollectionViewCell class] forCellWithReuseIdentifier:@"OJPNewSetCollectionViewCell"];
    [self.collectionView registerClass:[AddWorkCollectionViewCell class] forCellWithReuseIdentifier:@"AddWorkCollectionViewCell"];
    [self.collectionView registerClass:[OJPNewSetHeaderCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"OJPNewSetHeaderCollectionReusableView"];
    
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self.view addSubview:self.collectionView];
    
    
//    [self.collectionView layoutSubviews];
    
    // Do any additional setup after loading the view.
}


-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    NSArray *array = self.dataArray[section];
    if (array.count<5) {
        return array.count + 1;
    }
    return 5;
}

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 3;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
     NSArray *array = self.dataArray[indexPath.section];
    if (indexPath.row == 0 && [array count]<5) {
        AddWorkCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AddWorkCollectionViewCell" forIndexPath:indexPath];
        return cell;
    }
        OJPNewSetCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"OJPNewSetCollectionViewCell" forIndexPath:indexPath];
    WorkMsgModel *model;
        if([array count]>4){
          model = array[indexPath.row];
        }else{
          model = array[indexPath.row - 1];
        }
        cell.model = model;
    return cell;
}

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
    OJPNewSetHeaderCollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"OJPNewSetHeaderCollectionReusableView" forIndexPath:indexPath];
    switch (indexPath.section) {
        case 0:
            headerView.titleLabel.text = @"企业用工";
            break;
        case 1:
            headerView.titleLabel.text = @"个人用工";
            break;
        case 2:
            headerView.titleLabel.text = @"家庭用工";
            break;
        default:
            break;
    }
    return headerView;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    NSArray *array = self.dataArray[indexPath.section];
    NSString *str;
    WorkMsgModel *model;
    if (indexPath.row == 0 && [array count]<5) {
       str = @"  +选择零工类型";
    }else{
        if([array count]>4){
            model = array[indexPath.row];
        }else{
            model = array[indexPath.row - 1];
        }
    }
    NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:13.0f]};  //指定字号
    CGRect rect = [str boundingRectWithSize:CGSizeMake(0, Height(25))/*计算宽度时要确定高度*/ options:NSStringDrawingUsesLineFragmentOrigin |
                   NSStringDrawingUsesFontLeading attributes:dic context:nil];
    if ([str isEqualToString:@"  +选择零工类型"]) {
        NSLog(@"%f",rect.size.width+Width(15)+Width(15));
        return CGSizeMake(rect.size.width+Width(15)+Width(15), 25);
    }
    NSLog(@"%f",model.www+Width(35)+Width(15));
    return CGSizeMake(model.www+Width(35)+Width(15), 25);
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section{
    return CGSizeMake(CGRectGetWidth([UIScreen mainScreen].bounds), 65);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section{
    return 20;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSMutableArray *array = self.dataArray[indexPath.section];
    if (indexPath.row == 0 && [array count]<5) {
        [self showSelectedView];
    }else{
        [self removeFromText:indexPath];
    }
}

-(void)removeFromText:(NSIndexPath*)indexPath{
   
    NSMutableArray *array = [NSMutableArray arrayWithArray:self.dataArray[indexPath.section]];
    NSLog(@"%@",array);
    NSLog(@"%@",self.dataArray);
    
    if (array.count==5) {
        [array removeObjectAtIndex:indexPath.row];
    }else{
        [array removeObjectAtIndex:indexPath.row - 1];
    }
    [self.dataArray replaceObjectAtIndex:indexPath.section withObject:array];
    [self.collectionView reloadData];
    [self.collectionView layoutIfNeeded];
}
-(void)arrayAddSomething:(NSArray *)array{
    self.dataArray = [NSMutableArray array];
    for (int i=0; i<array.count; i++) {
        NSMutableArray *oneArr = [NSMutableArray array];
        NSArray *arr = array[i];
        for (int j=0; j<arr.count; j++) {
            WorkMsgModel *model = [[WorkMsgModel alloc]init];
            model.titleStr = arr[j];
            
            NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:13.0f]};  //指定字号
            CGRect rect = [arr[j] boundingRectWithSize:CGSizeMake(0, Height(25))/*计算宽度时要确定高度*/ options:NSStringDrawingUsesLineFragmentOrigin |
                           NSStringDrawingUsesFontLeading attributes:dic context:nil];
            model.www = rect.size.width;
            [oneArr addObject:model];
        }
        [self.dataArray addObject:oneArr];
    }
}



- (void)showSelectedView{
    UIAlertView *alet = [[UIAlertView alloc]initWithTitle:@"ss" message:@"sdfsd" delegate:nil cancelButtonTitle:@"ss" otherButtonTitles: nil];
    [alet show];
    
    
//    self.seltecterTypeView = [[ClassSeltecterTypeView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight) withDirection:1 withClassType:0];
//    self.seltecterTypeView.isDiallelTable = NO;
//    self.seltecterTypeView.isShowShadow = YES;
//    [self.seltecterTypeView reloadAllTableView];
//    [self.view addSubview:self.seltecterTypeView];
//    [self.seltecterTypeView showTypeView];
//
//
//    self.seltecterTypeView.didClickSureButton = ^(BOOL flag) {
//
//    };
    
}
#import "OJPNewSetCollectionViewCell.h"
#import "OJPConstants_h.h"
@implementation OJPNewSetCollectionViewCell

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(Width(5) , 0, self.contentView.frame.size.width - Width(10), Height(25))];
        self.titleLabel.textColor = kMainLightGrayTextColor;
        self.titleLabel.layer.borderColor = kMainGrayTextColor.CGColor;
        self.titleLabel.textAlignment = NSTextAlignmentLeft;
        self.titleLabel.font = [UIFont systemFontOfSize:13.0f];
        self.titleLabel.layer.cornerRadius = 5;
        self.titleLabel.layer.masksToBounds = YES;
        self.titleLabel.layer.borderWidth = 0.6;
        [self.contentView addSubview:self.titleLabel];
        
        
//        UIView *view  = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 10, 25)];
//        view.backgroundColor = [UIColor blueColor];
//        [self.contentView addSubview:view];
        
    }
    return self;
}

-(void)setModel:(WorkMsgModel *)model{
    _model = model;
    NSMutableAttributedString *attri = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"  %@    ",model.titleStr]];
    NSTextAttachment *attch = [[NSTextAttachment alloc] init];
    attch.image = [UIImage imageNamed:@"shanchuBut.png"];
    attch.bounds = CGRectMake(0, -2.5, Width(13), Width(13));
    NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attch];
    [attri appendAttributedString:string];
    self.titleLabel.attributedText = attri;
}

- (void)setBounds:(CGRect)bounds {
    [super setBounds:bounds];
    self.contentView.frame = bounds;
}

-(void)setTitle:(NSString *)title{
   
}

-(void)layoutSubviews{
    self.titleLabel.frame = CGRectMake(Width(5) , 0, self.contentView.frame.size.width - Width(10), Height(25));
//    self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(Width(5) , 0, self.contentView.frame.size.width - Width(10), Height(25))];
}

#import "OJPNewSetCollectionViewFlowLayout.h"

@implementation OJPNewSetCollectionViewFlowLayout

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

/**
 * 用来做布局的初始化操作(不建议在init方法中进行布局的初始化操作)
 - 注意:一定要调用[super prepareLayout]
 */
- (void)prepareLayout{
    [super prepareLayout];
    // 水平滚动
//    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
//    // 设置内边距
//    CGFloat inset = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5;
//    self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
}




- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    // 获得super已经计算好的布局属性
  
    NSArray *array = [super layoutAttributesForElementsInRect:rect];
    for (int i=0; i<array.count; i++) {
        UICollectionViewLayoutAttributes *attrs = array[i];
//        attrs.representedElementKind
        if (attrs.frame.origin.x>10) {
           UICollectionViewLayoutAttributes *leftAtt = array[i-1];
            CGRect aa = attrs.frame;
            aa.origin.x = leftAtt.frame.origin.x + leftAtt.frame.size.width;
            attrs.frame = aa;
        }
    }
    
    return array;
}

/**
 * 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局
 * 一旦重新刷新布局,就会重新调用下面的方法:
 1.prepareLayout
 2.layoutAttributesForElementsInRect:方法
 */
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}

感觉最要注意的是几点

1.    [self.collectionView reloadData];

 

2. [self.collectionView layoutIfNeeded];

 

3.- (void)setBounds:(CGRect)bounds {

    [super setBounds:bounds];

    self.contentView.frame = bounds;

}

 

4.-(void)layoutSubviews{

    self.titleLabel.frame = CGRectMake(Width(5) , 0, self.contentView.frame.size.width - Width(10), Height(25));

}

 

 

import UIKit import SnapKit class DeviceListNewViewController: SurveillanceCommonTableController { // MARK: - UI Components private lazy var fixedTitleView: UIView = { let view = UIView() view.backgroundColor = .systemRed.withAlphaComponent(0.8) // 🔴 return view }() private lazy var titleLabel: UILabel = { let label = UILabel() label.text = "设备列表" label.font = UIFont.systemFont(ofSize: 20, weight: .medium) label.textColor = .white label.textAlignment = .center return label }() private lazy var tabScrollView: UIScrollView = { let scrollView = UIScrollView() scrollView.showsHorizontalScrollIndicator = true scrollView.bounces = true scrollView.alwaysBounceHorizontal = true scrollView.backgroundColor = .systemGreen.withAlphaComponent(0.5) // 🟢 return scrollView }() private let tabButtonTitles = ["所有设备", "收藏页面", "站点选择"] private var selectedTabIndex = 0 private var tabButtons: [UIButton] = [] private var collectionView: UICollectionView! private lazy var stickyContainer: UIView = { let view = UIView() view.backgroundColor = .clear return view }() // MARK: - Constraint private var stickyTopConstraint: Constraint? private var hasSetupHeader = false private let tabScrollViewHeight: CGFloat = 60 // MARK: - 👇 把 addSubViews 放进 setup 阶段,确保约束前已有父视图 override func tpbSetupSubviews() { super.tpbSetupSubviews() // ✅ 第一步:构建所有 view 层级关系(全部提前 addSubview) view.addSubview(fixedTitleView) view.addSubview(stickyContainer) view.addSubview(tableView) // 确保 tableView 是最后加的?根据你的基类逻辑调整 stickyContainer.addSubview(tabScrollView) setupTitleView() createTabButtons() setupCollectionView() // 添加按钮到 tabScrollView for button in tabButtons { tabScrollView.addSubview(button) } updateTabSelection(index: selectedTabIndex) } private func setupTitleView() { fixedTitleView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.center.equalToSuperview() make.leading.greaterThanOrEqualToSuperview().offset(20) make.trailing.lessThanOrEqualToSuperview().offset(-20) } } private func createTabButtons() { tabButtons = tabButtonTitles.enumerated().map { index, title in let btn = UIButton(type: .custom) btn.tag = index btn.setTitle(title, for: .normal) btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.setTitleColor(.black, for: .normal) btn.setTitleColor(.systemBlue, for: .selected) btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor(white: 0.85, alpha: 1.0).cgColor btn.layer.cornerRadius = 20 btn.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) btn.addTarget(self, action: #selector(onTabTapped(_:)), for: .touchUpInside) return btn } } private func setupCollectionView() { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.minimumLineSpacing = 10 layout.minimumInteritemSpacing = 10 layout.sectionInset = UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16) collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .gray collectionView.alwaysBounceVertical = true collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "DeviceCell") } // MARK: - ✅ 所有约束在此设置,此时所有 view 都已 addSubview override func tpbMakeConstraint() { // 固定标题栏 fixedTitleView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide) make.leading.trailing.equalToSuperview() make.height.equalTo(40) } // stickyContainer stickyContainer.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() make.height.equalTo(tabScrollViewHeight) self.stickyTopConstraint = make.top.equalTo(fixedTitleView.snp.bottom).constraint } // ✅ 此时 tabScrollView 已经有父视图 stickyContainer → superview 为 nil tabScrollView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)) make.height.equalTo(tabScrollViewHeight - 8) } // tableView 占据剩余空间 tableView.snp.remakeConstraints { make in make.top.equalTo(stickyContainer.snp.bottom) make.leading.trailing.bottom.equalToSuperview() } } // MARK: - Layout Buttons private func layoutTabButtonsInScrollView() { for button in tabButtons { button.snp.removeConstraints() } let buttonHeight: CGFloat = 36 let padding: CGFloat = 16 let spacing: CGFloat = 8 var lastButton: UIButton? for button in tabButtons { button.snp.makeConstraints { make in make.centerY.equalToSuperview() make.height.equalTo(buttonHeight) if let prev = lastButton { make.leading.equalTo(prev.snp.trailing).offset(spacing) } else { make.leading.equalTo(tabScrollView).offset(padding) } make.width.greaterThanOrEqualTo(80) make.width.lessThanOrEqualTo(120) } lastButton = button } if let last = lastButton { last.snp.makeConstraints { make in make.trailing.equalTo(tabScrollView).offset(-padding) } } tabScrollView.layoutIfNeeded() print("📊 tabScrollView contentSize: \(tabScrollView.contentSize)") } // MARK: - Data private func reloadData() { var tempSectionArray = [TPBTableSectionModel]() let section = TPBTableSectionModel() var cellModels = [TPBBaseTableCellModel]() let numItems = 20 let itemsPerRow: CGFloat = 2 let rowHeight: CGFloat = 80 let lineSpacing: CGFloat = 10 let numRows = ceil(CGFloat(numItems) / itemsPerRow) let collectionHeight = numRows * rowHeight + (numRows - 1) * lineSpacing let collectionCellModel = TPBBaseTableCellModel.customContent(with: collectionView) collectionCellModel.height = TPBTableElementHeight.customHeight(collectionHeight) cellModels.append(collectionCellModel) section.cellModelArray = cellModels tempSectionArray.append(section) sectionArray = tempSectionArray tableView.reloadData() } override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self collectionView.dataSource = self collectionView.delegate = self } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard view.bounds.width > 0 else { return } if !hasSetupHeader { hasSetupHeader = true layoutTabButtonsInScrollView() reloadData() } } // MARK: - ScrollView Delegate override func scrollViewDidScroll(_ scrollView: UIScrollView) { super.scrollViewDidScroll(scrollView) guard scrollView === tableView else { return } let offsetY = scrollView.contentOffset.y let fixedTitleBottom = fixedTitleView.frame.maxY if offsetY <= 0 { stickyTopConstraint?.update(offset: fixedTitleBottom) } else { let targetY = max(fixedTitleBottom, fixedTitleBottom + offsetY) stickyTopConstraint?.update(offset: targetY - offsetY) } view.layoutIfNeeded() } // MARK: - Actions @objc private func onBack() { navigationController?.popViewController(animated: true) } @objc private func onSettings() { print("Settings tapped") } @objc private func onTabTapped(_ sender: UIButton) { guard sender.tag != selectedTabIndex else { return } tabButtons[selectedTabIndex].isSelected = false selectedTabIndex = sender.tag sender.isSelected = true } private func updateTabSelection(index: Int) { for (i, btn) in tabButtons.enumerated() { btn.isSelected = (i == index) } } } // MARK: - UICollectionViewDataSource extension DeviceListNewViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 20 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DeviceCell", for: indexPath) cell.backgroundColor = .systemBlue return cell } } // MARK: - UICollectionViewDelegateFlowLayout extension DeviceListNewViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let padding: CGFloat = 32 + 10 let width = (collectionView.bounds.width > 0 ? collectionView.bounds.width : UIScreen.main.bounds.width) - padding return CGSize(width: width / 2, height: 80) } }
12-03
import UIKit import SnapKit class DeviceListNewViewController: SurveillanceCommonTableController { // MARK: - UI Components private lazy var fixedTitleView: UIView = { let view = UIView() view.backgroundColor = .systemRed.withAlphaComponent(0.8) // 🔴 return view }() private lazy var titleLabel: UILabel = { let label = UILabel() label.text = "设备列表" label.font = UIFont.systemFont(ofSize: 20, weight: .medium) label.textColor = .white label.textAlignment = .center return label }() private lazy var tabScrollView: UIScrollView = { let scrollView = UIScrollView() scrollView.showsHorizontalScrollIndicator = true scrollView.bounces = true scrollView.alwaysBounceHorizontal = true scrollView.backgroundColor = .systemGreen.withAlphaComponent(0.5) // 🟢 return scrollView }() private let tabButtonTitles = ["所有设备", "收藏页面", "站点选择"] private var selectedTabIndex = 0 private var tabButtons: [UIButton] = [] private var collectionView: UICollectionView! private lazy var stickyContainer: UIView = { let view = UIView() view.backgroundColor = .clear return view }() // MARK: - Constraint private var stickyTopConstraint: Constraint? private var hasSetupHeader = false private let tabScrollViewHeight: CGFloat = 60 // MARK: - 👇 把 addSubViews 放进 setup 阶段,确保约束前已有父视图 override func tpbSetupSubviews() { super.tpbSetupSubviews() // ✅ 第一步:构建所有 view 层级关系(全部提前 addSubview) view.addSubview(fixedTitleView) view.addSubview(stickyContainer) view.addSubview(tableView) // 确保 tableView 是最后加的?根据你的基类逻辑调整 stickyContainer.addSubview(tabScrollView) setupTitleView() createTabButtons() setupCollectionView() // 添加按钮到 tabScrollView for button in tabButtons { tabScrollView.addSubview(button) } updateTabSelection(index: selectedTabIndex) } private func setupTitleView() { fixedTitleView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.center.equalToSuperview() make.leading.greaterThanOrEqualToSuperview().offset(20) make.trailing.lessThanOrEqualToSuperview().offset(-20) } } private func createTabButtons() { tabButtons = tabButtonTitles.enumerated().map { index, title in let btn = UIButton(type: .custom) btn.tag = index btn.setTitle(title, for: .normal) btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.setTitleColor(.black, for: .normal) btn.setTitleColor(.systemBlue, for: .selected) btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor(white: 0.85, alpha: 1.0).cgColor btn.layer.cornerRadius = 20 btn.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) btn.addTarget(self, action: #selector(onTabTapped(_:)), for: .touchUpInside) return btn } } private func setupCollectionView() { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.minimumLineSpacing = 10 layout.minimumInteritemSpacing = 10 layout.sectionInset = UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16) collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .gray collectionView.alwaysBounceVertical = true collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "DeviceCell") } // MARK: - ✅ 所有约束在此设置,此时所有 view 都已 addSubview override func tpbMakeConstraint() { // 固定标题栏 fixedTitleView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide) make.leading.trailing.equalToSuperview() make.height.equalTo(40) } // stickyContainer stickyContainer.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() make.height.equalTo(tabScrollViewHeight) self.stickyTopConstraint = make.top.equalTo(fixedTitleView.snp.bottom).constraint } // ✅ 此时 tabScrollView 已经有父视图 stickyContainer → superview 为 nil tabScrollView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)) make.height.equalTo(tabScrollViewHeight - 8) } // tableView 占据剩余空间 tableView.snp.remakeConstraints { make in make.top.equalTo(stickyContainer.snp.bottom) make.leading.trailing.bottom.equalToSuperview() } } // MARK: - Layout Buttons private func layoutTabButtonsInScrollView() { for button in tabButtons { button.snp.removeConstraints() } let buttonHeight: CGFloat = 36 let padding: CGFloat = 16 let spacing: CGFloat = 8 var lastButton: UIButton? for button in tabButtons { button.snp.makeConstraints { make in make.centerY.equalToSuperview() make.height.equalTo(buttonHeight) if let prev = lastButton { make.leading.equalTo(prev.snp.trailing).offset(spacing) } else { make.leading.equalTo(tabScrollView).offset(padding) } make.width.greaterThanOrEqualTo(80) make.width.lessThanOrEqualTo(120) } lastButton = button } if let last = lastButton { last.snp.makeConstraints { make in make.trailing.equalTo(tabScrollView).offset(-padding) } } tabScrollView.layoutIfNeeded() print("📊 tabScrollView contentSize: \(tabScrollView.contentSize)") } // MARK: - Data private func reloadData() { var tempSectionArray = [TPBTableSectionModel]() let section = TPBTableSectionModel() var cellModels = [TPBBaseTableCellModel]() let numItems = 20 let itemsPerRow: CGFloat = 2 let rowHeight: CGFloat = 80 let lineSpacing: CGFloat = 10 let numRows = ceil(CGFloat(numItems) / itemsPerRow) let collectionHeight = numRows * rowHeight + (numRows - 1) * lineSpacing let collectionCellModel = TPBBaseTableCellModel.customContent(with: collectionView) collectionCellModel.height = TPBTableElementHeight.customHeight(collectionHeight) cellModels.append(collectionCellModel) section.cellModelArray = cellModels tempSectionArray.append(section) sectionArray = tempSectionArray tableView.reloadData() } override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self collectionView.dataSource = self collectionView.delegate = self } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard view.bounds.width > 0 else { return } if !hasSetupHeader { hasSetupHeader = true layoutTabButtonsInScrollView() reloadData() } } // MARK: - ScrollView Delegate override func scrollViewDidScroll(_ scrollView: UIScrollView) { super.scrollViewDidScroll(scrollView) guard scrollView === tableView else { return } let offsetY = scrollView.contentOffset.y let fixedTitleBottom = fixedTitleView.frame.maxY if offsetY <= 0 { stickyTopConstraint?.update(offset: fixedTitleBottom) } else { let targetY = max(fixedTitleBottom, fixedTitleBottom + offsetY) stickyTopConstraint?.update(offset: targetY - offsetY) } view.layoutIfNeeded() } // MARK: - Actions @objc private func onBack() { navigationController?.popViewController(animated: true) } @objc private func onSettings() { print("Settings tapped") } @objc private func onTabTapped(_ sender: UIButton) { guard sender.tag != selectedTabIndex else { return } tabButtons[selectedTabIndex].isSelected = false selectedTabIndex = sender.tag sender.isSelected = true } private func updateTabSelection(index: Int) { for (i, btn) in tabButtons.enumerated() { btn.isSelected = (i == index) } } } // MARK: - UICollectionViewDataSource extension DeviceListNewViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 20 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DeviceCell", for: indexPath) cell.backgroundColor = .systemBlue return cell } } // MARK: - UICollectionViewDelegateFlowLayout extension DeviceListNewViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let padding: CGFloat = 32 + 10 let width = (collectionView.bounds.width > 0 ? collectionView.bounds.width : UIScreen.main.bounds.width) - padding return CGSize(width: width / 2, height: 80) } }
12-03
了 我发给你所有代码。给出我所有修改: // // DeviceListView.swift // SurveillanceHome // // Created by MaCong on 2025/12/3. // Copyright © 2025 tplink. All rights reserved. // // DeviceListView.swift class DeviceListView: UIView { private let collectionView = UICollectionView( frame: .zero, collectionViewLayout: Self.createLayout() ) // ✅ 使用真实设备数组 var devices: [TPSSDeviceForDeviceList] = [] { didSet { collectionView.reloadData() invalidateIntrinsicContentSize() } } // ✅ 回调携带真实设备 + 通道 var onMoreButtonTap: ((TPSSDeviceForDeviceList, TPSSChannelInfo?) -> Void)? override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder: NSCoder) { super.init(coder: coder) setup() } private static func createLayout() -> UICollectionViewLayout { let layout = UICollectionViewFlowLayout() layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) layout.minimumLineSpacing = 10 return layout } private func setup() { collectionView.backgroundColor = .clear collectionView.showsVerticalScrollIndicator = false collectionView.register(DeviceListCell.self, forCellWithReuseIdentifier: "DeviceListCell") collectionView.delegate = self collectionView.dataSource = self addSubview(collectionView) collectionView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ collectionView.topAnchor.constraint(equalTo: topAnchor), collectionView.leadingAnchor.constraint(equalTo: leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: trailingAnchor), collectionView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) translatesAutoresizingMaskIntoConstraints = false } } // MARK: - UICollectionView DataSource & Delegate extension DeviceListView: UICollectionViewDataSource, UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return devices.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DeviceListCell", for: indexPath) as! DeviceListCell let device = devices[indexPath.item] // 假设每个设备对应一个主通道(或根据业务逻辑传入具体 channel) let mainChannel: TPSSChannelInfo? = device.channels.first // 示例逻辑 cell.configure(with: device, channel: mainChannel) cell.onMoreTap = { [weak self] channel in self?.onMoreButtonTap?(device, channel) } return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = collectionView.bounds.width > 0 ? collectionView.bounds.width : UIScreen.main.bounds.width let itemWidth = width - 32 let itemHeight = itemWidth * 9 / 16 + 60 return CGSize(width: itemWidth, height: itemHeight) } } // MARK: - intrinsicContentSize 自动高度 extension DeviceListView { override var intrinsicContentSize: CGSize { calculateRequiredHeight() } override func invalidateIntrinsicContentSize() { super.invalidateIntrinsicContentSize() if let tableView = findParentTableView() { DispatchQueue.main.async { tableView.beginUpdates() tableView.endUpdates() } } } private func calculateRequiredHeight() -> CGFloat { guard !devices.isEmpty else { return 150 } let width = collectionView.frame.width > 0 ? collectionView.frame.width : UIScreen.main.bounds.width let itemWidth = width - 32 let itemHeight = itemWidth * 9 / 16 + 60 let lineSpacing: CGFloat = 10 let totalHeight = CGFloat(devices.count) * itemHeight + lineSpacing * CGFloat(devices.count - 1) return max(totalHeight, 150) } private func findParentTableView() -> UITableView? { var parent = superview while parent != nil { if let tableView = parent as? UITableView { return tableView } parent = parent?.superview } return nil } } // // DeviceListCell.swift // SurveillanceHome // // Created by MaCong on 2025/12/3. // Copyright © 2025 tplink. All rights reserved. // class DeviceListCell: UICollectionViewCell { @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var thumbnailImageView: UIImageView! @IBOutlet weak var statusLabel: UILabel! @IBOutlet weak var moreButton: UIButton! var onMoreTap: ((TPSSChannelInfo?) -> Void)? private var currentDevice: TPSSDeviceForDeviceList! private var selectedChannel: TPSSChannelInfo? override func awakeFromNib() { super.awakeFromNib() setupUI() } private func setupUI() { layer.cornerRadius = 8 clipsToBounds = true } func configure(with device: TPSSDeviceForDeviceList, channel: TPSSChannelInfo? = nil) { self.currentDevice = device self.selectedChannel = channel nameLabel.text = device.deviceName ?? "Unknown Device" statusLabel.text = (device.onlineStatus == .online) ? "在线" : "离线" statusLabel.textColor = (device.onlineStatus == .online) ? .systemGreen : .systemRed // 加载缩略图 if let url = device.snapshotUrl { thumbnailImageView.kf.setImage(with: url) } else { thumbnailImageView.image = UIImage(systemName: "camera") } // 清理旧事件 moreButton.removeTarget(nil, action: nil, for: .allEvents) moreButton.addTarget(self, action: #selector(moreButtonTapped), for: .touchUpInside) } @objc private func moreButtonTapped() { onMoreTap?(selectedChannel) } } // // DeviceListNewViewController.swift // SurveillanceHome // // Created by MaCong on 2025/12/3. // Copyright © 2025 tplink. All rights reserved. // import UIKit import SnapKit // MARK: - DeviceListNewViewController class DeviceListNewViewController: SurveillanceCommonTableController, SelectOrganizationViewDelegate { // MARK: - 属性 private lazy var titleView: NewTitleView = { let view = NewTitleView() view.titleText = "设备列表" view.didClickCallback = { [weak self] _ in guard let self = self else { return } let selectOrgView = self.selectOrganizationView let titleView = self.titleView if titleView.isAnimating { return } if !titleView.isInExpandStatus { titleView.isAnimating = true self.view.addSubview(selectOrgView) selectOrgView.show(animated: true) { titleView.isAnimating = false } } else { titleView.isAnimating = true self.hideLoadingView() selectOrgView.dismiss(animated: true) { titleView.isAnimating = false } } // 更新箭头状态 titleView.changeToStatus(expand: !titleView.isInExpandStatus) } return view }() private lazy var multiScreenButton: UIButton = { let btn = UIButton(type: .custom) btn.setImage(TPImageLiteral("media_player_switch_multi_live"), for: .normal) btn.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) btn.addTarget(self, action: #selector(clickMultileLive), for: .touchUpInside) return btn }() var selectedTabType: DeviceListMasterSelectedType = .all { didSet { UserDefaults.standard.deviceListSelectedType = selectedTabType forceRefreshDeviceList() } } var selectedSiteInfo: TPSSVMSSubsiteInfo? { didSet { saveSelectedSiteInfo(selectedSiteInfo) forceRefreshDeviceList() } } private func saveSelectedSiteInfo(_ info: TPSSVMSSubsiteInfo?) { guard let siteInfo = info else { try? FileManager.default.removeItem(at: URL(fileURLWithPath: DeviceListMasterViewController.getDocumentsPath(path: DeviceListMasterViewController.kSelectedSiteFileName) ?? "")) return } do { let data = try NSKeyedArchiver.archivedData(withRootObject: siteInfo, requiringSecureCoding: true) try data.write(to: URL(fileURLWithPath: DeviceListMasterViewController.getDocumentsPath(path: DeviceListMasterViewController.kSelectedSiteFileName) ?? "")) } catch { print(error) } } private lazy var selectOrganizationView: SelectOrganizationView = { let view = SelectOrganizationView() view.rootViewController = self view.delegate = self view.frame = CGRect( x: 0, y: SelectOrganizationView.statusBarHeight + SelectOrganizationView.navigationBarHeight, width: screenWidth, height: screenHeight - SelectOrganizationView.statusBarHeight - SelectOrganizationView.navigationBarHeight ) return view }() // MARK: - 设备数据与视图 private var deviceModels: [NewListDeviceModel] = [] { didSet { deviceListView.devices = deviceModels } } private lazy var deviceListView: DeviceListView = { let view = DeviceListView() view.onMoreButtonTap = { [weak self] device, channel in self?.showDeviceMenu(for: device, channel: channel) } return view }() // MARK: - 生命周期 override func viewDidLoad() { super.viewDidLoad() setNavigation() tableView.contentInsetAdjustmentBehavior = .automatic reloadData() // 初始加载设备 loadDevicesFromContext() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(false, animated: false) } // MARK: - 子视图与约束 override func tpbSetupSubviews() { super.tpbSetupSubviews() } override func tpbMakeConstraint() { tableView.snp.remakeConstraints { make in make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top) make.leading.trailing.equalToSuperview() make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom) } } // MARK: - 创建 UI 组件 private func setNavigation(){ navigationItem.titleView = titleView titleView.snp.makeConstraints { make in make.width.lessThanOrEqualTo(200) make.height.equalTo(44) } let backButtonItem = self.tpbCreateLeftBarButtonItem(with: TPImageLiteral("common_light_back_nor"), andTarget: self, andAction: #selector(jumpToXXX)) let centralButtonItem = self.tpbCreateLeftBarButtonItem(with: TPImageLiteral("central_surveillance_button"), andTarget: self, andAction: #selector(centralButtonClicked)) let messageButtonItem = self.tpbCreateLeftBarButtonItem(with: TPImageLiteral("tabbar_message_nor"), andTarget: self, andAction: #selector(onMessageButtonTapped)) navigationItem.leftBarButtonItems = [backButtonItem, centralButtonItem] navigationItem.rightBarButtonItem = messageButtonItem } // MARK: - 创建组件:设备数量视图 private func createDeviceCountView() -> UIView { let containerView = UIView() containerView.backgroundColor = .tpbCard containerView.layer.cornerRadius = 4 containerView.clipsToBounds = true let labels = ["NVR", "4K", "2K", "HD"] var categoryViews: [UIView] = [] for text in labels { let categoryView = UIView() categoryView.backgroundColor = UIColor(white: 0.93, alpha: 1.0) categoryView.layer.cornerRadius = 8 categoryView.clipsToBounds = true let label = UILabel() label.text = text label.textColor = UIColor.tpbTextPrimary label.font = UIFont.systemFont(ofSize: 15, weight: .semibold) label.textAlignment = .center categoryView.addSubview(label) label.snp.makeConstraints { make in make.edges.equalToSuperview().inset(10) } containerView.addSubview(categoryView) categoryViews.append(categoryView) } for (index, view) in categoryViews.enumerated() { view.snp.makeConstraints { make in make.height.equalTo(60) make.centerY.equalTo(containerView) if index == 0 { make.leading.equalTo(containerView).offset(20) } else { make.leading.equalTo(categoryViews[index - 1].snp.trailing).offset(16) make.width.equalTo(categoryViews[0]) } if index == labels.count - 1 { make.trailing.equalTo(containerView).offset(-20) } } } return containerView } // MARK: - 创建组件:存储空间视图 private func createStorageUsageView() -> UIView { let containerView = UIView() containerView.backgroundColor = .tpbCard containerView.layer.cornerRadius = 8 containerView.clipsToBounds = true let titleLabel = UILabel() titleLabel.text = "存储空间" titleLabel.font = UIFont.systemFont(ofSize: 14, weight: .medium) titleLabel.textColor = .tpbTextPrimary containerView.addSubview(titleLabel) let progressView = UIProgressView(progressViewStyle: .default) progressView.progressTintColor = UIColor.systemBlue progressView.trackTintColor = UIColor(white: 0.9, alpha: 1.0) containerView.addSubview(progressView) let detailLabel = UILabel() detailLabel.font = UIFont.systemFont(ofSize: 13, weight: .regular) detailLabel.textColor = .tpbTextPrimary detailLabel.textAlignment = .center containerView.addSubview(detailLabel) let used = 1.2 let total = 5.0 let percent = Float(used / total) progressView.setProgress(percent, animated: false) let percentage = Int(percent * 100) detailLabel.text = String(format: "%.1f TB / %.1f TB (%d%%)", used, total, percentage) titleLabel.snp.makeConstraints { make in make.top.leading.equalTo(containerView).offset(16) } progressView.snp.makeConstraints { make in make.centerX.centerY.equalTo(containerView) make.width.equalTo(200) make.height.equalTo(6) } detailLabel.snp.makeConstraints { make in make.top.equalTo(progressView.snp.bottom).offset(8) make.centerX.equalTo(progressView) } return containerView } // MARK: - 创建组件:设备列表容器 Cell private func createDeviceListCell() -> TPBBaseTableCellModel { let containerView = UIView() containerView.backgroundColor = .tpbBackground let paddedView = UIView() paddedView.backgroundColor = .clear paddedView.layer.cornerRadius = 3 paddedView.clipsToBounds = true containerView.addSubview(paddedView) paddedView.addSubview(deviceListView) // 布局约束 paddedView.snp.makeConstraints { make in make.edges.equalTo(containerView).inset(UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)) } deviceListView.snp.makeConstraints { make in make.edges.equalTo(paddedView) make.height.greaterThanOrEqualTo(1) } // 使用 deviceListView 作为内容视图 let cellModel = TPBBaseTableCellModel.customContent(with: deviceListView) // ✅ 正确设置高度:使用 OC 工厂方法 cellModel.height = TPBTableElementHeight.automatic() return cellModel } // MARK: - 创建组件:多屏按钮作为 Header View private func createMultiScreenHeader() -> UIView { let headerView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 44)) headerView.backgroundColor = .clear let container = TPBBaseView() container.backgroundColor = .clear container.layer.cornerRadius = 3 container.clipsToBounds = true headerView.addSubview(container) container.snp.makeConstraints { make in make.trailing.equalTo(headerView).offset(-16) make.centerY.equalTo(headerView) make.size.equalTo(CGSize(width: 44, height: 44)) } multiScreenButton.removeFromSuperview() container.addSubview(multiScreenButton) multiScreenButton.snp.makeConstraints { make in make.edges.equalToSuperview() } return headerView } // MARK: - 创建合并后的 Section(Header + Cell) private func createMergedDeviceSection() -> TPBTableSectionModel { let section = TPBTableSectionModel() section.customHeaderView = createMultiScreenHeader() section.sectionHeaderHeight = TPBTableElementHeight.customHeight(44) section.cellModelArray = [createDeviceListCell()] return section } // MARK: - 刷新数据 private func reloadData() { var tempSectionArray = [TPBTableSectionModel]() // Section 0: 设备数量 let section0 = TPBTableSectionModel() let deviceCountView = createDeviceCountView() let deviceCountCellModel = TPBBaseTableCellModel.customContent(with: deviceCountView) deviceCountCellModel.height = TPBTableElementHeight.customHeight(100) section0.cellModelArray = [deviceCountCellModel] tempSectionArray.append(section0) // Section 1: 存储空间 let section1 = TPBTableSectionModel() let storageView = createStorageUsageView() let storageCellModel = TPBBaseTableCellModel.customContent(with: storageView) storageCellModel.height = TPBTableElementHeight.customHeight(100) section1.cellModelArray = [storageCellModel] tempSectionArray.append(section1) // Section 2: 合并后的设备列表 let section2 = createMergedDeviceSection() tempSectionArray.append(section2) sectionArray = tempSectionArray tableView.reloadData() } // MARK: - Actions @objc private func clickMultileLive() { print("Multi-Screen 按钮被点击") } @objc private func jumpToXXX() { print("跳转到【待定】页面") } @objc private func centralButtonClicked() { print("跳转到【中心监控】页面") let centralJumpVC = CentralJumpViewController() let centralJumpNaviVC = BaseNavigationController(rootViewController: centralJumpVC) centralJumpNaviVC.view.frame = self.view.frame centralJumpNaviVC.view.backgroundColor = .clear centralJumpVC.currentSiteId = selectedTabType == .all ? nil : selectedSiteInfo?.siteId centralJumpVC.maskDidClickBlock = { centralJumpNaviVC.view.removeFromSuperview() } UIApplication.shared.keyWindow?.addSubview(centralJumpNaviVC.view) } @objc private func onMessageButtonTapped() { print("消息按钮被点击") } // MARK: - 加载视图控制 func hideLoadingView() { titleView.hideLoadingAnimation() } // MARK: - SelectOrganizationViewDelegate func didSelectOrganization(_ organization: OrganizationModel) { titleView.titleText = organization.name hideLoadingView() reloadData() } func didCancelSelectOrganization() {} // MARK: - 设备数据管理 private func loadDevicesFromContext() { DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } let devices = TPAppContextFactory.shared().devices(inDeviceGroup: "default", includingHiddenChannels: false) let filteredDevices = devices.filter { $0.listType == .remote && $0.displayOnline } let models = filteredDevices.map { NewListDeviceModel(device: $0) } DispatchQueue.main.async { self.deviceModels = models } } } private func forceRefreshDeviceList() { loadDevicesFromContext() } // MARK: - 弹出菜单 private func showDeviceMenu(for device: TPSSDeviceForDeviceList, channel: TPSSChannelInfo? = nil) { let menu = DeviceListMenuView(frame: .zero) // 配置菜单项显示逻辑 DeviceListMenuView.configure(device: device, channel: channel) // 设置点击回调 menu.action = { [weak self] (item, device, channel) in switch item { case .setting: self?.jumpToDeviceSetting(for: device, channel: channel) case .alarmMode: self?.toggleNotification(for: device, channel: channel) case .upgrade: self?.startFirmwareUpgrade(for: device, channel: channel) case .unbind: self?.confirmUnbindDevice(for: device, channel: channel) case .collect: self?.toggleFavoriteStatus(for: device, channel: channel) @unknown default: assertionFailure("Unhandled menu item: \(item)") } } // 弹出菜单,忽略返回值(或保存) _ = presentGuideWith( viewToPresent: menu, size: CGSize(width: 200, height: menu.items.preferredheight), source: self.multiScreenButton ) } private func jumpToDeviceSetting(for device: NewListDeviceModel) { print("跳转到设备设置: \(device.alias)") // 实际跳转逻辑 } private func toggleNotification(for device: NewListDeviceModel) { print("切换通知: \(device.alias)") // 调用 API } }
最新发布
12-06
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值