I have a weird issue with UICollectionView and UITabBarController. Inside the UITabBarController i have references to two different ViewControllers. If i put View Controller that has UICollectionView as a first page of UITabBarController then my list with cells (UICollectionView) is loading normally like this:
But if i put it as a second View Controller when i open the tab it is like UIImageView and UILabel are disappearing from the cells:
I have checked collectionView method that is getting the cells and there is always data printed for the Label and Image. Here is a code of the methods for DataSource and Delagate
// tell the collection view how many cells to make
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
// make a cell for each cell index path
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! AdsCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.myLabel.text = self.items[indexPath.item]
cell.backgroundColor = UIColor.whiteColor() // make cell more visible in our
cell.layer.borderColor = UIColor.grayColor().CGColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
cell.layoutIfNeeded()
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let width = collectionView.frame.width - 22;
return CGSize(width: width/2, height: width/2);
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
}
Is there someone that had problem like this? How can it be solved?
Related
I am creating a scrollable view filled with monthly calendars. I'm using a collection view to display a calendar inside a table view full of calendars. So each table view cell is a calendar for a specific month, and each collection view cell is a day. I have a separate swift file for the tableview cell from the view controller. Since each tableview cell is going to look different (because different months), the tableview cell needs to know which row it is placed in inside the tableview during its creation in dequeque cell function.
tableView.dequeueReusableCell(withIdentifier: "CalendarTableViewCell", for: indexPath)
I need to get the indexPath in the "for: indexPath" parameter inside the tableview cell file because the collectionview inside the tableview cell gets created when the tableview cell is dequeued. The contents of the collection view depends on which tableview row it's in. So how do I get that parameter?
Sorry for the long explanation, please help if possible. Thank you!
Create an array in UITableViewCell subclass and use that array in collection view data source methods.
class MonthCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
let datesArray = [String]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return datesArray.count
}
}
In tableView cellForRowAt method assign the date values and reload the collectionView
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! MonthCell
cell.datesArray = []//dates based on indexPath
cell.collectionView.reloadData()
return cell
}
Or
Assign a reference to the table view cell index path in tableView cellForRowAt method
class MonthCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
var tableIndexPath:IndexPath?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let tableIndexPath {
// return value based on tableIndexPath
} else {
return 0
}
}
}
//cellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MonthCell") as! MonthCell
cell.tableIndexPath = indexPath
cell.collectionView.reloadData()
return cell
}
I’ve been programming app like instagram for learn swift and ios programming. Now I’d created UIStackView that would be a top bar, and 3 bottom buttons that posts requests and change images.
The problem that I have - I can’t set position, width and delete cell spacing on CollectionView. I want to:
CollectionView full width on all devices
cell spacing between images = 1
CollectionView should be positioned between top bar and bottom buttons full height
What did I have now?
screen of my emulator: http://prntscr.com/d1mmu1
screen of my storyboard: http://prntscr.com/d1mnhr
I’d tried do this with StoryBoard, had changed all of parametres but hasnt reached my goals.
If I add equal height to CollectionView and main view of app it takes all screen and after scrolling it disapears…
How can I set that position and width, height, and cell spacing on it?
here is a code of my viewcontroller for CollectionView -
// MARK: - UICollectionViewDataSource protocol
// tell the collection view how many cells to make
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.imageView.image = UIImage(named: "main/p\(self.items[indexPath.item].code)/main/main.jpg")
print("main_card_images/p\(self.items[indexPath.item].code)/main/main")
return cell
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
print(self.items[indexPath.item])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let yourNextViewController = (segue.destination as! ViewControllerCard)
let indexPath = collectionView.indexPath(for: sender as! UICollectionViewCell)
yourNextViewController.mainCardImage = self.items[(indexPath?.item)!]
}
Try this code.
Note : Below code works only for 4 column layout tested on all iPhone models.If you want different number of column.You need to play with cell width and height or look at my other answer Adjust cellsize for fit all screens?
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionInset = UIEdgeInsets(top: 3, left: 3, bottom: 3, right: 3)
layout.minimumInteritemSpacing = 03
layout.minimumLineSpacing = 03
layout.invalidateLayout()
return CGSize(width: ((self.view.frame.width/4) - 4), height:((self.view.frame.width / 4) - 4));
}
Output:
I want to make a scrollable list of images like instagram with 4 columns. I created a collection view with image views http://prntscr.com/d15rnx . But I get this result - http://prntscr.com/d15tsq
code -
// MARK: - UICollectionViewDataSource protocol
// tell the collection view how many cells to make
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.imageView.image = UIImage(named: "main/p\(self.items[indexPath.item].code)/main/main.jpg")
print("main_card_images/p\(self.items[indexPath.item].code)/main/main")
return cell
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
print(self.items[indexPath.item])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let yourNextViewController = (segue.destination as! ViewControllerCard)
let indexPath = collectionView.indexPath(for: sender as! UICollectionViewCell)
yourNextViewController.mainCardImage = self.items[(indexPath?.item)!]
}
Make sure you have set inter cell spacing to 0 and then all you have to do is implement:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return(collectionView.bounds.size.width/4,collectionView.bounds.size.height/4)
}
This will resize all cells to be equal to 1/4 the UICollectionView width and thus create 4 cells in each row(1/4 the height value is just cause i like symmetry in these types of UI). If there appears to be any less number of cells than that, its because the inter-item spacing has not been set to 0. If you want inter-item spacing, just subtract that value from the width value returned in the above function
Alternatively
You can implement your own custom flow layout but the above solution is far more simpler.
I have found it easiest to configure my collection view via the setting in Interface Builder. For a collection view with a flow layout here are the settings I use for the insets. I have found the item size width: 125.8 and height: 125.8 to give me the best size for displaying on most devices in portrait or landscape.
Here's what my collection view looks like.
You can of course set this programmatically using UICollectionViewDelegateFlowLayout or as Rikh suggests by subclassing CollectionViewFlowLayout
Hi everyone. I started learning programming 1 month ago so please be nice if I don't explain my problem well :)
My project is composed of a main UITableView. In each cell of the UITableView, I have a UICollectionView (on horizontal scrolling).
Img 1 : Main view
The width of each UICollectionViewCell is the same as the entire UITableViewCell. My first problem is about sizing the height of the UITableViewCell (which will depend of the size of the UICollectionView itself and the size of the content on top of it).
Img 2 : CollectionView
This has to be done automatically. In fact, the UICollectionViewCells will not have the same height, so when the user will scroll the UICollectionView horizontally, a new UICollectionViewCell will appear (with different height) and the height of the UITableViewCell will have to adapt.
The second problem I have is about sizing the UICollectionViewCell, in fact I will not know in advance what the height of it is going to be (the width is the same as the UITableView). I should import the content from a nib.
So now here is my ViewController file,
the variables:
#IBOutlet weak var tableView: UITableView!
var storedOffsets = [Int: CGFloat]()
the UITableView extension : Create the cell of the UITableView and set delegate of UICollectionView inside it
extension IndexVC: UITableViewDelegate, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6 //Temp
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("CellCollectionView") as? CellPost {
let post = self.posts[indexPath.row]
cell.configureCell(post)
return cell
} else {
return CellPost()
}
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? CellPost else { return }
tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
tableViewCell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
}
func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? CellPost else { return }
storedOffsets[indexPath.row] = tableViewCell.collectionViewOffset
}
the part of the UICollectionView : Add the view from a Nib/xib to the cell
extension IndexVC: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9 //Temp
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellInCollectionView", forIndexPath: indexPath)
if let textPostView = NSBundle.mainBundle().loadNibNamed("textPostView", owner: self, options: nil).first as? textPostView {
textPostView.configurePost(post.descriptionLbl)
cell.addSubview(textPostView)
textPostView.translatesAutoresizingMaskIntoConstraints = false
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":textPostView]))
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":textPostView]))
}
return cell
}
Make the size of the cell the same as the entire UICollectionView
extension IndexVC: UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(collectionView.frame.width, collectionView.frame.height)
}
And I created this extension in the class dedicated for the UITableViewCell (not useful for the problem, but if any of you want to recreate it)
extension CellPost {
func setCollectionViewDataSourceDelegate<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>(dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.setContentOffset(collectionView.contentOffset, animated:false) // Stops collection view if it was scrolling.
collectionView.reloadData()
}
var collectionViewOffset: CGFloat {
set {
collectionView.contentOffset.x = newValue
}
get {
return collectionView.contentOffset.x
}
}
If anyone want to use this code, it works great, but I have to hardcode the height of the UITableView with a tableView( ... heightForRowAtIndexPath ...)
I tried everything to make the UITableViewCell adapt to what's inside it (I tried calculating the size of content sent by the Nib I'm putting in the cell, and then send it to tableView( ... heightForRowAtIndexPath ...)
but I can't make it work. I also tried Auto Layouts but maybe I'm doing it wrong. I also think the problem could come from the part that I imported the nib in my cell.
I also have no idea of a way to expand the cell after the user has swiped the UICollectionViewCell, is there any way to create an event when that happens? And maybe call tableView( ... heightForRowAtIndexPath ...) again?
As i understanding you need to auto adjust tableview cell height. so you can use autolayout for adjust cell height.
Write in ViewDidLoad
self.tableView.estimatedRowHeight = 80
self.tableView.rowHeight = UITableViewAutomaticDimension
Before Returning cell in CellForRowAtIndexPath
self.tableView.setNeedsLayout()
self.tableView.layoutIfNeeded()
You can find link for autolayout.
enter link description here
In order to solve my problem, I created an array to store the height of each cell of the UITableView (named cellHeight).
I also calculate the height of each cell at the start of the program in an array composed of array (named cellHeightForPost). The main array represent all the TableView cells, each array inside it represent the height of each cell of the 'collectionView'
In order to update the table view everytime I change the collectionView inside it, i used the fonction collectionView(... willDiplayCell...) :
self.tableView.beginUpdates()
cellHeight[collectionView.tag] = cellHeightForPost[collectionView.tag][indexPath.row]
self.tableView.setNeedsLayout()
self.tableView.layoutIfNeeded()
self.tableView.endUpdates()
My UITableView size is defined in tableView(... heightForRowAtIndexPath) and tableView(... estimatedHeightForRowAtIndexPath) :
return cellHeight[indexPath.row]
To set the size of the collectionViewCells :
return CGSize(width: collectionView.frame.width, height: cellHeightForPost[collectionView.tag][indexPath.row])
I currently have a collection view set up to display a dynamic number of objects in its view. Each cell displays an image from the corresponding object. When a cell is tapped, it triggers a segue to the next view in the hierarchy. The cell's corresponding object is passed to the next view However, I am noticing that when I return the view with the collection, the ordering of cells has changed, and now, when I tap one to got the next view, its properties are from objects of other cells.
Below are my methods of the UICollectionView:
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return objectsCount
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Chow Object Reuse ID", forIndexPath: indexPath) as UICollectionViewCell
cell.backgroundColor = UIColor.whiteColor()
var imageView:UIImageView = UIImageView()
imageView.frame = CGRect(x: 0, y: 0, width: 90, height: 90)
//Since loading of images is a time-intensive task, all the thumbnails
//may have not been fetched yet.
if (imageThumbsArray.count == objectsCount) { //Eventually, a more elegant fix will be needed.
imageView.image = imageThumbsArray[indexPath.row]
}
cell.addSubview(imageView)
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.objectToBePassed = self.chowObjectsArray[indexPath.row] as PFObject
self.performSegueWithIdentifier("Show Chow Details", sender: self)
}
I also call self.PastObjectsCollection.reloadData()
Why is the reordering and mixing up of cells happening?
Thanks,
Siddharth