UICollectionView Scroll Jumpy Initially - ios

I am using a UICollectionView for chat and using scroll to bottom on load. It's all working good except the first time I scroll manually it bounces up about 50-100 pt. If I continue scrolling it's fine and goes into the safe area and below the screen.
What is causing the initial bounce? It seems that it bounces off the top of the bottom safe area first, and then goes below the safe area fine if you continue scrolling.
What I'm trying to get is a nice smooth scroll initially without the weird bounce.
It seems to be caused by the fact that I am dynamically sizing image heights after they load with invalidateLayout.

I was able to solve this by luck with the following
AutoSizing cells: cell width equal to the CollectionView
// UICollectionViewController
if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
layout.estimatedItemSize = CGSize(width: view.frame.width, height: 100)
}
// UICollectionViewCell
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
// Specify you want _full width_
let targetSize = CGSize(width: layoutAttributes.frame.width, height: 200)
// Calculate the size (height) using Auto Layout
let autoLayoutSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.defaultLow)
let autoLayoutFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: autoLayoutSize)
// Assign the new size to the layout attributes
autoLayoutAttributes.frame = autoLayoutFrame
return autoLayoutAttributes
}

Related

Collection View Dynamic size (DID THE FOLLOWING BUT DIDNOT WORK)

I did what this answer said but it did not work why?
How to adjust height of UICollectionView to be the height of the content size of the UICollectionView?
but there is a problem its not resizing what am i missing out
I have also faced same issue so I did another approach. You need to add width constraint and you need to set it constant in systemLayoutSizeFitting function.
The main thing you should be careful about that is add all subviews to contentView of cell and also the last subview must has a constraint to bottom of contentView like the autoResizing TableViewCell
class ABCCell: UICollectionViewCell {
lazy var width: NSLayoutConstraint = {
let width = contentView.widthAnchor.constraint(equalToConstant: bounds.size.width)
width.isActive = true
return width
}()
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
width.constant = bounds.size.width
return contentView.systemLayoutSizeFitting(CGSize(width: targetSize.width, height: 1))
}
}

Uicollection view dynamic height but fixed width

I am working with UICollectionView I have 4 different kind of cells. Every cell is designed in xib file . when i load them in collection view it goes out of screen. i don't know why it is happening. Some cells have fixed height while some have dynamic height (depends on data coming from API). So is there any possible way to solve this issue.
i have tried Estimate size automatic to dynamic
override func awakeFromNib() {
super.awakeFromNib()
self.contentView.translatesAutoresizingMaskIntoConstraints = false
let screenwidth = UIScreen.main.bounds.size.width
widthAnchor.constraint(equalToConstant: screenwidth-20)
}
func setSize () {
let layout = mainCollection.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)}
}
try this!
let screenWidth = self.CollecionView.frame.width

Is it possible to change the height of collection view with its dynamic cell height? which delegate method should we use if possible?

I have to change the height of collection view with its dynamic cell height. cell height depends on its content and that cell height should update the height of collection view.
If you're using UICollectionViewFlowLayout then you can use self sizing cells for UICollectionView and UITableView feature available from iOS 8 and above. This feature helps in dynamically setting the height of the cell based on the content and constraints set in the cell.
There are 2 step setup you need to do for self sizing cells to work.
1. Set estimatedItemSize on UICollectionViewFlowLayout:
//for storyboard:
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width, height: 100)
}
//using code:
let flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView.init(frame: .zero, collectionViewLayout: flowLayout)
flowLayout.estimatedItemSize = CGSize.init(width: UIScreen.main.bounds.width, height: 100)
2. Use Autolayout to configure custom UICollectionView cell or implement
preferredLayoutAttributesFittingAttributes in your custom cell
var isHeightCalculated: Bool = false
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
//Exhibit A - We need to cache our calculation to prevent a crash.
if !isHeightCalculated {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var newFrame = layoutAttributes.frame
newFrame.size.width = CGFloat(ceilf(Float(size.width)))
layoutAttributes.frame = newFrame
isHeightCalculated = true
}
return layoutAttributes
}
read stackoverflow and blog for more info on how to use autolayout for self sizing cells.

Making a UILabel fit nicely into a Collection View Cell

I have collection view cells with equal width and height in a flow layout. They contain a single UILabel, which numberOfLines property is set to 0. The constraints of the label are:
The cell is made circular:
cell.layer.cornerRadius = cell.frame.width / 2
cell.clipsToBounds = true
I increase the size of each cell based on the label's text size. However, it's width and height can't be greater than 150. Here is how I determine the size of each cell:
private func estimatedFrameForText(text: String) -> CGRect {
let size = CGSize(width: 100, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17)], context: nil)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let hashLabelText = textArray[indexPath.item]
let textSize = estimatedFrameForText(text: hashLabelText)
let width = min(150, textSize.width + 20)
let height = width
return CGSize(width: width, height: height)
}
With this I get the following result (I'm showing the part where the cells' width and height are equal to 150).:
As you can see, when the cell hits its maximum possible size and the text of the labels continues to increase, at some point, the text gets off the visible part of the cells. I understand why this happens (the layout debugger shows this clearly), however I can't find a solution to the problem, yet.
What I want is that the edges of the label remain visible no matter what is the size of the cell. The text's tail can be truncated if the cell reaches its maximum size, but the text continues to increase.
I've tried to increase the space between the label and the bounds of the cells but that affects how the text looks in the smallest cells. I've also tried to change the minimum font scale of the label, but again, without a success.
You have to use UIEdgeInsets for this, Create a class for UILabel:
import UIKit
class UILabelDraw: UILabel {
override func drawText(in rect: CGRect) {
let insets: UIEdgeInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
}
}
Use this class as a Label class like below:
Output of UIEdgeInsets is below:

UICollectionView horizontal paging with space between pages

I'm looking for a way to replace the native UIPageViewController horizontal paging with a UICollectionView.
so far i did the following:
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = collectionView.frame.size
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 10
collectionView.setCollectionViewLayout(layout, animated: false)
collectionView.isPagingEnabled = true
collectionView.alwaysBounceVertical = false
this works fine and i get an horizontal paging effect.
Now i want to add horizontal space between the pages (like u will do with UIPageViewControllerOptionInterPageSpacingKey on UIPageViewController)
so far i couldn't fine a way to add the spaces without damaging the paging effect.
im looking for the same behavior as with the UIPageViewController: the cell should fill the entire screen width, and the space between the cells should only be visible when switching a page.
Solution one:
collectionView.isPagingEnabled = false
add a minimumLineSpacing for the distance between pages
implement targetContentOffsetForProposedContentOffset:withScrollingVelocity: to move the contentOffset to the closest page. You can calculate the page with simple math based on your itemSize and minimumLineSpacing, but it can take a little work to get it right.
Solution Two:
collectionView.isPagingEnabled = true
add a minimumLineSpacing for the distance between pages
the paging size is based on the bounds of the collectionView. So make the collectionView larger then then screenSize. For example, if you have a minimumLineSpacing of 10 then set the frame of the collectionView to be {0,-5, width+10, height}
set a contentInset equal to the minimumLineSpacing to make the first and last item appear correctly.
By default, UICollectionViewFlowLayout's minimumLineSpacing is 10.0, when collectionView's item is horizontally filled (itemSize.width = collectionView.bounds.width) and collectionView is paging enabled, greater than zero 'minimumLineSpacing' will cause an unintended performance: start from second page, every page has a gap which will be accumulated by page number.
It seems that we can expand collectionView's width by 'minimumLineSpacing' to fix this problem. But pratice negates this solution: When there are tow pages or more, collectionView will not give the last one a 'lineSpacing', so it's 'contentSize' is not enough to show this page's content completely, which means if the 'minimumLineSpacing' were 10.0, the last page's end would overstep the collectionView's contentSize by 10.0.
Finally, I use the following simple solution to insert a margin between every item (like UIScrollView's preformance): Expand every collectionViewItem's width by a fixed value 'pageSpacing' (such as 10.0), and expand the collectionView's width by the same value, too. And don't forget that, when layout collevtionViewCell's subviews, there's an additional spacing which is not for diplaying the real content.
Heres the code written in Swift 3.1, I'm sure it's easily understanding for you:
let pageSpacing: CGFloat = 10
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: view.frame.width + pageSpacing, height: view.frame.height)
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
let frame = CGRect(x: 0, y: 0, width: view.frame.width + pageSpacing, height: view.frame.height)
let collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
view.addSubview(collectionView)
}
}
class MyCell: UICollectionViewCell {
var fullScreenImageView = UIImageView()
override func layoutSubviews() {
super.layoutSubviews()
fullScreenImageView.frame = CGRect(x: 0, y: 0, width: frame.width - pageSpacing, height: frame.height)
}
}
Hope that can help you.

Resources