How to display specific cell of UICollectionView using viewForSupplementaryElementOfKind - swift - ios

I am a new iOS programming. and now i am developing an app which display similar to appstore. But i have some problems which when user open the app so, i want cell of data should be display the second cell. for example, i have three cell then when user open the app the second cell should be automatically scrolled to that position.
Here is how i created header section: (programmatically)
/// banner section
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerCellId, for: indexPath) as! HeaderBanner
header.sectionCategory = dashbordCell?[indexPath.item]
return header
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let size = CGSize(width: view.frame.width, height: 370)
return size
}
Here is I created HeaderBanner
let dataBannerCollectionView: UICollectionView = {
var scrollInterval: Int = 3
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero,collectionViewLayout: flowLayout)
collectionView.backgroundColor = UIColor.clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
func setupViewsCell(){
dataBannerCollectionView.showsHorizontalScrollIndicator = false
dataBannerCollectionView.dataSource = self
dataBannerCollectionView.delegate = self
dataBannerCollectionView.register(HeaderBannerDataCell.self, forCellWithReuseIdentifier: headerCellId)
addSubview(dataBannerCollectionView)
setupConstraintDataBannerCollectionView()
}
Here is behavior of HeaderBanner file.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = sectionCategory?.dataBanner?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: headerCellId, for: indexPath) as! HeaderBannerDataCell
cell.dataBannerCell = sectionCategory?.dataBanner![indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
print(frame.width)
let size = CGSize(width: frame.width - 100 , height: 350)
return size
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Banner selected")
}
As you can see the header section display the first cell of image. I just my app look like this when user opens the app .
Here is output of my sample app:
Here is my expectation which i want to display the middle cell of all cells.

You can use the scrollToItem function of UICollectionView:
if you have three cells for example:
dataBannerCollectionView.scrollToItem(at: IndexPath(item: 2, section: 0), at: UICollectionViewScrollPosition.centeredHorizontally, animated: false)
Add that code in setupViewsCell() function

Related

Swift: UICollectionView sizeForItemAt will never be executed

I'm making a CollectionView which has 2 sections, everything is fine but the size. I'd like to give different size of cell in different section, but when I call sizeForItemAt, Xcode shows a yellow warning "Will never be executed". Here's how I set my CollectionView functions:
extension ArtistProfileViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch mySections[section] {
case .profile:
return 1
case .relatedArtists:
return 10
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch mySections[indexPath.section] {
case .profile:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ArtistProfileCell.identifier, for: indexPath) as? ArtistProfileCell else { return UICollectionViewCell()}
return cell
case .relatedArtists:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RelatedArtistCell.identifier, for: indexPath) as? RelatedArtistCell else { return UICollectionViewCell()}
cell.myImageView.image = UIImage(systemName: "person")
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
}
And here is how I set my CollectionView:
var myCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.estimatedItemSize = .zero
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.register(ArtistProfileCell.self, forCellWithReuseIdentifier: ArtistProfileCell.identifier)
cv.register(RelatedArtistCell.self, forCellWithReuseIdentifier: RelatedArtistCell.identifier)
return cv
}()
I did set the CollectionView.delegate/datasource to self in viewDidLoad, and I can see the cell appear on the screen. However, the size of cell does not change at all. Plus, I know one of the solution is to set the estimated size to none in storyboard, but I don't know how to set it programmatically. So I don't know if it works.
There's one thing odd, when I call sizeForItemAt, it does not show the complete function automatically. I have to type it by myself like this.
Does anyone can help? Thanks in advance!
You put sizeForItemAt inside cellForItemAt. They need to be separate.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch mySections[indexPath.section] {
case .profile:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ArtistProfileCell.identifier, for: indexPath) as? ArtistProfileCell else { return UICollectionViewCell()}
return cell
case .relatedArtists:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RelatedArtistCell.identifier, for: indexPath) as? RelatedArtistCell else { return UICollectionViewCell()}
cell.myImageView.image = UIImage(systemName: "person")
return cell
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
Try selecting your code, then pressing Ctrl + i to re-format your code (fixes indenting too!)

Swift UICollectionView works strange, registers only second tap

I'm implementing a collection view in Xcode 11 project, what happens is that only second tap gets registered. When user taps on left cell it doesn't open detail screen and when user next taps on right cell then left cell opens, very strange behavior. I'm not using storyboards just programmatic approach. Here's my code:
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
return cv
}()
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: 150)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCollectionViewCell
cell.backgroundColor = .white
cell.data = myData[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let newViewController = SecondViewController(data: myData[indexPath.item])
self.navigationController?.pushViewController(SecondViewController, animated: true)
}
Replace ( didDeselectItemAt isn't didSelectItemAt )
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
with
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
Also it should be
let newViewController = SecondViewController(data: myData[indexPath.item])
self.navigationController?.pushViewController(newViewController, animated: true)

How to scroll the collection view horizontal with header

I have collectionview with header.
This is the image
In this, i have display the questions in the UICollectionReusableView as header and answers in the collectionviewcell.
So the image is this way.But i need to scroll the collectionview horizontal only.
But that time it will become as :-
This is the header.
This is the cell.
But i need this in the first screen header and its answers. when user scroll it then need show 2nd question in header and its answers. How to implement it?
currently my code is as below:-
extension NH_DummyDataViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return self.questionViewModel.numberOfSections()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.questionViewModel.numberOfRowsIn(section: section)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = verticalCollectionView.dequeueReusableCell(withReuseIdentifier: "NH_Headercell", for: indexPath)as! NH_Headercell
// cell.options.text = "hello"
// let optionModel = self.questionViewModel.datafordisplay(atindex: indexPath)
cell.setReviewData(reviews:self.questionViewModel.datafordisplay(atindex: indexPath))
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let firstheader: NH_QuestionHeaderCell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "NH_QuestionHeaderCell", for: indexPath ) as! NH_QuestionHeaderCell
firstheader.setReviewData(reviews:questionViewModel.titleForHeaderInSection(atsection:indexPath.section))
return firstheader
}
}
// MARK: UICollectionViewDelegateFlowLayout
extension NH_DummyDataViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 100)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 100)
}
}
Set Collection view scroll direction Horizontal and set paging
yourCollectionview.pagingEnabled = true

UICollectionViewController Footer/ Header not showing

I am implementing a one section UICollectionViewController where I would like to add a header & footer.
I have read RayWenderlich tutorial to implement the logic and everything works fine EXCEPT my header & footer which are not showing up.
I checked "Section Header" & "Section Footer" in storyboard inspector, and added identifier for each UICollectionReusableView with "headerView" and "footerView".
I implemented the viewForSupplementaryElementOfKind method (see below code), but is not triggered (no breakpoints fired while debugging) while referenceSizeForFooterInSection and referenceSizeForHeaderInSection are . When app launch, I can't see header and footer background color, however I can see a space which should be header & footer.
Thank you in advance!
Here is an extract from my ViewController:
class VoteNextCityViewController : UICollectionViewController, UICollectionViewDelegateFlowLayout {
fileprivate let sectionInsets = UIEdgeInsets(top: 50.0, left: 20.0, bottom: 50.0, right: 20.0)
fileprivate let itemsPerRow: CGFloat = 2
private let cellID = "cellID"
var pendingCityArray: [City] = []
override func viewDidLoad() {
super.viewDidLoad()
fetchVotedCities()
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! VoteNextCityCell
let city = pendingCityArray[indexPath.row] //cannot not be nil, else numberOfItemsInSection would return 0
cell.setupView(city: city)
return cell
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pendingCityArray.count
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
//2
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: widthPerItem+50)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return sectionInsets.left
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: (self.collectionView?.frame.size.width)!, height: 50)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: (self.collectionView?.frame.size.width)!, height: 50)
}
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionFooter :
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "footerView", for: indexPath as IndexPath)
view.backgroundColor = UIColor.blue
return view
case UICollectionElementKindSectionHeader:
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "headerView", for: indexPath as IndexPath)
view.backgroundColor = UIColor.red
return view
default:
assert(false, "Unexpected element kind")
}
}
I think only the _ (underscore with a space) is missing as per new syntax.
So try replacing the function definition
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
with the below one
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {

UICollectionView with multiple rows

I use UICollectionView (views with UIButtons) to book the time:
override func viewDidLoad() {
super.viewDidLoad()
collectionViewTime.register(UINib(nibName: "TimeCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "timecell")
collectionViewTime.dataSource = self
collectionViewTime.delegate = self
if let layout = collectionViewTime.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .vertical
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Timeline.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let numberOfItems = CGFloat(collectionView.numberOfItems(inSection: 0))
let combinedItemWidth = (numberOfItems * flowLayout.itemSize.width) + ((numberOfItems - 1) * flowLayout.minimumInteritemSpacing)
let padding = (collectionView.frame.width - combinedItemWidth) / 2
return UIEdgeInsets(top: 0, left: padding, bottom: 0, right: padding)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "timecell", for: indexPath) as! TimeCollectionViewCell
let time = Timeline[indexPath.row][0]
cell.btnTime.setTitle(DateManager.dateToTime(date: time.0), for: .normal)
return cell
}
And I got all views (UIButtons) in one line:
How can I set automatically moving UIViews in new rows?
So, in my case (iPad) one row will contain 7 UIViews, iPhone will contain 4-5 UIViews in one row.
For that you need to implement UICollectionViewDelegateFlowLayout method collectionView(_:layout:sizeForItemAt:) and change the collectionView's Scroll direction to Vertical
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let minCellSpace: CGFloat = 10 //Set minimum cell space that you want
if UIDevice.current.userInterfaceIdiom == .pad {
//For iPad return 7 cell as you mention in question
return CGSize(width: (collectionView.frame.size.width / 7) - minCellSpace, height: yourCellHeight)
}
else {
//For iPhone return 4 cell, if you want to return 5 cell divide width to 5
return CGSize(width: (collectionView.frame.size.width / 4) - minCellSpace, height: yourCellHeight)
}
}
Please use one more flow delegate method as below
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize;
------
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:(button width), height:button hieght);
}

Resources