Changing frame and contentInset of UICollectionView in layoutSubviews() using IGListKit - ios

I need to change the frame and top content inset (contentInset.top) of my collection view (which is a UICollectionView). The top inset and frame changes depend on the bounds of the superview and the content offset of the collection view, thus I put the inset-changing code in layoutSubviews().
override func layoutSubviews() {
super.layoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.contentInset.top = new_inset_top
collectionView.frame = new_frame
}
However, the collection view does not account for the new insets and the log shows the following:
The behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less than the height of the UICollectionView
minus the section insets top and bottom values, minus the content
insets top and bottom values.
How can I fix this to let the collection view displays correctly?
I am using IGListKit

You do not need to do all these inside of layoutSubviews.
in viewDidLoad implement it like following
func viewDidLoad() {
super.viewDidLoad()
// let say you want 40px top inset
collectionView.contentInset = UIEdgeInsetsMake(40, 0, 0, 0)
}
Another thing is that, i don't get, why are you setting the frame in layoutSubviews. There is no need to set frame to change the contentInset.

Related

Horizontal scroll view scrolls but only partially and bounces back to original position

I've got a horizontal scroll view with content as follows:
When I run it scrolls horizontally partially - I can scroll until about half of the red view is visible but then it bounces back.
How can I get it so that it can scroll all the way so all the red view is visible and then stays there?
I have this in the view controller, but it makes no difference if there or not.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("Content view bounds: \(contentView.bounds)")
scrollView.layoutIfNeeded()
scrollView.contentSize = contentView.bounds.size
}
In the screenshot of the storyboard there is no trailing edge constraint for the red view, however if I add one between the red view and the content view then when I run on the device it stops scrolling and looks like this:
You shouldn't need to be explicitly setting the .contentSize to begin with -- you can let auto-layout handle it all for you.
First, delete your Content View.width = width constraint:
Having that constraint told auto-layout to make your contentView only as wide as the scroll view, so you wouldn't get any horizontal scrolling. By explicitly setting the .contentSize you got some scrolling, but as you found it didn't give you what you wanted.
After deleting that constraint, add a 20-pt trailing constraint from Red View to the trailing edge of the content view:
Now, you have a complete chain of horizontal constraints...
- Blue View.leading = leading + 20
- Blue View Width
- Red View.leading = Blue View.trailing + 40
- Red View Width
- trailing = Red View.trailing + 20
This satisfies auto-layout and properly defines the width of Content View... and since Content View is constrained to leading and trailing of your Scroll View, you get correct horizontal scrolling.
No need for any code.
I got it to work by adding the following:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
scrollView.contentSize = CGSize(width: 750,height: 812)
}
But why does the above work but not the following?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("Content view bounds: \(contentView.bounds)")
scrollView.layoutIfNeeded()
scrollView.contentSize = contentView.bounds.size
}
At runtime contentView.bounds is (0.0, 0.0, 750.0, 812.0)

UITableViewCell detail text label improper layout

I have UITableViewCell subclass that insets the content view. When the content view is inset the text label is okay but the detail text label becomes out of frame and has does not adjust itself to the new content view frame.
Screen shot of cell
I have tried calling setNeedsLayout to tell the layout engine to adjust the layout of all the subviews but issue has still persisted. Below is the code to inset the content view in my UITableViewCell subclass
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets.custom)
contentView.layer.cornerRadius = CornerRadius.defaultRadius
}
enum CornerRadius {
static let defaultRadius: CGFloat = 10
}
Issue has been resloved by changing the frame then calling
super.layoutSubviews()
This has to be called after you make the changes to the frame.

UITableView height equal to contentSize height

I have a scroll view and inside of if couple labels and a tableView. I would like for that tableView to be scrolled by a outer scrollView and not the tableView's scrollView, so what I did is to set constraint for tableView height to be equal to contentSize height. But I have this problem that it is sized correctly only when push animation is completed (and viewDidLayoutSubviews gets called, I guess)
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableHeightConstraint?.constant = tableViewController.tableView.contentSize.height
}
Video Link
Content size will refresh with scroll view logic. So layout subviews is surely not enough. I have one case where I resize table view depending on it's content. What I do is use intrinsic size (it is that compression priority thing in storyboard). I subclass the table view and override these:
override var contentSize:CGSize {
didSet {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
}
The rest may then be done with constraints. Im my case I do not resize it further then it's superview but you do what you must. Still a bit of caution here: If this is not restricted you destroy the table view dequeuing feature and all the cells may be loaded instantly which may consume loads of memory and CPU. I would avoid that if possible. And if not, the next best thing is using a vertical stack view on a scroll view which should produce the same result you seem to expect.

Dynamic height TableView in a Scroll View

I am designing a page having a scroll view and above it a table view(scroll disabled). For doing this I have referred answers in this question - Make UITableView not scrollable and adjust height to accommodate all cells ,but wasn't successful.
Hierarchy of views along with provided constraints-
-Main View
-Scroll view
pinned to all sides of main view(0,0,0,0), constraint to margins
-Content View
pinned to scroll view(0,0,0,0),equal width to main view,equal height to main view(priority - 250)
-Table view inside content view
scroll disabled,having 50 point spaces from all sides,Height(>=),bottom spacing 50(relation >=).I have put greater than equal so as to increase height dynamically.
Now when I populate my table view I use the code as
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableview.dequeueReusableCellWithIdentifier("cellreuse", forIndexPath: indexPath)
cell.textLabel?.text = name[indexPath.row]
tableview.frame.size = tableview.contentSize
return cell
}
So when I run my code, it increases the tableview frame but doesn't stretch the content size and it just becomes weird as my scroll view doesn't scroll to the end of the table view neither my table view obeys the auto layout constraints.
Just I needed to do this -
remove the line - tableView.frame.size = tableView.contentSize
Add a height constraint for table view.
Set priority to High
Create an outlet of the height constraint(Ctrl+Drag).
Wherever you need to reload data of your table, set the height constraint to tableview's content height.
tableHeightConstraint.constant = tableview.contentSize.height
Assign a table height. Let it be constant 0.
Just add below lines.
tableView.heightConstant.constant = CGFloat.greatestFiniteMagnitude
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.heightConstant.constant = tableView.contentSize.height
With this, you can easily achieve dynamic table height. Working on iOS 13, Swift 5.
Had the same issue and resolved it by doing the following:
Create an outlet of the height constraint for the table view with a priority of 1000
#IBOutlet private weak var tableViewHeight: NSLayoutConstraint!
On viewDidLayoutSubview call layoutIfNeeded on the table view and then set the table view height constraint to the height of the content view
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.layoutIfNeeded()
tableViewHeight.constant = tableView.contentSize.height
}
Tested on iOS 14.1 and iOS 16.1

Can't center views in tableHeaderView with auto layout and storyboards

I can't center a view that has been placed as a subview of a UITableView's tableHeaderView in a storyboard using auto layout.
In a storyboard, I have a UITableView. I dragged a UIView (the red view) to the top, released, and it created a table header view automatically. I then dragged and dropped another UIView (the yellow view) on top of the table header view, resized, and applied some constraints to ensure it stays centered:
When I run the app on the simulator, here's what I get:
The yellow view is obviously not centered. However, the "Filter" button at the bottom is.
I know it's tricky to get the height right using auto layout and storyboards and table header views (and you can see that the height of the red view is definitely incorrect), but at this point, I'm just trying to solve for horizontally centering my yellow view.
Am I able to set this all up in my storyboard without having to configure my constraints in code?
Make sure that your UITableView has the leading, trailing, bottom, top constraints set up against its superview.
Check the table header view and all sub views have Autoresize Subviews enabled:
You can also force the table to re-render the header view by re-setting it to the same view:
[self.tableView setTableHeaderView:self.outletToHeaderView];
Update: to resize the table header view, give give it an appropriate frame in viewWillAppear:
CGRect newFrame = self.outletToHeaderView.frame;
newFrame.size.width = self.tableView.bounds.size.width;
newFrame.size.height = 44;
[self.outletToHeaderView setFrame:newFrame];
// Then reset it to force the table view to re-render/accommodate
[self.tableView setTableHeaderView:self.outletToHeaderView]
In your file TableViewHeader:
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
yourView.translatesAutoresizingMaskIntoConstraints = false
yourView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
}
You need to use prototype cell from table view, than dequeue it with reusable identifier and return it contentView. Only that's do the trick
var headerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.headerView = (self.tableView.dequeueReusableCellWithIdentifier("CustomHeaderCell") as! UITableViewCell).contentView
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return self.headerView
}
upd:
Oh sorry, my example is written in Swift. But it's easy to understand how to do the same in Obj-C

Resources