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

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)
}
}

Related

Modifying the size of the cell of UICollectionView

I am trying to modify the size of the cells that the UICollectionview contains. I believe that the sizeForItemAtIndexPath should do the trick. Yet nothing is happening.
I have looked into similar questions, they have advised doing the same thing. I am sort of suspicious of my inheritance.
The problem is that the cells remain in the same size regardless of the value they were fed in.
class HikeViewController: UIViewController {
var test: Int?
#IBOutlet weak var options: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = false
print(test!)
options.dataSource = self
options.delegate = self
}
}
extension HikeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
//collectionCell
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath as IndexPath)
cell.backgroundColor = UIColor.green
return cell
}
func collectionView(collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize
{
return CGSize(width: 400, height: 500)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.row)
}
}
And when I conform to UICollectionViewDelegateFlowLayoutthe app termainates where I set the followings:
options.dataSource = self
options.delegate = self
And the error is:
Could not cast value of type 'HikingClub.HikeViewController' (0x108e1f948) to 'UICollectionViewDataSource' (0x10e723ff0).
It's worth adding that I have made sure of the connection between HikeViewController and the storyboard.
extension HikeViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
}
It should be something like this
Make your extension a subclass of UICollectionViewDelegateFlowLayout
Use the following function
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// code
}
You need to specify that you implement the protocol UICollectionViewDelegateFlowLayout in your class declaration.
extension HikeViewController: UICollectionViewDelegateFlowLayout {}
also you have are not calling the delegate method, just defining a new one.
check the first parameter (collectionView -> _ collectionView)
func collectionView(_ collectionView : UICollectionView,layout collectionViewLayout:UICollec

2 UIcollectionview in 1 UIviewcontroller with 2 different UIcollectionflowlayout?

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.

Vertical collection view cells inside horizontal collection view cells do not resize when the device enters from portrait to landscape mode

I have been trying to implement a vertical collection view inside a horizontal collection view. I have succeeded in my venture up to great extent. But when the device is rotated from portrait to landscape mode, the vertical collection view cells do not resize. Although the cells resize properly when the device enters from landscape to portrait mode. Even the horizontal collection view cells resize properly as I have invalidated the flow layout inside the viewWillTransitionToSize function. I have implemented the same function (with other functions like numberOfItemsAtIndexPath, cellForItemAt, sizeForItemAt, , etc.) for vertical collection view too (inside a class "HorizontalCollectionViewCell" in a separate CocoaTouch file). But all in vein. All the view and subviews (including the two collection views) have been created using storyboard (not code). I have been searching for the solution for last 1 week , but haven't succeeded yet. Kindly help me find a proper solution for this problem.
Here is the code inside ViewController file:-
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var horizontalView: UIView! // Container View for horizontal Collection View.
#IBOutlet weak var horizontalCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
horizontalCollectionView.delegate = self
horizontalCollectionView.dataSource = self
menuBarCollectionView.delegate = self
menuBarCollectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == menuBarCollectionView {
let cell = menuBarCollectionView.dequeueReusableCell(withReuseIdentifier: "menuButtonsCell", for: indexPath) as! MenuBarCollectionViewCell
cell.menuButtonsLabel.text = menuButtonsName[indexPath.row]
return cell
}
let cell = horizontalCollectionView.dequeueReusableCell(withReuseIdentifier: "horizontalCell", for: indexPath) as! HorizontalCollectionViewCell
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == menuBarCollectionView {
let menuButtonWidthFactor = self.menuBarView.frame.width - 1
return CGSize(width: menuButtonWidthFactor / 3, height: 35)
}
return CGSize(width: horizontalView.frame.width, height: horizontalView.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
if collectionView == menuBarCollectionView {
return 0.5
}
return 0
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
menuBarCollectionView.collectionViewLayout.invalidateLayout()
horizontalCollectionView.collectionViewLayout.invalidateLayout()
}
}
Here is the code inside subclass HorizontalCollectionViewCell file:-
import UIKit
class HorizontalCollectionViewCell: UICollectionViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var verticalCollectionView: UICollectionView! {
didSet {
self.verticalCollectionView.delegate = self
self.verticalCollectionView.dataSource = self
}
}
#IBOutlet weak var verticalView: UIView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = verticalCollectionView.dequeueReusableCell(withReuseIdentifier: "verticalCell", for: indexPath)
cell.backgroundColor = UIColor.red
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.verticalView.frame.width, height: 100)
}
func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
verticalCollectionView.collectionViewLayout.invalidateLayout()
}
}

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().

UICollectionView cells overlapping swift

I am creating a UICollectionView, but when a new cell is added to the view it is partially overlapping the previous cell. Below is my code:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
cell.frame.size.width = self.view.frame.width / 3
cell.frame.size.height = self.view.frame.height / 4
cell.backgroundView = imageView
return cell
}
How do I make sure the cells don't overlap?
You shouldn't assign the frame value by yourself in the cellForItemAtIndexPath method.
The more suitably way is to do this in the UICollectionViewLayout, then set it as the collectionview's layout property. Actually, you need a UICollectionViewLayout instance when you init the UICollectionView instance.
Or simply, use the UICollectionViewFlowLayout which system provides to implement the flow layout conveniently, tell it the size of each cell, spaces between them, and some other informations by its delegate. The layout instance will arrange all for you.
For example.
class MYViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
//In viewDidLoad()
var collectionViewFlowLayout = UICollectionViewFlowLayout()
var myCollectionView = UICollectionView(frame: self.view.bounds, layout: collectionViewFlowLayout)
// A flow layout works with the collection view’s delegate object to determine the size of items, headers, and footers in each section and grid.
// That delegate object must conform to the UICollectionViewDelegateFlowLayout protocol.
myCollectionView.delegate = self
myCollectionView.dataSource = self
// MARK: UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(10, 10);
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 10;
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 10;
}
}
For more infomation, read the doc.
For me it was the wrong cell width and height specified in the size inspector of the view controller. Just set that properly possibly the same as your nib file and it may work.
Please see screenshot for clarity.
All what you need is to extend you ViewController from UICollectionViewDelegateFlowLayout and implement sizeForItemAt :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if (collectionView == myCollectionView_1) {
return CGSize(width: self.view.frame.width / 3, height: self.view.frame.height / 4)
} else {
return collectionView.frame.size
}
}
For me, simply changing Estimated Size to None at the collection view options fixed the problem.
For ref please check the below screenshot:

Resources