I have a view different UITableViewCells, depending on the content one of those cells will be loaded, however 1 cell is messing with me.
I'm trying to add it to the Table View, but my constraints are messing with me. With the translatesAutoresizingMaskIntoConstraints on true it displays fine, however I get the well known error message regarding constraints. Setting translatesAutoresizingMaskIntoConstraints to false, seems to completely mess up the layout, especially height wise.
class videoCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
playerView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func playerView() {
let height = self.frame.width * 9 / 16
let vpFrame = CGRect(x: 0, y: 0, width: self.frame.width, height: height)
let vpView = vpView(frame: vpFrame)
//vpView.translatesAutoresizingMaskIntoConstraints = false
vpView.sizeToFit()
addSubview(vpView)
vpView.leftAnchor.constraint(equalTo: leftAnchor, constant: 10).isActive = true
vpView.rightAnchor.constraint(equalTo: rightAnchor, constant: -10).isActive = true
vpView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
vpView.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
}
}
The error with translatesAutoresizingMaskIntoConstraints on false:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one
you don't want.
I also have this one set:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
The problem here is you mixing Frame-based layout rules and Autolayout rules.
A frame based layout is what you did in your code by calculating the size and the width of your vpview.
The Autolayout constraints are what you did by activating some anchor constraints for location attributes (top,bottom,left,right).
By default when you add a view, there are some prototyping constraints settled by default. That's why by default translatesAutoresizingMaskIntoConstraints is equal to true.
So if I take your code in your function playerView you are saying to the compiler :
Don't worry I compute the height for this view by myself
Then you disable your frame layout based and said to the compiler to handle this by putting anchor constraints.
RESULT => The compiler is lost obviously because the size that you compute (height, width) and your anchor constraints have conflicts.
So you have to decide by computing yourself your sizes and locations for your views OR use autolayout. Then on your error console you can list all the conflicted constraints and try to resolve them.
P.S : One advice when you want to fix left right anchor constraints, you should prefer trailing and leading anchors. Apple recommends it. And to go any furthere is the Apple documentation to learn about layout and autolayout : Apple doc
Related
Why is it that when I set tableView.rowHeight = 100 in viewDidLoad() I always get the default height value of 44.0? I tried setting tableView.estimatedHeight =100 as well but no luck, I tried setting the delegate method tableView.heightForRowAt as well but that doesn't seem to have any effect as well what so ever. So the question is: how do you set the height for a tableView Cell?
override func viewDidLoad(){ // tableView viewDidLoad
super.viewDidLoad()
tableView.rowHeight = 100
tableView.register(TickerCell.self, forCellReuseIdentifier: "Cell")
}
// Custom Cell Init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .clear
print(frame.height) // always prints 44.0
let symbolstack = UIStackView(arrangedSubviews: [symbolLabel,companyLabel])
let sectorstack = UIStackView(arrangedSubviews: [sectorLabel,exchangeLabel])
let mainStack = UIStackView(arrangedSubviews: [symbolstack,sectorstack])
sectorstack.axis = .vertical
symbolstack.axis = .vertical
sectorstack.alignment = .trailing
symbolstack.alignment = .leading
symbolstack.distribution = .fillEqually
sectorstack.distribution = .fillEqually
mainStack.distribution = .fillProportionally
addSubview(mainStack)
mainStack.translatesAutoresizingMaskIntoConstraints = false
mainStack.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
mainStack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -45).isActive = true
mainStack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 15).isActive = true
addSubview(WatchlistStar)
WatchlistStar.translatesAutoresizingMaskIntoConstraints = false
WatchlistStar.widthAnchor.constraint(equalToConstant: 15).isActive = true
WatchlistStar.leadingAnchor.constraint(equalTo: mainStack.trailingAnchor, constant: 20).isActive = true
WatchlistStar.heightAnchor.constraint(equalToConstant: 15).isActive = true
WatchlistStar.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
So the problem appears to be in the fact then in the cell init method the rowHeight I set In viewDidLoad does not seem to register, only in prepareForReuse when I print frame.height do I get 100 printed,So in which method do I setup the cell layout then?
A cell is not born knowing its height, so init is a pointless place to look at the frame. In fact, it has no inherent height. Cells are reused. They take on a height only in relation to a particular row of the table where they are being used right at the moment. That height can change when the cell is used in a different row (because rows can be different heights). So your layout needs to cope with that.
In the code you have shown, you are using autolayout. The whole point of autolayout is that you don't care about the frame of things at any one moment. Everything adjusts automatically as the surrounding frame changes. Autolayout is about relationships.
So the solution in that case is: don't look at frame.height. You don't need to know it. Just lay out the relationships between the views and the cell, and everything will be correct when the cell appears, if you have used autolayout correctly.
On the other hand, as you now say in a comment "Im forced to [use autolayout] not because I want to" — okay, so if the goal is to do layout manually, like we did before there was autolayout, then the place to do it is in the data source's cellForRowAt:. Or you could try doing it in the cell's layoutSubviews if you want the cell to lay itself out. See the old edition of my book, online, for how we used to do this: http://www.apeth.com/iOSBook/ch21.html#_custom_cells
Be very careful to distinguish between adding subviews and resizing them. You don't want to make the mistake of adding subviews that you have already added. So add the subviews in init, sure, as it is called only once, but size them in a place where the actual size of the row has been communicated to the cell.
One more piece of advice. Your current code uses the phrase addSubview, meaning self.addSubview. That is totally wrong and illegal. Never never add a subview directly to a cell. Add it only to the cell's contentView, and size it in relation to that.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableView.automaticDimension;
self.tableView.estimatedRowHeight = 40
}
I wish to have 2 labels on custom table view cell. First label should be on left 15 points away from left margin and 2nd label should be on right 15 points away from right margin. It can grow internally. Since the label is not going to display lots of data, it surely won't overlap on each other.
I am using stack view. Below are the images for my custom xib file. Number of lines for both the label is set to 1. When I launch, I see a blank cell without the labels. What is missing?
EDIT: Adding more details. I updated distribution on UIStackView to Fill Equally and updated alignment for 2nd label i.e start time label to right. I am seeing the data on the cell now, but 2nd label is not getting aligned to right.
Code in cellForRow:
let cell = tableView.dequeueReusableCell(withIdentifier: "displayStartTime") as! ScheduleDisplayStartTimeCustomCell
cell.selectionStyle = UITableViewCell.SelectionStyle.gray
cell.titleLabel.text = "Start"
cell.timeLabel.text = startTime
return cell
This is how it looks now after the edit:
Storyboard solution:
You can select the distribution for the StackView to equal spacing in the storyboard, with the default spacing value. The Labels only need the height contraint after that (or you could set the height for the StackView), and will be positioned to the sides of the StackView.
Resulting cell
The text alignment in the Label won’t matter, as the Label will be only as wide as needed.
I do not use storyboards that much but I know this works.
First you have to register the cell in your viewDidLoad:
tableView.register(ScheduleDisplayStartTimeCustomCell.self, forCellReuseIdentifier: "displayStartTime")
Then you can programmatically create a custom cell like this:
class ScheduleDisplayStartTimeCustomCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let startLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 1
label.textAlignment = .left
return label
}()
let timeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 1
label.textAlignment = .right
return label
}()
func setupView() {
addSubview(startLabel)
addSubview(timeLabel)
NSLayoutConstraint.activate([
startLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
startLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
timeLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
timeLabel.centerYAnchor.constraint(equalTo: centerYAnchor)
])
selectionStyle = UITableViewCell.SelectionStyle.gray
}
}
And finally you would set your cells like this :
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "displayStartTime") as! ScheduleDisplayStartTimeCustomCell
cell.startLabel.text = "Start"
cell.timeLabel.text = startTime
return cell
}
I have had similar issues in the past, the best solution I found it to assign a BackgroundColor to the labels (Blue, Red) and the StackView (Black). Then I see if the problem is with the constraints, or the UILabel text alignment properties.
Also, I noticed that you have an extension to UIViews, there may be something in there that is causing the problem.
Xcode 10.2, Swift 5.
I've got a UICollectionViewCell defined in a .xib file. I load that in my UIViewController (not UICollectionViewController) subclass and return it in cellForItemAt.
In my UICollectionViewCell (called TagCell), I do this:
override
func
awakeFromNib()
{
super.awakeFromNib()
self.contentView.translatesAutoresizingMaskIntoConstraints = false
self.translatesAutoresizingMaskIntoConstraints = false
self.layer.borderColor = UIColor(red: 0.354, green: 0.442, blue: 0.297, alpha: 0.74).cgColor
self.layer.borderWidth = 1.0
self.layer.cornerRadius = 4.0
}
The Nib has only the top-level UICollectionViewCell that I dropped in, and a UILabel with fixed 4-pixel constraints to all four edges. I set the text of the UILabel in cellForItemAt. However, the resulting collection is rendered with all cells at the estimated height I set in viewDidLoad():
override
func
viewDidLoad()
{
super.viewDidLoad()
let nib = UINib(nibName: "TagCell", bundle: Bundle(for: type(of: self)))
self.collectionView.register(nib, forCellWithReuseIdentifier: "TagCell")
let layout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.estimatedItemSize = CGSize(width: 60.0, height: 25.0)
}
Cell Nib File:
This seems to be anything Apple and other resources I found online tell me to do, but I must be missing something crucial. I've also tried setting the horiztontal compression resistance priority to 1000 on my UILabel, but they keep getting truncated.
I aso tried putting in a >= width constraint, but that appears to be ignored.
Before I set self.contentView.translatesAutoresizingMaskIntoConstraints, I would get the conflicting constraints warning in the debugger. But now it doesn't complain about the >= width constraint, even thought it's violating it.
So, it turns out you must do the following in your cell’s awakeFromNib():
self.contentView.translatesAutoresizingMaskIntoConstraints = false
let fixupConsts =
[
self.contentView.leftAnchor.constraint(equalTo: self.leftAnchor),
self.contentView.rightAnchor.constraint(equalTo: self.rightAnchor),
self.contentView.topAnchor.constraint(equalTo: self.topAnchor),
self.contentView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
]
NSLayoutConstraint.activate(fixupConsts)
Despite the fact that WWDC 2014 Session 226 very explicitly states the only thing you need to do is set estimatedCellSize to a non-zero value, it’s simply not enough.
If anyone can provide a better answer, I'll gladly mark it as the answer.
I was reading about auto layout rendering pipelines i mean how auto layout work under the hood. There are some methods which get called at different stages of autoLayout rendering like
layoutIfNeeded()
layoutSubviews()
updateConstraints()
updateConstraintsIfNeeded()
but i don't know which method is called when and what is the significance of that method and if i want to use auto layout then in which order i can use that methods and how can i control the autoLayout rendering pipeline
Usually you don't need to care about the autolayout method chain. You just need to create the constraints for the views to define their sizes and positions. You can add/remove, activate/deactivate constraints anytime in lifecycle of the view, but you want to always have a set of satisfiable (non-conflicting), yet complete set of constraints.
Take an example. You can tell the autolayout that button A should be 50 points wide, 20 points high, with its left top corner positioned at point (0,0) in the viewController's view. Now, this is non-conflicting, yet complete set of constraints for the button A. But lets say you want to expand that button, when the user taps it. So in the tap handler you will add one new constraint saying that the button should be 100 points wide - now you have unsatisfiable constraints - there is a constraint say it should be 50 points wide, and another one saying it shoul be 100 points wide. Therefore, to prevent conflict, before activating the new constraint, you have to deactivate the old one. Incomplete constraints is an opposite case, lets say you deactivate the old width constraint, but never activate the new one. Then autolayout can calculate position (because there are constraints defining it), and height, but not width, which usually ends in undefined behavior (now in case of a UIButton that's not true, because it has intrinsic size, which implicitly defines its width and height, but I hope you get the point).
So when you create those constraints is up to you (in my example you were manipulating them when the user tapped the button). Usually you start in initializer in case of a UIView subclass or in loadView in UIViewController subclass and there you can define and activate the default set of constraints. And then you can use handlers to react to user activity. My recommendation is prepare all the constraints in loadView, keep them in properties, and activate/deactivate them when necessary.
But there are of course some limitation as when and how not to create new constraints - for a more detailed discussion of those cases I really recommend looking at Advanced Autolayout Toolbox by objc.io.
EDIT
See following example of a simple custom SongView that uses autolayout for layout and supports also some dynamic changes in constraints by activating/deactivating them. You can just simply copy paste the whole code into a playground and test it out there, or include it in a project.
Notice there that I don't call any of the autolayout lifecycle methods, except of setNeedsLayout and layoutIfNeeded. setNeedsLayout sets a flag telling the autolayout that constraints have been changed, and layoutIfNeeded then tells it to recalculate frames. Normally, that would happen automatically, but to animate the constraints changes we need to tell it explicitly - see the setExpanded method in SongView. For more detailed explanation of using autolayout in animations, see my different answer.
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let songView = SongView()
let button = UIButton()
override func loadView() {
super.loadView()
view.backgroundColor = .white
self.view.addSubview(button)
self.view.addSubview(songView)
button.setTitle("Expand/Collapse", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(expandCollapse), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
songView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// button has intrinsic size, no need to define constraints for size, position is enough
button.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -50),
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
// songView has defined its height (see SongView class), but not width, therefore we need more constraints
songView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
songView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
songView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
])
}
#objc func expandCollapse() {
if songView.isExpanded {
songView.setExpanded(to: false, animated: true)
} else {
songView.setExpanded(to: true, animated: true)
}
}
}
class SongView: UIView {
private let numberLabel: UILabel = UILabel()
private let nameLabel: UILabel = UILabel()
private var expandedConstraints: [NSLayoutConstraint] = []
private var collapsedConstraints: [NSLayoutConstraint] = []
// this can be triggered by some event
private(set) var isExpanded: Bool = false
func setExpanded(to expanded: Bool, animated: Bool) {
self.isExpanded = expanded
if animated {
if expanded {
// setup expanded state
NSLayoutConstraint.deactivate(collapsedConstraints)
NSLayoutConstraint.activate(expandedConstraints)
} else {
// setup collapsed
NSLayoutConstraint.deactivate(expandedConstraints)
NSLayoutConstraint.activate(collapsedConstraints)
}
self.setNeedsLayout()
UIView.animate(withDuration: 0.2, animations: {
self.layoutIfNeeded()
})
} else {
// non animated version (no need to explicitly call setNeedsLayout nor layoutIfNeeded)
if expanded {
// setup expanded state
NSLayoutConstraint.deactivate(collapsedConstraints)
NSLayoutConstraint.activate(expandedConstraints)
} else {
// setup collapsed
NSLayoutConstraint.deactivate(expandedConstraints)
NSLayoutConstraint.activate(collapsedConstraints)
}
}
}
var data: (String, String)? {
didSet {
numberLabel.text = data?.0
nameLabel.text = data?.1
}
}
init() {
super.init(frame: CGRect.zero)
setupInitialHierarchy()
setupInitialAttributes()
setupInitialLayout()
}
fileprivate func setupInitialHierarchy() {
self.addSubview(numberLabel)
self.addSubview(nameLabel)
}
fileprivate func setupInitialAttributes() {
numberLabel.font = UIFont.boldSystemFont(ofSize: UIFont.preferredFont(forTextStyle: UIFontTextStyle.body).pointSize)
numberLabel.textColor = UIColor.darkGray
numberLabel.text = "0"
numberLabel.textAlignment = .right
nameLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
nameLabel.text = "NONE"
nameLabel.textAlignment = .left
self.backgroundColor = UIColor.lightGray
}
fileprivate func setupInitialLayout() {
self.translatesAutoresizingMaskIntoConstraints = false
numberLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.translatesAutoresizingMaskIntoConstraints = false
// just randomly selected different layouts for collapsed and expanded states
expandedConstraints = [
numberLabel.widthAnchor.constraint(equalToConstant: 35),
self.heightAnchor.constraint(equalToConstant: 80),
]
collapsedConstraints = [
numberLabel.widthAnchor.constraint(equalToConstant: 50),
self.heightAnchor.constraint(equalToConstant: 40),
]
// activating collapsed as default layout
NSLayoutConstraint.activate(collapsedConstraints)
NSLayoutConstraint.activate([
numberLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 4),
numberLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -4),
numberLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 4),
nameLabel.centerYAnchor.constraint(equalTo: numberLabel.centerYAnchor),
nameLabel.leftAnchor.constraint(equalTo: numberLabel.rightAnchor, constant: 8),
nameLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -4)
])
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
PlaygroundPage.current.liveView = ViewController()
I am trying to set the height of a view that is on top of my prototype cell in a table view controller. I use IB to set it's height (size inspector) and set it to 61 like so (the green view is the 'header' view):
But whenever I run the app, its' height ends up being 568.0. I have an IBOutlet called testUIView for the view in my table view controller, and I do: println("testUIView Height->\(testUIView.frame.height)") and indeed ends up being 568.0 at runtime.
Here is a screenshot showing its' height at runtime:
So my question is: How can I set the view's height so it is 61 at runtime so it indeed looks like my first screenshot (size-wise)?
I tried to set its' height property inside override func viewWillLayoutSubviews() but it did not let me assign a value to the height testUIView.frame.height = CGFloat(61.0).
Any help is appreciated! Thanks in advance!
Cheers!
Here is a solution which uses section header views rather than the actual table header view:
If you'd like to use a header for you UITableView instead you can design another prototype cell in Interface Builder, make a custom class based on a UITableViewCell and assign it to the prototype cell in interface builder on the class inspector.
Then in your controller you're going to use
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
In that function you're actually going to create a reusable cell from your table view but cast as the custom cell you made for the header. You will have access to all of it's properties like a regular UITableViewCell, then you're just going to return the cell's view
return cell.contentView
Another method you're going to use is
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 61.0
}
That one is pretty self explanatory.
Swift 3.0.1
public override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 61.0
}
Swift 3/Xcode 8:
Add this in viewDidLoad():
let HEADER_HEIGHT = 100
tableView.tableHeaderView?.frame.size = CGSize(width: tableView.frame.width, height: CGFloat(HEADER_HEIGHT))
Enjoy!
The accepted answer doesn't actually answer the question. It instead offers an alternative by using the SECTION header. This question has been answered by others but I will duplicate the answer here with a few more instructions.
Loading the view
Table views are as old as iPhones and therefore you sometimes have to force it to do what you want.
First we need to load the header and manually set its height. Otherwise the view will take more height than it needs. We do this on the viewDidLayoutSubviews callback:
lazy var profileHeaderView: ProfileHeaderView = {
let headerView = ProfileHeaderView()
return headerView
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
private func sizeHeaderToFit() {
profileHeaderView.setNeedsLayout()
profileHeaderView.layoutIfNeeded()
var frame = profileHeaderView.frame
frame.size.height = profileHeaderView.calculateHeight()
profileHeaderView.frame = frame
tableView.tableHeaderView = profileHeaderView
}
As you can see, I like to put my views inside lazy vars. This ensures that they are always created but only when I start using them.
You can also see that I'm calculating the height. In some cases, your height is fixed and therefore you can just set the frame height to a hardcoded value.
Set some priorities
We will likely see some constraint warnings appear in our debugger. This happens because the table view first forces a 0x0 size before using the size we specified above At this moment, your constraints and the height of the view are in conflict with each other.
To clear these, we simply set the constraint priorities. First you should wrap your header view components inside another view (I generally always do this for header views). This will make managing constraints much easier on your header view.
We then need to set the bottom constraint priorities to high:
containerView.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
containerView.setContentHuggingPriority(.defaultHigh, for: .vertical)
Here is a more complete example:
WARNING: Thought it is still useful as a guide for laying out your views, do not use this code if you're creating your views using nibs or storyboards.
class ProfileHeaderView: UIView {
lazy var containerView: UIView = {
let view = UIView()
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupLayout()
}
required init?(coder aDecoder: NSCoder) {
// We do this because the view is not created using storyboards or nibs.
fatalError("init(coder:) has not been implemented")
}
private func setupLayout() {
self.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
containerView.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
containerView.setContentHuggingPriority(.defaultHigh, for: .vertical)
// Set the rest of your constraints against your containerView not self and add your subviews to your containerView not self
}
}
Here is the example of the constraints set using snap-kit:
containerView.snp.makeConstraints() { make in
make.top.equalTo(self.snp.top)
make.leading.equalTo(self.snp.leading)
make.trailing.equalTo(self.snp.trailing)
make.bottom.equalTo(self.snp.bottom).priority(.high)
}
Make sure you add your constraints to the containerView not self and use containerView to add your subviews and rest of your constraints.
It has to be one of the strangest issues in iOS.
If you do just want a fixed height, as of 2019 you can:
public override func viewDidLayoutSubviews() {
var frame = tableView.tableHeaderView!.frame
frame.size.height = 68
tableView.tableHeaderView!.frame = frame
}
Strange stuff.
In swift 4.1 and Xcode 9.4.1
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad {
return 75.0
} else {
return 50.0
}
}
If you use .xib file with UIVIew for your HeaderView, you can use self-sizing header like this
override func layoutSubviews() {
super.layoutSubviews()
// Manually set the view's frame based on layout constraints.
// The parent UITableView uses the header view's frame height when laying out it's subviews.
// Only the header view's height is respected.
// The UITableView ignores the view frame's width.
// Documentation: https://developer.apple.com/documentation/uikit/uitableview/1614904-tableheaderview
frame.size = systemLayoutSizeFitting(
.init(
width: frame.size.width,
height: 0
),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel
)
}