UICollectionView custom cell and make horizontal scroll - ios

I'm a newbie of IOS app develop. Now a day , I want to make a screen likes below picture.
How to make it?
I'm thinking about using UICollectionView but I don't know how to customize it?

You have implemented method named: "sizeForItemAt", it is used to set the size of every cell of your collection view. And your condition is satisfied because of bidirectional scrolling property of collection view. If you change the size of cell, than this may be worst for your UI.

Follow the steps:-
Select collection view from storyboard (if using).
Than go to Attributes Inspector.
And set the Scroll Direction Vertical to Horizontal.
(Vertical is default)

I can do that now by below workaround
class ViewController: UIViewController, UICollectionViewDelegateFlowLayout{
...
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 40.0, height: 500.0)
}
}

You can do it after seeing tutorial of content offset and content size of collection view. This two parameter help you to check for overlapped cell.
Check content offset of every cell with the x origin of green view.

create cocoa touch file.
step 1
step 2
Now you will have two files created.
use xib to design your UI and CollectionViewCell class to connect xib's outlets.
Now in the main view controller where you are confirming UIcollectionViewDelegate and UIcollectionViewDataSource register the xib for an identifier.
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: Bundle.main), forCellWithReuseIdentifier: "yourIdentifier")
you can use this yourIdentifer to deque item for collection view in ItemForRowAtIndexPath.
Now to Change the Scroll Direction Property. Below is the image you can refer
Or programatically you can change it by following piece of code.
if let layout = self.yourCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}

Related

Why do the cells have the same width as the label?

I have a simple UICollectionViewController in my Storyboard, with custom cells with a single UILabel inside of them.
When I run the project with mock data the cells have the same width as the labels even though I made the controller conform to UICollectionViewDelegateFlowLayout and added the following code:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.collectionView.frame.width / 2.5, height: 100)
}
Does anyone know what the problem is?
In the storyboard, select the collection view and switch to the Size inspector. You will see something like this:
Change Automatic to None.
if you are using Storyboards, try giving Label inside a view and give leading as well as trailing constraints accordingly.
hope it solves your problem.

Why on Xcode 11, UICollectionViewCell changes size as soon as you scroll (I already set size in sizeForItem AtIndexPath:)?

I have collectionview inside tableview cell and I use nib for my collection view cell (in which I use autolayout for my imageview and labels and it is on Freeform mode). I'm setting cell size in tableviewcell class which is handling the delegate for collectionview inside it by this method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 200, height :150)
}
It always works but after Xcode 11 it doesn't.
I have the same problem. And my solution is to change the Estimate size to None in the Xcode 11.
You set collectionview Estimate size to None in Xcode 11. The reason for this is that cells in a UICollectionView can now self-size with Auto Layout constrained views in the canvas. To opt into the behavior for existing collection views, enable “Automatic” for the collection view’s estimated size, and “Automatic” for cell’s size from the Size inspector.
If deploying before iOS 13, you can activate self sizing collection view cells by calling performBatchUpdates(_:completion:) during viewDidLoad()
Ref:
https://developer.apple.com/documentation/xcode_release_notes/xcode_11_release_notes
As Anh Tuan said in another answer here, you just need to change the Estimate Size to none in the Size Inspector of the Collection View from the Storyboard.
But if you wanna do this programmatically, you can try this code:
let layout = myCollectionViewReferenceHere.collectionViewLayout as! UICollectionViewFlowLayout
layout.estimatedItemSize = .zero
This problem is coming in Xcode 11. Go to the attribute inspector and change the estimateSize to None. will fix every thing.

CollectionView in ScrollView

I am working on an iOS app where I want to show a UIScrollView with two UILabels an UIImageView and a UICollectionView at the bottom. Now I don't know how to do this in auto layout, who could help me out?
I've tried just adding everything and setting the constraints to each other, but I do remember that the UIScrollView needs to calculate the intrinsic content height. How can I create this scroll view?
I would avoid embedding the UICollectionView into a UIScrollView in this case. It will be easier to add the UILabels and UIImageView into a section header using a UICollectionReusableView subclass.
Here are the steps:
Add a section header to the UICollectionView:
Create your section header view class by subclassing UICollectionReusableView. Set custom class and reuse identifier of the header reusable view:
Lay out your header view. Connect your outlets.
Implement the viewForSupplementaryElementOfKind method.
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
// If you also use footers:
// use a switch statement on the 'kind' argument to
// decide which view to dequeue.
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "Header", for: indexPath)
// set up your header view
return view
}

UICollectionReusableView header possible bug

I have a custom UICollectionReusableView with a single label inside: the label has leading, trailing, top and bottom constraints set to 8.
On the controller, I register the cell and then:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return self.sectionSizeForIndex(section)
}
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
let cell = self.collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "cellID", forIndexPath: indexPath) as! FooCollectionViewCell
cell.setupLabelWithText("Lorem ipsum")
return cell
}
For debugging reasons, I show both the borders of the cell and the borders of the inner label, with different colors.
Here's what happens: The cell got the right frame, but the inner label seems not update constraints according to its parent view (cell).
On "Debug View Hierarchy", I see that also cell.contentView doesn't update itself -> I suppose that is this the reason why the label doesn't update.
In cell awake from nib:
override func awakeFromNib() {
super.awakeFromNib()
self.label.numberOfLines = 0
self.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.translatesAutoresizingMaskIntoConstraints = true
}
add this inside viewForSuplementary
switch kind {
case UICollectionElementKindSectionHeader:
let cell = self.collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "cellID", forIndexPath: indexPath) as! FooCollectionViewCell
cell.setupLabelWithText("Lorem ipsum")
return cell
default: break
}
this will let you collectionView know that you actually are using a header and not a footer or other type of view kind
The problem may be that you are using an instance of UICollectionViewCell for a supplementary view instead of the usual custom subclass of UICollectionReusableView.
UICollectionReusableView does not have a contentView property, unlike UICollectionViewCell. The latter creates its own view for that property by manipulating your view hierarchy and constraints. It manages the frame of the content view, and gives it a gesture recognizer, amongst other things. The relationship between the cell (which is itself a view) and the content view (as a subview of the cell) is somewhat opaque and has led to developer grief in previous versions of iOS.
There is nothing in the documentation expressly forbidding the use of UICollectionViewCell as a supplementary view. But there are implicit indications that the expected practice is to the contrary.
The purpose of UICollectionViewCell is to "present the main content" of your collection view, or "your main data items". It "presents the content for a single data item". Supplementary views, on the other hand, "are separate from the collection view's cells". "Like cells, these views are provided by the data source object, but their purpose is to enhance the main content of your app."
When configuring supplementary views in a storyboard, the "Collection View Programming Guide for iOS" provides the following guidance:
For supplementary views, drag a Collection Reusable View from the object library and drop it on to your collection view. Set the custom class and the collection reusable view identifier of your view to appropriate values.
There is no express guidance about configuring supplementary views programmatically, but there is also no reason to think the expected practice would be any different.
In testing your scenario (iOS 10.2 and iOS 9.3, Xcode 8), I could not reproduce the problem. I found that the frames of the content view and label were both set correctly.
But the easiest solution to your problem is likely to be to adopt the expected practice:
Change FooCollectionViewCell (and any associated nib file) from a subclass of UICollectionViewCell to a subclass of UICollectionReusableView.
Remove the autoresizing mask lines from awakeFromNib.

Mixing cells from storyboard and programmatically created cells

I have collection view in the storyboard. I have a part of cells in storyboard and part of cells I created programmatically. What should I do in sizeForItemAtIndexPath method? Which value should I return? For my programmatically created cells I return size for him. But I don't want copy size from storyboard for cells from storyboard. I mean, I have:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return self.viewModels[indexPath.item].viewSize // for programmatically created cells
}
And in viewModel class:
class ViewModel {
var viewSize:CGSize = CGSizeMake(50, 50)//for example
}
Also, I have cells from storyboard and size of it I setted in storyboard. For example, I have 5 different cells in storyboard and each has different size. Should I do ?
let viewModel = self.viewModels[indexPath.item]
if viewModel.id == "CELL_1" { return CGSizeMake(10, 10) }
else if viewModel.id == "CELL_2" { return CGSizeMake(20, 20) }
//and so on
I don't want to make this :(
Do you know solution how I can get size of item from storyboard or create behaviour when this method is not implemented? (I mean, if you don't implement this method then collection view will get size from storyboard)
You can instantiate the cell using its reuse ID and call:
cell.setNeedsLayout()
cell.layoutIfNeeded()
then access the cell's frame to get it's actual size.

Resources