Autolayout CollectionView - iphone

I am trying to use autolayout for a CollectionVIew, but when I run it in iPhone plus, a space is created between the cells.
iPhone 8 Plus
iPhone 8

I guess you already set the cell size from collection view's delegate method. One thing you need to do is to call invalidateLayout() whenever the collection view's frame changes, so it will re-render the cells.
IE:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}

I solved it by setting in ViewDidLoad the size of the cell (I just set the width, but you could set the height too)
let layout = YourCollectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.estimatedItemSize = CGSize(width: view.bounds.width / 2 , height: 114)

Related

reloadData() on UICollectionView change contenteOffset with dynamics cells

I just updated my app for iOS 12 and I found this problem with my collectionView. I'm using self sizing cells this way on my collectionView subclass :
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.estimatedItemSize = CGSize(width: collectionView.frame.width, height: 44)
layout.itemSize = UICollectionViewFlowLayout.automaticSize
}
When I'm calling reloadData(), the contentOffset is changing where it doesn't have to. I've tried a workaround that worked perfectly well on on iOS 11 by is overriding reloadData() :
override func reloadData() {
let oldOffset = contentOffset
super.reloadData()
layoutSubviews() // This line do the job with iOS 11.
self.setContentOffset(oldOffset, animated: false)
}
But on iOS 12 it doesn't work anymore and the contentOffset is completely broken when calling reloadData()
I've also tried to invalidateLayout() instead of layoutSubviews() but doesn't work too.
Can someone explain me why and give me a potential solution ?
Thanks

Set UITableView header view height depends on its childviews

I have a headerview in my UITableView but I can't find a solution to make it dynamically height. In my headerview I have a UITextView which height depends on its content.
What I have tried so far.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
func sizeHeaderToFit() {
let headerView = mTabView.tableHeaderView!
mTxtViewDescription.sizeToFit()
mConDescriptionHeight.constant = mTxtViewDescription.frame.height
print(mContainerView.frame.maxY)
headerView.frame.size.height += mTxtViewDescription.frame.height
}
The green area is my headerview
Changing the height of the frame of a UITableView header that's already been set won't recalculate the height, meaning you can't just change the height of a tableHeaderView as its layout is managed by the tableView. You need to set the tableHeaderView again, setting the property triggers the new calculation.
There are a number of questions going into detail about this, several linked from this one.

Determine width of UIView in Swift

I have an autolayouted UIView and need to know the width of it. How do I find the width the easiest way in Swift?
You can find the width of any view by
let width = yourView.bounds.width
If you have applied a width constraint to the view and you have an outlet for that constraint then you can find it by.
let width = yourWidthConstraint.constant
The right place to get the UIScreen frame data is in viewDidLayoutSubviews as it is called after the subViews have been laid out in screen also it is called after every time your device changes orientation such as your width will be different when your user goes into landscape mode.This is called after viewWillAppear:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let viewWidth = self.myView.bounds.width
}
Inside implementation your view you can use
override func layoutSubviews() {
super.layoutSubviews()
// you can get width of view in this function
print(self.frame.size.width)
}

Why does UITableViewCell.layoutSubviews() have frame.width as 600 (universal storyboard width) and not 320 (specific iPhone device)?

I have a project with a Universal storyboard (screen size: 600 x 600) containing a class that overrides UITableViewCell:
class MyTableCell: UITableViewCell
{
...
}
Rather than utilising a prototype cell, the UITableView that displays this cell registers MyTableCell as a class:
myTableView.registerClass( MyTableCell.self, forCellReuseIdentifier: "myCell" )
MyTableCell then overrides layoutSubviews:
override func layoutSubviews ()
{
super.layoutSubviews()
let width = CGRectGetWidth( frame )
}
Great! However, the width that is returned is the universal 600, and not the device-specific 320 that I need.
What am I doing wrong please? Thank you.
layoutSubviews() will get called at different times in the cell's lifecycle, and can have different values as it gets moved around. When you first get it off of the storyboard it will have the width you see in the storyboard. After it gets inserted in the table view it will have the same width as the table view.
Normally this isn't a problem. It will get called with the wrong size once and then again with the correct size. But it may seem confusing in the debugger.
If you never see it change to the 320 you expect, that probably means that your table view isn't configured properly for auto layout in the storyboard, and is actually drawing at 600 pixels wide at runtime.
Put a breakpoint on that method, and try entering this into the debug console to see what the table view size is. This should return lots of information about the table view, including its size.
po superview

Animate Layout Changes inside UICollectionViewCell and it's subviews having AutoLayout constraints

I'm increasing/decreasing the size of a collection view cell upon selection. The cell has subviews including a green bar, a the label "vs" and two numeric text fields. As shown on the gif/link below. The challenge is to animate the scale and also animate the subviews scale contained within the cell and maintain the layout constraints given. What is happening as shown on the video, is that the target layout jumps to it's final destination without animating the changes. On initial click, the cell "5vs0" scales nicely but the "0" textfield jumps to the far right without any animation. On second click to minimise the scale, the green bar on the left immediately resize to value equal to the textfields 5 and 0. This means that the auto layout constraints are working well with the container view (collection view cell) when it scales! The only problem is that the layout constraints do not animate along with the collection view's invalidate layout.
Video showing collection view cell scale but unintended subview jerky changes
I've set up the xib file with no auto layout warnings or errors. The collection view's layout get invalidated upon cell click, and a call to setCollectionViewLayout with animation set to true. NB after which I call a central dispatch to the main queue to call layoutIfNeeded against the collection view cell that needs changing. This doesn't seem to affect the layout animation on the subviews. Structure wise, as written on the code below, I keep a dictionary of layouts to which I toggle in between selecting a cell item.
I could manually invoke animation blocks to change the bounds of the subviews, however this is rather messy against different iOS screen sizes etc.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){
var cell = collectionView.cellForItemAtIndexPath(indexPath) as! MatchEntryCell
if (collectionView.collectionViewLayout.isEqual(self.layouts[.Standard]!)){
collectionView.setCollectionViewLayout(self.layouts[.Edit]!, animated: true)
cell.homeScore!.userInteractionEnabled = true
cell.homeScore!.becomeFirstResponder()
picker.selectRow(cell.homeScore!.text.toInt()! , inComponent: 0, animated: true)
picker.selectRow(cell.awayScore!.text.toInt()! , inComponent: 1, animated: true)
} else if (collectionView.collectionViewLayout.isEqual(self.layouts[.Edit]!)) {
collectionView.setCollectionViewLayout(self.layouts[.Standard]!, animated: true)
cell.homeScore!.resignFirstResponder()
cell.homeScore!.userInteractionEnabled = false
}
dispatch_async(dispatch_get_main_queue()) {
UIView.animateWithDuration(0.5, animations: { () -> Void in
cell.layoutIfNeeded()
})
}
}
I've added autoresizing masks with width and height as well as below for the subviews on my UICollectionViewCell subclass. But no joy! This post is related to this solution, but I have no joy! Autolayout is not resizing Uicollectionviewcell items when animating between layouts
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderWidth = 1
self.layer.cornerRadius = 10
self.layer.borderColor = UIColor.clearColor().CGColor
self.homeBar?.autoresizingMask = .FlexibleWidth | .FlexibleHeight
self.homeScore?.autoresizingMask = .FlexibleWidth | .FlexibleHeight
self.awayBar?.autoresizingMask = .FlexibleWidth | .FlexibleHeight
self.awayScore?.autoresizingMask = .FlexibleWidth | .FlexibleHeight
self.autoresizingMask = .FlexibleWidth | .FlexibleHeight
}
Anyone have any ideas or suggestions? Cheers!
I've solved this problem by using legacy springs and struts. The alignment rects don't interpolate the collection view cell's itemSize change when using auto layout, but springs and struts does.