2 UIcollectionview in 1 UIviewcontroller with 2 different UIcollectionflowlayout? - ios

may I know is there any way to implement 2 UIcollectionview in 1 UIviewcontroller with 2 different UIcollectionflow layout.
The main issue is one UIcollectionview is conform to iOS UIcollectionviewflowlayout and the other is conform to a Waterfalllayout. Because the problem I faced now is I cant have both delegate functions in one UIviewcontroller. Thanks all.
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
}
func collectionView(_ collectionView: UICollectionView, layout: WaterfallLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
}

//: Playground - noun: a place where people can play
import UIKit
class Controller: UIViewController, UICollectionViewDelegateFlowLayout, WaterfallLayoutDelegate {
let collectionView = UICollectionView(frame: CGRect.zero)
let waterfallCollectionView = UICollectionView(frame: CGRect.zero)
override func viewDidLoad() {
collectionView.delegate = self
collectionView.collectionViewLayout = UICollectionViewFlowLayout()
waterfallCollectionView.delegate = self
let waterfallLayout = WaterfallLayout()
waterfallLayout.delegate = self
waterfallCollectionView.collectionViewLayout = waterfallLayout
}
//MARK: - WaterfallLayoutDelegate
func collectionView(_ collectionView: UICollectionView, layout: WaterfallLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.zero
}
//MARK: - UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.zero
}
}
//MARK: - Just for test
protocol WaterfallLayoutDelegate: class {
func collectionView(_ collectionView: UICollectionView, layout: WaterfallLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
}
class WaterfallLayout: UICollectionViewLayout {
weak var delegate: WaterfallLayoutDelegate?
}
Everything just works.

Related

CollectionView is not displaying vertically as expected

I am trying to implement following screen.
But in my code, it is displaying following way
following code I wrote,
import UIKit
class LunchViewController: UIViewController {
#IBOutlet weak var mainCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension LunchViewController:UICollectionViewDataSource,UICollectionViewDelegateFlowLayout,UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 412, height: 180)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// return self.myLibraryArray.count
return 8
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// let item = self.myLibraryArray[indexPath.row]
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RestaurantCollectionViewCell", for: indexPath) as? RestaurantCollectionViewCell else {
return UICollectionViewCell()
}
//cell.fillDetails(item)
return cell
}
}
Here is the complete project link
Can any one tell me, what is the issue? or what I have to write the code to get expected output?
Update:
Select collectionView and Change Estimate Size to none. In storyboard.
if you dont want space between two cells then set 0 to for lines in Min spacing
And i have given width as screen width
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.size.width, height: 180)
}
Output :-
Change sizeForItem method to this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width, height: 180)
}
Also, make sure the mainCollectionView is constrained to the leading and trailing edges in the storyboard.

Programmatically Get Height of Collection View Section Outside Collection View Protocol Methods

I have a chat app and the chat collectionView is created in a pod file that I don't have access to change. Each chat message is a section in the collectionView. I can determine the number of sections (chat messages) by calling myCollectionView.numberOfSections.
I want to determine the height of each section in the collectionView. How might I do this programmatically without using the collectionView protocol methods.
you can try this way:
class ThirdPartyViewController: UIViewController {
var collectionView: UICollectionView!
}
class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout {
let vc = ThirdPartyViewController()
var collectionView: UICollectionView { return vc.collectionView }
var originDelegate: UICollectionViewDelegateFlowLayout { return vc as! UICollectionViewDelegateFlowLayout }
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
//return custom value
return CGSize(width: 100, height: 100)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
//return origin value
return originDelegate.collectionView!(collectionView, layout: collectionViewLayout, sizeForItemAt: indexPath)
}
}

Make UICollectionViewCell same width as UICollectionView

Am writing this question just as a reference and to help anyone facing the same issue I faced. Many answers here at SO and in google search didn't provide a complete solution. So Here I answer my own question
Here is the code that works and sets both width and height correctly
import UIKit
class MyViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
#IBOutlet weak var myCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
myCollectionView.delegate = self
myCollectionView.reloadData()
}
//Other methods here
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) //Put your Cell Identifier istead of Cell
let height = cell.contentView.frame.height
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
return .zero
}
}

uicollectionview - data automatically load without calling reloaddata

I got two questions.
I am wondering why my collection view automatically load data without calling imageCollectionView.reloadData().
Solved. See comment
Why func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize is not called? I didn't see the print("collectionViewLayout called") I am trying to modify the cell's size so the cell's height equals to the collection view height
Solved. See comment
Here is the codes
class ProductInternalDetailVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var selectedProduct: Product?
#IBOutlet weak var imageCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
imageCollectionView.delegate = self
imageCollectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath) as! ProductInternalDetailCVC
if indexPath.row == 0 {
cell.productImage.image = selectedProduct!.image1
} else {
cell.productImage.image = selectedProduct!.image2
}
cell.productImage.frame = CGRect(x: 1, y: 1, width: cell.frame.size.width-2, height: cell.frame.size.height-2)
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
//return CGSize(width: collectionView.frame.size.height-1, height: collectionView.frame.size.height-1)
print("collectionViewLayout called")
return CGSize(width: 10, height: 10)
}
}
class ProductInternalDetailCVC: UICollectionViewCell {
#IBOutlet weak var productImage: UIImageView!
}
Thanks for the help.
Question 2: The function signature has changed a bit. Change the function signature with this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
...
}
And don't forget to implement the UICollectionViewDelegateFlowLayout protocol.
Question 1:
When entering the ViewController your collectionView loads the datasource of the collectionView automatically if this is your question.for example, make changes on datasource without changing the view (means without calling viewDidLoad method) you will not see the changes until you call imageCollectionView.reloadData().

cellForItemAt IndexPath does not call if I'm using UICollectionViewDelegateFlowLayout. Is it a bug of iOS?

cellForItemAt indexPath does not call if I'm using UICollectionViewDelegateFlowLayout.
In this case:
class Something: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
}
it calls my cellforItemAtIndexPath, but does not call my
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
}
but if I include UICollectionViewDelegateFlowLayout:
class Something: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
}
it will call:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
}
but will not call cellForItemAtIndexPath. I do not understand the problem. Is that a bug of iOS or I do not see anything?
For me, when creating the collectionView with layout, it should be let layout = UICollectionViewFlowLayout(), not let layout = UICollectionViewLayout() which is so easy to neglect. I was trapped in this like more than twice.
Try this code.
import UIKit
let kSCREENWIDTH = UIScreen.main.bounds.size.width
let kSCREENHEIGHT = UIScreen.main.bounds.size.height
let COLLECTION_CELL = "collectionCell"
class DemoController: UIViewController {
var collectionView : UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
}
/// create programatically collection view
fileprivate func setupCollectionView() {
let layout = UICollectionViewFlowLayout()
// layout.itemSize = CGSize(width:(kSCREENWIDTH-20)/2,height:150)
layout.sectionInset = UIEdgeInsetsMake(5, 7, 5, 7)
layout.minimumLineSpacing = 5
layout.minimumInteritemSpacing = 1
collectionView = UICollectionView.init(frame: self.view.bounds, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.white
self.view .addSubview(collectionView)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: COLLECTION_CELL)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//MARK: UICollectionViewDelegate
extension DemoController : UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// add your code here
}
}
//MARK: UICollectionViewDataSource
extension DemoController : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
/// add your code here
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: COLLECTION_CELL, for: indexPath)
cell.backgroundColor = UIColor.lightGray
print("Here cellForItemAt Works")
return cell
}
}
//MARK: UICollectionViewDelegateFlowLayout
extension DemoController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
print("Here sizeForItemAt Works")
return CGSize(width: 150, height: 150)
}
}
In my case contentInset was not proper for the UICollectionView sub class.
Try adding below code in you sub class
override func layoutSubviews() {
super.layoutSubviews()
self.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
override func reloadData() {
DispatchQueue.main.async {
super.reloadData()
}
}
Hope this helps someone!
Returning a size of CGSize(width: 0, height: 0) caused the delegate cellForItemAt not to get called in my case.
For example:
This would result in the issue.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 0, height: 0)
}
Where as this would not.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 10, height: 10)
}

Resources