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

UITableView中sendSubviewToBack失效,如何调整单元格层级?

问题描述

我想要调整UITableView的视图层级,已经使用sendSubviewToBack和bringSubviewToFront方法,且先创建RedCell,需要确保RedCell位于BlueCell下方(RedCell中的overBgView不应遮挡BlueCell),但实际结果不符合预期:RedCell的黄色overBgView覆盖在了BlueCell上方。

相关代码

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print("<<< \(indexPath)")
        
        if indexPath.row == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: RedCell.identifier, for: indexPath) as! RedCell
            // RedCell
            tableView.sendSubviewToBack(cell)
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: BlueCell.identifier, for: indexPath) as! BlueCell
            // BlueCell
            tableView.bringSubviewToFront(cell)
            return cell
        }
    }
    
    private lazy var tableView: UITableView = {
        let table = UITableView(frame: .zero, style: .plain)
        table.estimatedRowHeight = 80
        table.showsVerticalScrollIndicator = false
        table.separatorStyle = .none
        table.delegate = self
        table.dataSource = self
        table.register(RedCell.self, forCellReuseIdentifier: RedCell.identifier)
        table.register(BlueCell.self, forCellReuseIdentifier: BlueCell.identifier)
        return table
    }()
    
    private lazy var label: UILabel = {
        let label = UILabel()
        label.text = "------split----the views below are not table cell"
        label.textColor = .black
        return label
    }()
    
    private lazy var view1: UIViewOne = {
        let view = UIViewOne()
        return view
    }()
    
    private lazy var view2: UIView = {
        let view = UIView()
        view.backgroundColor = .blue
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(RedCell.self, forCellReuseIdentifier: "redCell")
        tableView.register(BlueCell.self, forCellReuseIdentifier: "blueCell")
        setupUI()
    }
    
    public init() {
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    private func setupUI() {
        view.addSubview(tableView)
        view.addSubview(label)
        view.addSubview(view1)
        view.addSubview(view2)
        
        tableView.snp.makeConstraints { make in
            make.top.equalToSuperview().inset(200)
            make.left.right.equalToSuperview()
            make.height.equalTo(120)
        }
        
        label.snp.makeConstraints { make in
            make.top.equalTo(tableView.snp.bottom).offset(30)
            make.centerX.equalToSuperview()
            make.height.equalTo(20)
        }
        
        view1.snp.makeConstraints { make in
            make.top.equalTo(label.snp.bottom).offset(30)
            make.left.right.equalToSuperview()
            make.height.equalTo(44)
        }
        view2.snp.makeConstraints { make in
            make.top.equalTo(view1.snp.bottom)
            make.left.right.equalToSuperview()
            make.height.equalTo(44)
        }
    }
}

class RedCell: UITableViewCell {
    static let identifier = "RedCell"
    
    private lazy var overBgView: UIView = {
        let view = UIView()
        view.backgroundColor = .yellow
        return view
    }()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        backgroundColor = .red
        setupUI()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        contentView.addSubview(overBgView)
//        contentView.sendSubviewToBack(overBgView)
        self.superview?.sendSubviewToBack(overBgView)
        
        overBgView.snp.makeConstraints { make in
            make.top.left.right.equalToSuperview()
            make.height.equalTo(60)
        }
    }
}

class BlueCell: UITableViewCell {
    static let identifier = "BlueCell"
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        backgroundColor = .blue
        setupUI()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        
    }
}

class UIViewOne: UIView {
    private lazy var oversizeView: UIView = {
        let view = UIView()
        view.backgroundColor = .yellow
        return view
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .red
        addSubview(oversizeView)
        oversizeView.snp.makeConstraints { make in
            make.top.left.right.equalToSuperview()
            make.height.equalTo(60)
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
问题分析与修复方案

核心问题1:Cell初始化时操作父视图层级无效

在RedCell的setupUI方法中,self.superview?.sendSubviewToBack(overBgView)完全无效——Cell初始化阶段还未被添加到UITableView,此时superview为nil,代码不会执行。而且overBgView是添加在contentView上的,你却试图通过Cell的父视图调整它的层级,逻辑错误。

核心问题2:UITableView的Cell层级由系统自动管理,手动调整会被覆盖

UITableView会根据滚动、复用逻辑自动维护Cell的层级关系,在cellForRowAt中调用sendSubviewToBack/bringSubviewToFront的操作会被系统后续的布局逻辑覆盖,无法保证持久生效。


修复步骤

  1. 修正RedCell中overBgView的层级
    将overBgView放在contentView的最底层,直接调用contentView的层级调整方法即可:

    private func setupUI() {
        contentView.addSubview(overBgView)
        contentView.sendSubviewToBack(overBgView) // 替换原无效代码
        
        overBgView.snp.makeConstraints { make in
            make.top.left.right.equalToSuperview()
            make.height.equalTo(60)
        }
    }
    
  2. 通过layer.zPosition强制指定Cell层级
    利用图层的zPosition属性控制Cell的显示层级,数值越大越靠上,这个设置不会被UITableView的系统逻辑覆盖:
    修改cellForRowAt方法:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print("<<< \(indexPath)")
        
        if indexPath.row == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: RedCell.identifier, for: indexPath) as! RedCell
            cell.layer.zPosition = 0 // RedCell层级更低
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: BlueCell.identifier, for: indexPath) as! BlueCell
            cell.layer.zPosition = 1 // BlueCell层级更高
            return cell
        }
    }
    
  3. 移除重复的Cell注册代码
    viewDidLoad中重复注册Cell的代码无意义,直接删除:

    // 移除以下两行
    // tableView.register(RedCell.self, forCellReuseIdentifier: "redCell")
    // tableView.register(BlueCell.self, forCellReuseIdentifier: "blueCell")
    

    因为在tableView的懒加载初始化代码中已经完成了Cell注册。


最终效果

修复后,BlueCell会稳定显示在RedCell上方,RedCell的黄色overBgView不会遮挡BlueCell,符合预期需求。

内容的提问来源于stack exchange,提问作者malcopolocei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:35:54