You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift:根据服务器响应动态显示/隐藏UITableView Section

问题

开发一款包含多Section、多Header View的UITableView应用,需求是根据服务器响应动态随机显示或隐藏Section(默认有6个Section,服务器返回需显示2、4或6个),但当前实现未成功,代码如下:

var allPermissionSection = ["A","B","C","D","E","F"] // default permission 

override func numberOfSections(in tableView: UITableView) -> Int {
    return totalSectionTable // total count of showing cell
} 
 
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let modelPermission = mainArrayCheckValidations[0] as! MyAcountModel
    if section == 0 {
        return 1
    }
    //  arraySectionName got from server check index if default section or server reponse index is equal to same then true this continue
    if allPermissionSection[0] == arraySectionName[0] {
            if arrayTblItems.count > 0 {
                return arrayTblItems.count
            }else if modelPermission.phoneNumbersEnabled ==  true {
                return 1
            }
    }
    if allPermissionSection[1] == arraySectionName[1] {
            if arrayTblItemsForElectronic.count > 0 {
                return arrayTblItemsForElectronic.count
            }else if modelPermission.econsentEnabled ==  true {
                return 1
            }
    }
    if allPermissionSection[2] == arraySectionName[2] {
            if arrayTblItemsForEmergnceContact.count > 0 {
                return arrayTblItemsForEmergnceContact.count
            }else if modelPermission.emergencyContactsEnabled ==  true {
                return 1
            }
    }
    return 0
}

 override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let modelPermission = mainArrayCheckValidations[0] as! MyAcountModel
    if indexPath.section == 0 {
        return UITableView.automaticDimension
    }
    if allPermissionSection[0] == arraySectionName[0] {
      //  if indexPath.section == 1 {
            if arrayTblItems.count <= 0 {
                if modelPermission.phoneNumbersEnabled == true{
                    return 40
                }else{
                    return 0
                }
            }
       // }
    }
    if allPermissionSection[1] == arraySectionName[1] {
       // if indexPath.section == 2 {
            if arrayTblItemsForElectronic.count <= 0 {
                if modelPermission.econsentEnabled == true{
                    return 40
                }else{
                    return 0
                }
            }
       // }
    }

    if allPermissionSection[2] == arraySectionName[2] {
       // if indexPath.section == 3 {
            if arrayTblItemsForEmergnceContact.count <= 0 {
                if modelPermission.emergencyContactsEnabled == true{
                    return 40
                }else{
                    return 0
                }
            }
       // }
    }
    return UITableView.automaticDimension
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if indexPath.section == 0 {
        let cell = tableView.dequeueReusableCell(withIdentifier: USER_DETAIL_CELL, for: indexPath) as! UserDetail_Cell
        cell.updateWith(mainArrayCheckValidations[indexPath.row], indexPath.row, mainArrayCheckValidations.count, tblCellType)
        return cell
    }
    if allPermissionSection[0] == arraySectionName[0] {
       // if indexPath.section == 1 {
            if arrayTblItems.count > 0 {
                let cell = tableView.dequeueReusableCell(withIdentifier: PHONE_CELL, for: indexPath) as! PhoneNumberDetailCell
                cell.updateWith(arrayTblItems[indexPath.row], indexPath.row, arrayTblItems.count, tblCellType)
                return cell
            }else{
                let cell = tableView.dequeueReusableCell(withIdentifier: DEFAULT_CELL, for: indexPath) as! Default_Custom_Cell
                cell.lbl_Name.text = ""
                return cell
            }
        //}
    }
    if allPermissionSection[1] == arraySectionName[1] {
       // if indexPath.section == 2 {
            if arrayTblItemsForElectronic.count > 0 {
                let cell = tableView.dequeueReusableCell(withIdentifier: CONSTENT_CELL, for: indexPath) as! Constent_Cell
                cell.updateWith(arrayTblItemsForElectronic[indexPath.row], indexPath.row, arrayTblItemsForElectronic.count, tblCellType)
                return cell
            }else{
                let cell = tableView.dequeueReusableCell(withIdentifier: DEFAULT_CELL, for: indexPath) as! Default_Custom_Cell
                cell.lbl_Name.text = ""
                return cell
            }
       // }
    }
    if allPermissionSection[2] == arraySectionName[2] {
      //  if indexPath.section == 3 {
            if arrayTblItemsForEmergnceContact.count > 0 {
                let cell = tableView.dequeueReusableCell(withIdentifier: EMERGENCY_CONTACT_CELL, for: indexPath) as! EmergencyCnctCell
                cell.updateWith(arrayTblItemsForEmergnceContact[indexPath.row], indexPath.row, arrayTblItemsForEmergnceContact.count, tblCellType)
                return cell
            }else{
                let cell = tableView.dequeueReusableCell(withIdentifier: DEFAULT_CELL, for: indexPath) as! Default_Custom_Cell
                cell.lbl_Name.text = ""
                return cell
            }
      //  }
    }
    return UITableViewCell()
}


 override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    if section == 0 {
        let headerView = setupHeaderView(textHeading: " Details")
        return headerView
    }
    if allPermissionSection[0] == arraySectionName[0] {
        //if section == 1 {
            let headerView = setupHeaderView(textHeading: "bers")
            return headerView
       // }
    }
    if allPermissionSection[1] == arraySectionName[1] {
      //  if section == 2 {
            let headerView = setupHeaderView(textHeading: "sent")
            return headerView
       // }
    }
    if allPermissionSection[2] == arraySectionName[2] {
      //  if section == 3 {
            let headerView = setupHeaderView(textHeading: "ontacts")
            return headerView
      //  }
    }
    return UIView()
}

 override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    if section == 0 {
        return 50
    }
    if allPermissionSection[0] == arraySectionName[0] {
     //   if section == 1 {
            return 50
     //   }
    }
    if allPermissionSection[1] == arraySectionName[1] {
      //  if section == 2 {
            return 50
      //  }
    }
    if allPermissionSection[1] == arraySectionName[1] {
      //  if section == 3 {
            return 50
       // }
    }
    return 0
}

   
  func setupHeaderView(textHeading:String) -> UIView? {
    let headerView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: tblVw.frame.width, height: 50))
    let headingLbl = UILabel()
    let topLbl = UILabel()
    let botomLbl = UILabel()
    topLbl.translatesAutoresizingMaskIntoConstraints =  false
    botomLbl.translatesAutoresizingMaskIntoConstraints =  false
    headerView.addSubview(botomLbl)
    headerView.addSubview(topLbl)
    botomLbl.leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive =  true
    botomLbl.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive =  true
    botomLbl.heightAnchor.constraint(equalToConstant:0.5).isActive = true
    botomLbl.bottomAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
    topLbl.leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive =  true
    topLbl.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive =  true
    topLbl.heightAnchor.constraint(equalToConstant:0.5).isActive = true
    topLbl.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
    botomLbl.text  = NULL_STRING
    topLbl.text = NULL_STRING
    topLbl.backgroundColor = .lightGray
    botomLbl.backgroundColor = .lightGray
    headingLbl.frame = CGRect.init(x: 0, y: 0, width: headerView.frame.width, height: headerView.frame.height)
    headingLbl.text = textHeading
    headingLbl.textColor = UIColor(hexString: "587358")
    headerView.backgroundColor = UIColor(displayP3Red: 245/256, green: 245/256, blue: 245/256, alpha: 0.6)
    headingLbl.textAlignment = .center
    headerView.addSubview(headingLbl)
    return headerView
}
问题分析

当前代码存在以下核心问题:

  • Section映射逻辑混乱:直接通过数组下标硬比对allPermissionSection和arraySectionName,未建立「当前显示Section」与「原始权限Section」的对应关系,导致Section索引错位,服务器返回部分Section时,tableView的Section索引和数据数组无法正确匹配。
  • 数据源管理缺失:未维护过滤后的有效Section列表,靠零散条件判断控制行数、高度,逻辑冗余且易出错。
  • Header/Row逻辑不匹配:viewForHeaderInSection、heightForHeaderInSection等方法未根据当前Section索引对应正确权限配置,导致Header显示错误或与Section内容脱节。
  • 强制解包风险:mainArrayCheckValidations[0] as! MyAcountModel未做空值判断,存在崩溃风险。
修正方案与实现思路

核心思路:维护一个过滤后的activeSections数组,只保留服务器允许显示的Section,所有tableView代理方法均基于该数组处理,彻底解决索引错位问题。

步骤1:定义Section模型(统一管理配置)

创建Section模型类,封装每个Section的标识、标题、数据、权限开关等信息,避免零散数组管理:

enum SectionIdentifier: String {
    case userDetails = "userDetails"
    case phoneNumbers = "phoneNumbers"
    case econsent = "econsent"
    case emergencyContacts = "emergencyContacts"
    // 补充剩余2个默认Section,如case D, E, F
}

struct TableSection {
    let identifier: SectionIdentifier
    let title: String
    var isEnabled: Bool // 由服务器响应控制
    var items: [Any] // 对应Section的行数据
    let permissionKey: KeyPath<MyAcountModel, Bool> // 权限判断的KeyPath
    let cellIdentifier: String // 对应Cell的复用ID
}

步骤2:初始化与更新数据源

在ViewController中维护原始Section列表和过滤后的活跃Section列表:

// 原始默认6个Section的配置
private var allSections: [TableSection] = [
    TableSection(identifier: .userDetails, title: "Details", isEnabled: true, items: [], permissionKey: \.userDetailsEnabled, cellIdentifier: USER_DETAIL_CELL),
    TableSection(identifier: .phoneNumbers, title: "Phone Numbers", isEnabled: false, items: [], permissionKey: \.phoneNumbersEnabled, cellIdentifier: PHONE_CELL),
    TableSection(identifier: .econsent, title: "E-Consent", isEnabled: false, items: [], permissionKey: \.econsentEnabled, cellIdentifier: CONSTENT_CELL),
    TableSection(identifier: .emergencyContacts, title: "Emergency Contacts", isEnabled: false, items: [], permissionKey: \.emergencyContactsEnabled, cellIdentifier: EMERGENCY_CONTACT_CELL),
    // 补充剩余2个默认Section
]
// 过滤后的活跃Section(只显示isEnabled为true的)
private var activeSections: [TableSection] = []
// 用户权限模型
private var accountModel: MyAcountModel?

服务器返回数据后,更新allSections的isEnabled状态,重新生成activeSections:

// 模拟服务器响应:返回需要显示的Section标识数组,如["phoneNumbers", "emergencyContacts"]
func updateSectionsFromServer(enabledSectionIds: [String]) {
    // 更新每个Section的启用状态
    allSections = allSections.map { section in
        var updatedSection = section
        updatedSection.isEnabled = enabledSectionIds.contains(section.identifier.rawValue)
        return updatedSection
    }
    // 过滤出活跃Section
    activeSections = allSections.filter { $0.isEnabled }
    // 刷新tableView
    tableView.reloadData()
}

步骤3:修正TableView代理方法

所有代理方法基于activeSections处理,彻底避免索引错位:

override func numberOfSections(in tableView: UITableView) -> Int {
    return activeSections.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let currentSection = activeSections[section]
    guard let model = accountModel else { return 0 }
    // 有数据则返回数据行数,无数据则根据权限判断是否显示默认行
    return !currentSection.items.isEmpty ? currentSection.items.count : (model[keyPath: currentSection.permissionKey] ? 1 : 0)
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let currentSection = activeSections[indexPath.section]
    guard let model = accountModel else { return 0 }
    if currentSection.items.isEmpty {
        return model[keyPath: currentSection.permissionKey] ? 40 : 0
    }
    return UITableView.automaticDimension
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let currentSection = activeSections[indexPath.section]
    if !currentSection.items.isEmpty {
        let cell = tableView.dequeueReusableCell(withIdentifier: currentSection.cellIdentifier, for: indexPath)
        // 根据Section类型更新Cell数据
        switch currentSection.identifier {
        case .userDetails:
            (cell as? UserDetail_Cell)?.updateWith(currentSection.items[indexPath.row], indexPath.row, currentSection.items.count, tblCellType)
        case .phoneNumbers:
            (cell as? PhoneNumberDetailCell)?.updateWith(currentSection.items[indexPath.row], indexPath.row, currentSection.items.count, tblCellType)
        case .econsent:
            (cell as? Constent_Cell)?.updateWith(currentSection.items[indexPath.row], indexPath.row, currentSection.items.count, tblCellType)
        case .emergencyContacts:
            (cell as? EmergencyCnctCell)?.updateWith(currentSection.items[indexPath.row], indexPath.row, currentSection.items.count, tblCellType)
        // 处理其他Section
        }
        return cell
    } else {
        // 无数据时返回默认Cell
        let cell = tableView.dequeueReusableCell(withIdentifier: DEFAULT_CELL, for: indexPath) as! Default_Custom_Cell
        cell.lbl_Name.text = ""
        return cell
    }
}

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let currentSection = activeSections[section]
    return setupHeaderView(textHeading: currentSection.title)
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 50 // 所有活跃Section的Header高度统一为50,如需特殊处理可在模型中添加height属性
}

// 修正setupHeaderView方法,改用AutoLayout适配不同屏幕
private func setupHeaderView(textHeading: String) -> UIView {
    let headerView = UIView()
    headerView.backgroundColor = UIColor(displayP3Red: 245/256, green: 245/256, blue: 245/256, alpha: 0.6)
    
    let topLine = UILabel()
    topLine.backgroundColor = .lightGray
    topLine.translatesAutoresizingMaskIntoConstraints = false
    
    let bottomLine = UILabel()
    bottomLine.backgroundColor = .lightGray
    bottomLine.translatesAutoresizingMaskIntoConstraints = false
    
    let headingLbl = UILabel()
    headingLbl.text = textHeading
    headingLbl.textColor = UIColor(hexString: "587358")
    headingLbl.textAlignment = .center
    headingLbl.translatesAutoresizingMaskIntoConstraints = false
    
    headerView.addSubview(topLine)
    headerView.addSubview(b
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 23:45:36