无法在UITableViewHeaderFooterView中布置子视图。

huangapple go评论76阅读模式
英文:

Couldn't layout Subviews in UITableViewHeaderFooterView

问题

I'm trying to make a custom header for 0 section of my UITableView. Here's the code I'm using to create this header:

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),
        ])
    }
}

Here's how I add this header in my 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")
}

Here's the result I'm getting:
无法在UITableViewHeaderFooterView中布置子视图。

As you see, image is ignoring safeArea on the screen. How to fix it?

英文:

I'm trying to make a custom header for 0 section of my UITableView. Here's the code I'm using to create this header:

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),
        ])
    }
}

Here's how I add this header in my 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")
    }

Here's the result I'm getting:
无法在UITableViewHeaderFooterView中布置子视图。

As you see, image is ignoring safeArea on the screen. How to fix it?

答案1

得分: 2

以下是您提供的代码的翻译部分:

好吧,您忽略了许多所需的代码(比如您的单元格类和控制器类)。

而且,您还没有展示您的`viewForHeaderInSection`方法的代码。

但是,我们可以看一个快速的示例...

**您的`ProfileHeaderView`稍作修改**

```swift
class ProfileHeaderView: UITableViewHeaderFooterView {
    
    // MARK: - 子视图
    
    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: - 生命周期
    
    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        setupUI()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupUI()
    }
    // MARK: - 私有
    
    private func setupUI() {
        contentView.backgroundColor = .lightGray
        addSuviews()
        setupConstraints()
    }
    
    private func addSuviews() {
        contentView.addSubview(userImage)
    }
    
    private func setupConstraints() {
        let g = contentView.layoutMarginsGuide
        
        // 这将避免自动布局的投诉
        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")
        
        // 移除默认填充
        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 nil
    }
}

当运行时看起来像这样:

无法在UITableViewHeaderFooterView中布置子视图。


请注意,我已经将HTML编码转换为适当的Swift代码。如果您需要更多帮助,请告诉我。

<details>
<summary>英文:</summary>

Well, you left out much of the needed code (such as your cell class and your controller class).

And, you haven&#39;t shown your code for `viewForHeaderInSection`.

But, we can look at a quick example...

**your `ProfileHeaderView` with minor modifications**

    class ProfileHeaderView: UITableViewHeaderFooterView {
    	
    	// MARK: - Subviews
    	
    	private var statusText: String = &quot;&quot;
    	
    	private lazy var userImage: UIImageView = {
    		//let imageView = UIImageView(image: UIImage(named: me.login))
    		let imageView = UIImageView(image: UIImage(named: &quot;face&quot;))
    
    		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),
    		])
    	}
    }
    
**a simple single-label cell class**

    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),
    		])
    	}
    }
    
**a simple view controller class with table view** - number of rows set to 30 so we can see the section header stays in place...

    class FeedViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    	
    	let feedView = UITableView()
    	
    	override func viewDidLoad() {
    		super.viewDidLoad()
    		
    		self.title = &quot;Log In&quot;
    		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: &quot;cell&quot;)
    		feedView.register(ProfileHeaderView.self, forHeaderFooterViewReuseIdentifier: &quot;ProfileHeaderView&quot;)
    		
    		// remove default padding
    		feedView.sectionHeaderTopPadding = 0.0
    
    	}
    	func numberOfSections(in tableView: UITableView) -&gt; Int {
    		return 1
    	}
    	func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
    		return 30
    	}
    	func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
    		let cell = tableView.dequeueReusableCell(withIdentifier: &quot;cell&quot;, for: indexPath) as! PostViewCell
    		cell.theLabel.text = &quot;\(indexPath)&quot;
    		return cell
    	}
    	func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -&gt; UIView? {
    		if section == 0 {
    			let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: &quot;ProfileHeaderView&quot;)
    			return v
    		}
    		// return some other header view for subsequent sections?
    		return nil
    	}
    }

Looks like this when run:

[![enter image description here][1]][1]


  [1]: https://i.stack.imgur.com/euwNU.png

</details>



# 答案2
**得分**: 0

使用 `contentView.safeAreaLayoutGuide` 作为您的布局指南。

`safeAreaLayoutGuide` 表示 bars 之间的布局区域(例如状态栏和底部导航栏之间的布局)。 

**编辑:** 更合理地设置您的表视图的约束(使用 `safeAreaLayoutGuide`),而不是对表视图中的各个视图分别进行设置。

<details>
<summary>英文:</summary>

Use `contentView.safeAreaLayoutGuide` instead of `contentView.layoutMarginsGuide` as your layout guide.

`safeAreaLayoutGuide` represents the layout area in between bars (e.g the layout between the status bar and the home bar)

**EDIT:** It probably makes more sense to correctly set the constraints of your table view (using the `safeAreaLayoutGuide`) instead of having to do that for individual views within your table view

</details>



huangapple
  • 本文由 发表于 2023年7月4日 20:33:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76612691.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定