无法在 UITableViewHeaderFooterView 中布局子视图

Couldn't layout Subviews in UITableViewHeaderFooterView

提问人:Philipp 提问时间:7/4/2023 更新时间:7/4/2023 访问量:52

问:

我正在尝试为我的 UITableView 的 0 部分制作一个自定义标头。下面是我用来创建此标头的代码:

class ProfileHeaderView: UITableViewHeaderFooterView {
    
    // MARK: - Subviews
    
    private var statusText: String = ""
    
    private lazy var userImage: UIImageView = {
        let imageView = UIImageView(image: UIImage(named: me.login))
                
        imageView.layer.cornerRadius = 48
        imageView.clipsToBounds = true
        
        imageView.translatesAutoresizingMaskIntoConstraints = false
        
        return imageView
    }()
    
    ...
    
    // MARK: - Lifecycle
    
    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        setupUI()
        addSuviews()
        setupConstraints()
    }
     // MARK: - Private

    private func setupUI() {
        backgroundColor = .lightGray
    }
    
    private func addSuviews() {
        addSubview(userImage)
    }
    
    private func setupConstraints() {
        let layoutMarginGuide = contentView.layoutMarginsGuide
            
        NSLayoutConstraint.activate([
            userImage.leadingAnchor.constraint(equalTo: layoutMarginGuide.leadingAnchor, constant: 10),
            userImage.topAnchor.constraint(equalTo: layoutMarginGuide.topAnchor, constant: 16),
            userImage.bottomAnchor.constraint(equalTo: layoutMarginGuide.bottomAnchor, constant: -16),
            userImage.widthAnchor.constraint(equalToConstant: 90),
            userImage.heightAnchor.constraint(equalToConstant: 90),
        ])
    }
}

以下是我在 ViewController 中添加此标头的方法:

    private func setupConstraints() {
        feedView.frame = view.bounds
        
        feedView.delegate = self
        feedView.dataSource = self
        feedView.register(PostViewCell.self, forCellReuseIdentifier: "cell")
        feedView.register(ProfileHeaderView.self, forHeaderFooterViewReuseIdentifier: "ProfileHeaderView")
        //feedView.register(ProfileHeaderView.self, forHeaderFooterViewReuseIdentifier: "profileView")
    }

这是我得到的结果:Screenshot

如您所见,图像忽略了屏幕上的 safeArea。如何解决?

iOS Swift UITableView UIKite 自动布局

评论

0赞 David H 7/4/2023
您似乎没有将子视图添加到 contentView 中
0赞 Philipp 7/4/2023
@DavidH 好的,我替换了: 替换为: 但是个人资料图片仍然在屏幕截图上的位置addSubview(userImage)contentView.addSubview(userImage)
0赞 DonMag 7/4/2023
@Philipp - 您是否尝试将其用作部分标题()?还是作为表视图标题()?viewForHeaderInSectiontableVIew.tableHeaderView
0赞 Philipp 7/4/2023
@DonMag实际上我需要将其作为第 0 节的标题

答:

0赞 ProtocolGuy 7/4/2023 #1

使用而不是作为布局指南。contentView.safeAreaLayoutGuidecontentView.layoutMarginsGuide

safeAreaLayoutGuide表示栏之间的布局区域(例如,状态栏和主栏之间的布局)

编辑:正确设置表视图的约束(使用 )可能更有意义,而不必对表视图中的单个视图执行此操作safeAreaLayoutGuide

2赞 DonMag 7/4/2023 #2

好吧,您遗漏了许多所需的代码(例如单元类和控制器类)。

而且,您尚未显示 的代码。viewForHeaderInSection

但是,我们可以看一个简单的例子......

您的 ProfileHeaderView 稍作修改

class ProfileHeaderView: UITableViewHeaderFooterView {
    
    // MARK: - Subviews
    
    private var statusText: String = ""
    
    private lazy var userImage: UIImageView = {
        //let imageView = UIImageView(image: UIImage(named: me.login))
        let imageView = UIImageView(image: UIImage(named: "face"))

        imageView.layer.cornerRadius = 48
        imageView.clipsToBounds = true
        
        imageView.translatesAutoresizingMaskIntoConstraints = false
        
        return imageView
    }()
    
    //...
    
    // MARK: - Lifecycle
    
    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        setupUI()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupUI()
    }
    // MARK: - Private
    
    private func setupUI() {
        contentView.backgroundColor = .lightGray
        addSuviews()
        setupConstraints()
    }
    
    private func addSuviews() {
        contentView.addSubview(userImage)
    }
    
    private func setupConstraints() {
        let g = contentView.layoutMarginsGuide
        
        // this will avoid auto-layout complaints
        let bottomC = userImage.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -16.0)
        bottomC.priority = .required - 1
        
        NSLayoutConstraint.activate([
            userImage.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 10),
            userImage.topAnchor.constraint(equalTo: g.topAnchor, constant: 16),
            
            //userImage.bottomAnchor.constraint(equalTo: layoutMarginGuide.bottomAnchor, constant: -16),
            bottomC,
            
            userImage.widthAnchor.constraint(equalToConstant: 90),
            userImage.heightAnchor.constraint(equalToConstant: 90),
        ])
    }
}

一个简单的单标签单元格类

class PostViewCell: UITableViewCell {
    let theLabel = UILabel()
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        theLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(theLabel)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            theLabel.topAnchor.constraint(equalTo: g.topAnchor),
            theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            theLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            theLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
        ])
    }
}

一个简单的视图控制器类,带有表视图 - 行数设置为 30,因此我们可以看到部分标题保持在原位......

class FeedViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    let feedView = UITableView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "Log In"
        view.backgroundColor = .systemBackground
        
        feedView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(feedView)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            feedView.topAnchor.constraint(equalTo: g.topAnchor),
            feedView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            feedView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            feedView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
        ])
        
        feedView.delegate = self
        feedView.dataSource = self
        feedView.register(PostViewCell.self, forCellReuseIdentifier: "cell")
        feedView.register(ProfileHeaderView.self, forHeaderFooterViewReuseIdentifier: "ProfileHeaderView")
        
        // remove default padding
        feedView.sectionHeaderTopPadding = 0.0

    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 30
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PostViewCell
        cell.theLabel.text = "\(indexPath)"
        return cell
    }
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        if section == 0 {
            let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: "ProfileHeaderView")
            return v
        }
        // return some other header view for subsequent sections?
        return nil
    }
}

运行时看起来像这样:

enter image description here

评论

0赞 Philipp 7/4/2023
这绝对是我需要的!感谢您的帮助!