I'm trying to display cells in a collection view one in top of the other. the layout I want to achieve is described properly in this delegate layout function:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.collectionView.frame.width, height: 190)
}
So let's make an example that we have 20 cells to display.
I want that the first cells displayed are the last ones.
Now I'm starting displaying the first cells and then I scroll to the last ones in this way:
self.collectionView.scrollToItem(at: collectionView.lastIndexpath(), at: .top, animated: true)
extension UICollectionView {
func lastIndexpath() -> IndexPath {
let section = max(numberOfSections - 1, 0)
let row = max(numberOfItems(inSection: section) - 1, 0)
return IndexPath(row: row, section: section)
}
}
How can I start displaying the collection view from the ending elements without having to display first the first elements?
Related
I have a CollectionView with one item. This item consists of a tableView with 1 cell. Here is how it looks in Storyboard hierarchy:
In my onboardingTableViewCell I have all displayed to user content: imageView and ShadowView (UIView).
When device is in a portrait mode - everything displayed properly - ShadowView bounds equal its Superview bounds:
But when I switch to landscape because of the shadow I can see that actually content bounds to Safe Area instead of Superview:
I want it to be pinned to Superview bounds, so the shadow looks properly.
But all of my constraints in Storyboard are set to Superview. CollectionView is set in code like that:
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
collectionView.collectionViewLayout.invalidateLayout()
let indexPath = IndexPath(item: self.currentPage, section: 0)
DispatchQueue.main.async {
self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
What is the problem there might be and how can I fix that?
I have a parent UICollectionView that nests 2 other UICollectionViews:
Parent UICollectionView (Vertical)
First nested UICollectionView (Horizontal) - Grey
Second nested UICollectionView (Vertical) - Red
The parent UICollectionView
I use numberOfSections(in collectionView:) and manually set the number of sections in the parent collection view. In this case I return 2; one for first (grey) nested horizontal UICollectionView and one for the second (red) nested vertical UICollectionView
Then in collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) I return 1. That means each nested UICollectionView will be placed inside a single item.
For each section I dequeue collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) a specific cell [see Nested UICollectionViews step 3] for the first nested horizontal UICollectionView and a specific cell [see Nested UICollectionViews step 3] for the second nested vertical UICollectionView.
Then I set the size of each item in section using collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath). This is where my problem begins. But let me continue on how I setup the nested UICollectionViews.
Nested UICollectionViews
This is a 3-step procedure for each nested UICollectionView:
I create the UICollectionViewCell (the appearance of the cell)
I use a UICollectionViewController to:
Fetch the data from Firebase
Dequeue the cell mentioned in step 1 and pass in the data fetched from Firebase
Set the dimensions of each item using collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath)
I create another UICollectionViewCell that:
Initializes the UICollectionViewController mentioned in step 2
Uses the view of this UICollectionViewController and adds it to the cell's contentView by filling the superview (aka the cell)
contentView.addSubview(UICollectionViewController().view) UICollectionViewController().view.fillSuperview()
The problem
When I set the dimensions of the section where the horizontal UICollectionView (grey) will be, I just need to match the height of the UICollectionView to the height of each cell.
But when I set the dimensions of the section where the vertical UICollectionView will be (red) I need to know the number of cells to calculate the correct height. Otherwise, the section could either be too big or too small.
Any idea how I can notify the parent UICollectionView controller of the number of cells of the vertical nested collection view?
Thanks in advance.
For those nested behaviours I recommend creating UICollectionView by code, instead of IB Storyboard, so you do not have to deal so much with autoresizing constraints. You can create UICollectionView programmatically (instead of UICollectionViewController with desired frame.size and then assigning UICollectionViewFlowLayout to it so you can better controll how big should be your item layout in your nested collectionView based on its frame. Some example code is here:
lazy var layout: UICollectionViewFlowLayout = {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.itemSize = self.frame.size
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
return layout
}()
lazy var customCollectionView: UICollectionView = {
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.width * 0.5), collectionViewLayout: self.layout)
collectionView.backgroundColor = UIColor.clear
collectionView.dataSource = self
collectionView.delegate = self
let collectionCellNib = UINib.init(nibName: "CustomCell", bundle: Bundle.main)
collectionView.register(collectionCellNib, forCellWithReuseIdentifier: "customCellId")
return collectionView
}()
I am trying to implement an application that allows a user to swipe horizontally between collection view cells while also being able to scroll vertically to see the entire content of a particular cell.
I want all the collection view cells to have a table view embedded within them.
The problem I am running into as of now is that my horizontal scrolling works as I have set the collection view flow layout to horizontal. I know that collection view flow layouts can only support one direction. Because of this, I tried to implement the following solution.
As of now within my project, I have a view controller with a scroll view inside. Embedded within the scroll view, is a collection view. This collection view has its own header implemented via dequeueReusableSupplementaryView.
I know that constraints can often be an issue preventing vertical scrolling to take place so here is a picture of my constraints:
Additionally, here is some code that I have used to implement this system:
Determining the scroll view content size
override func viewDidLayoutSubviews() {
super.viewWillLayoutSubviews()
scrollView.contentSize.height = collectionView.frame.size.height
scrollView.contentSize.width = self.view.frame.size.width
}
Setting up the collection view
func setupCollectionView() {
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UINib(nibName: "TableViewHolderCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "tableViewHolderCollectionViewCell")
collectionView.register(UINib(nibName: "CustomCollectionViewHeaderView", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "CustomCollectionViewHeaderView")
collectionView.delaysContentTouches = true
collectionView.contentInsetAdjustmentBehavior = .never
collectionView.bounces = false
collectionView.isPagingEnabled = true
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.sectionHeadersPinToVisibleBounds = true
layout.sectionInset = UIEdgeInsets(top: 0, left: -self.view.frame.size.width, bottom: 0, right: 0)
collectionView.setCollectionViewLayout(layout, animated: false)
}
Setting up the collection view data
extension TasksAndScheduleViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 8
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.view.frame.size.width, height: self.view.frame.size.height)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tableViewHolderCollectionViewCell", for: indexPath) as! TableViewHolderCollectionViewCell
cell.backgroundColor = colorArray[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 343)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
//setting up header view
return headerView
}
}
After trying to debug, I realized that the collection view vertical scroll might have been overriding the vertical scroll of the scroll view I had added to my view controller. In order to solve this, I created a custom class (as seen below) which my collection view implemented. To my knowledge, this was successful at disabling the vertical scroll for the collection view but it was not successful in enabling the other scroll view's vertical scroll.
class CollectionViewVerticalScroll: UICollectionView {
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let direction = panGestureRecognizer.direction(in: self)
if direction.contains(.Down) || direction.contains(.Up) {
return false
}
return true
}
}
My desired goal is to have the ability to swipe horizontally between these collection view cells that have table views while also being able to vertically scroll the collection view cell and the contents of the table view embedded inside of it. Ideally, the vertical scroll should allow me to scroll the table view cells while also moving the entire view upwards. The closest example to what I am trying to implement that I could find online is twitter's search page. The only difference is that my application has a collection header view and no navigation bar. I have attached a picture below:
I would appreciate any help. Please do let me know if you have any questions or if something doesn't make sense to you.
need to move the collection view cell accordingly. I have attached my screenshot for your reference:
the above is my current image but I need that to be like this:
Then I need to add image to the textfield but I have added some code but I could not add it
searchtextfield.leftViewMode = UITextFieldViewMode.always
searchtextfield.leftView = UIImageView(image: UIImage(named: "search"))
For the collection view cell width and height you have to use below method:
extension "Your Controller" : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width/2 - 15, height: collectionView.frame.size.width/2 - 15)
}
}
To make the the cell square I have added the with and height same. You can change it according to your requirement. The collection view width changes for different devices so you have to change the cell width and height too.
Don't forget to do the change in this too according to your requirement:
Hope this helps.
Is there a way to make all items in a collection view fit in one row and not automatically place items in an another row? I'm trying to copy something like Instagram's story display like the link below:
However, In my collection view, I'm getting a count from a server, and displaying the information in a collection view but it always goes to the next line after 3 items are displayed.
// number of sections function
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pictures.count
} // end of function
I think you don't want Vertical scrolling in Collection view.
For horizontal scrolling in collection view, select collection view in storyboard and change scroll direction to horizontal.
Refer this below pic:
you can add collectionViewLayout delegate method like this. CGFloat(3.0) is how many item you want, you have to divide it from collectionView's width
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemWidth: CGFloat = ((collectionView.bounds.size.width) / CGFloat(3.0))
return CGSize(width: itemWidth, height: collectionView.bounds.size.height)
}