I try to make my collectionView Cell height as per content.
I tried with Self Sizing CollectionViewCell but it is not working.
I used auto layout in CollectionView Cell.
Cell have three Labels with top,left,bottom,right Constraints & have Vertical Compression Ration is Required(1000).
My code is as follow:
override func viewDidLoad() {
super.viewDidLoad()
let flowlayout = col.collectionViewLayout as! UICollectionViewFlowLayout
flowlayout.estimatedItemSize = CGSizeMake((self.view.frame.size.width - 60) / 2 , 1)
}
}
//MARK: CollectionView
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 17
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = col.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CustomCell
if indexPath.row == 0 {
cell.lbl1.text = "Lorem ispum Simple text to show if it working or not"
}
else{
cell.lbl1.text = "\(indexPath.row)"
}
return cell
}
Some People are saying that it is not possible for collectionView Self Sizing Cell, even though Apple claims it.
You have to calculate manually.
If I try to calculate it manually then I am also unable to get cell object in sizeForItemIndexPath
Related
Currently, I have embedded a UICollectionViewCell in a UITableViewCell within one of the sections of my UITableView. I know how to dynamically change the cell's height in another section of my UITableView because I have a UITextView in another UITableViewCell that dynamically changes the height of the cell based on how much text is in the UITextView.
The problem I have is in regards to the UITableViewCell containing the UICollectionViewCell. My UICollectionViewCell has one row of 4 images that the user can add via the camera or photo library using a UIImagePickerController.
Currently as I have it, when the 5th picture is generated, the UITableViewCell's height remains static, but the user can scroll horizontally in the UICollectionViewCell like so:
My end goal is this:
And my storyboard:
Pretty self-explanatory but if there is only 4 images, the UITableViewCell remains the same as in screenshoot 1, but the cell's height will dynamically change if the UICollectionViewCell's height changes.
I have set the UICollectionView's scroll direction to be vertical only. Before explaining further, here's my partial code:
class TestViewController: UITableViewController, UITextFieldDelegate, UITextViewDelegate, UIImagePickerControllerDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
....
tableView.estimatedRowHeight = 40.0
tableView.rowHeight = UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell: UITableViewCell = UITableViewCell()
if indexPath.section == 1
{
cell = tableView.dequeueReusableCell(withIdentifier: "TextViewCell", for: indexPath)
let textView: UITextView = UITextView()
textView.isScrollEnabled = false
textView.delegate = self
cell.contentView.addSubview(textView)
}
else if indexPath.section == 4
{
if let imagesCell = tableView.dequeueReusableCell(withIdentifier: "ImagesCell", for: indexPath) as? CustomCollectionViewCell
{
if images_ARRAY.isEmpty == false
{
imagesCell.images_ARRAY = images_ARRAY
imagesCell.awakeFromNib()
}
return imagesCell
}
}
return cell
}
....
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
if indexPath.section == 1
{
return UITableViewAutomaticDimension
}
else if indexPath.section == 4
{
//return 95.0
return UITableViewAutomaticDimension
}
return 43.0
}
....
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage
{
if let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 4) ) as? CustomCollectionViewCell
{
cell.images_ARRAY.append(selectedImage)
cell.imagesCollectionView.reloadData()
}
}
picker.dismiss(animated: true, completion: nil)
}
....
func textViewDidChange(_ textView: UITextView)
{
...
// Change cell height dynamically
tableView.beginUpdates()
tableView.endUpdates()
}
}
class CustomCollectionViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
#IBOutlet var imagesCollectionView: UICollectionView!
var images_ARRAY = [UIImage]()
var images = [INSPhotoViewable]()
override func awakeFromNib()
{
super.awakeFromNib()
for image in images_ARRAY
{
images.append(INSPhoto(image: image, thumbnailImage: image) )
}
imagesCollectionView.dataSource = self
imagesCollectionView.delegate = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return images_ARRAY.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! ExampleCollectionViewCell
cell.populateWithPhoto(images[(indexPath as NSIndexPath).row]
return cell
}
....
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
return UIEdgeInsetsMake(0.0, 25.0, 0.0, 25.0)
}
}
Originally, my indexPath.section == 4, which contains the UICollectionViewCell returned a height of 95, but I commented that out and replaced it with returning UITableViewAutomaticDimension. I would assume that adjusted the height of the cell to fit the 5th image, but the cell remained a static height even though the UICollectionViewCell' height changed, allowing me to scroll vertically within that static UITableViewCell height.
I know these are some questions I found very similar to my situation, but they didnt help me resolve my particular issue:
Swift: Expand UITableViewCell height depending on the size of the
UICollectionView inside it
Auto Height of UICollectionView inside UITableViewCell
UICollectionView inside a UITableViewCell — dynamic height?
With some of the answers and suggestions, I've added the following:
imagesCell.images_ARRAY = images_ARRAY
imagesCell.awakeFromNib()
// Added code
imagesCell.frame = tableView.bounds
tableView.setNeedsLayout()
tableView.layoutIfNeeded()
However, this did not have any effects. Can anyone point me in the right direction on what code I need and placed where?
Thanks!
I am using these type of cells in my code, Not performing excellent performance wise(as affecting scrolling smoothness) but will let you achieve required design.
Use CollectionView inside tableViewCell with Vertical ScrollDirection and fixed width(I mean not dynamic in nature). This will put overflowing cells in vertical direction after filling horizontal direction.
Take out NSLayoutConstraint from xib(if you are using that) of collectionViewHeight. We will use it in later part.
set UITableViewAutomaticDimension in tableView in heightForRowAtIndexPath method.
And finally set cell's collectionViewHeight while returning cell in cellForRowAtIndexPath method using constraint that we took out in step 2.
Here I am attaching some code that may will help:
UITableView Part:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: xyzTableViewCell.self), for: indexPath) as! xyzTableViewCell
cell.collectionViewHeight.constant = (numberOfCells/5) * cell.cellHeight
return cell
}
UITableViewCell Part:
#IBOutlet fileprivate weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewHeight: NSLayoutConstraint
And you will need to reload that particular tableViewCell and reload collectionView inside this tableViewCell so that height function of tableView will be called and height of that tableViewCell will be refreshed, and to handle focused condition of that tableViewCell(when tableViewCell is in focus), I am saying this because if it's not in focus(or say cache, there is difference between them though) then cellForRowAtIndexPath method will be called on scrolling(when this cell is going to come in focus) then tableViewCell height will already be taken care of.
Hope this will help to achieve required functionality.
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 am trying to use a UICollectionView to display a square MyCollectionViewCell that has animated GIFs in a UIImageView.
Rows and sections are setup like so:
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}
I'm using SwiftGif to get GIFs assigned to the cell's UIImageView like so:
let gifs = [UIImage.gifWithName("gif1"), UIImage.gifWithName("gif2"), UIImage.gifWithName("gif3")]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("gifCell", forIndexPath: indexPath) as! GifCollectionViewCell
cell.gifImageView.image = gifs[indexPath.item % gifs.count]
return cell
}
Everything for the most part works great. But my issue is that when scrolling, there are times when cell is blank and no GIF appears. In the debugging process, I've added a label to the cell to display the indexPath.item, as you can see in code above, to make sure that the cell isn't getting passed over and have found that the label will always display indexPath even if the cell does not display a GIF.
I have tested with regular images instead like so:
let testImages = [UIImage(named: "testImage1"), UIImage(named: "testImage2", UIImage(named: "testImage3")]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("gifCell", forIndexPath: indexPath) as! GifCollectionViewCell
cell.gifImageView.image = testImages[indexPath.item % testImages.count]
return cell
}
and had no occurrences of blank cells.
Even more curious, when I originally actually built the GIF in collectionView(...cellForItemAtIndexPath) I did not get any issues with blank cells either:
let gifNames = ["gif1", "gif2", "gif3"]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("gifCell", forIndexPath: indexPath) as! GifCollectionViewCell
let gif = UIImage.gifWithName(gifNames[indexPath.item % gifNames.count])
cell.gifImageView.image = gif
return cell
}
This original implementation would have worked if it weren't for the fact the GIF build process drastically affects the scrolling performance of the UICollectionView which is what forced me to change implementation in the first place.
I have confirmed that this is not an issue with SwiftGif as I have replicated this issue in a different application using an animation render library and an AnimatedView in place of the UIImageView in MyCollectionViewCell and displayed animations instead of GIFs and got the same issue with cells randomly showing nothing instead of the animation when scrolling through the CollectionView.
I have tried the StackOverflow solution here and implemented a custom UICollectionViewFlowLayout like so:
class MyFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElementsInRect(rect)
let contentSize = self.collectionViewContentSize()
let newAttrs = attributes?.filter { $0.frame.maxX <= contentSize.width && $0.frame.maxY <= contentSize.height}
return newAttrs
}
}
And assigned it in viewDidLoad() like so:
self.collectionView?.collectionViewLayout = MyFlowLayout()
In MyFlowLayout I have also tried:
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
I have also messed with various cell sizes (width 1 less than height, height 1 less than width etc) and messed around with some section inset combinations but have not managed to find the source of this issue that is causing the animated view to not to show up.
Any help would be appreciated. Thanks
I solved the issue by setting the gifImageView.image = nil in collectionView(...cellForItemAtIndexPath) before assigning the image.
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("gifCell", forIndexPath: indexPath) as! GifCollectionViewCell
cell.gifImageView.image = nil // Added this line
cell.gifImageView.image = gifs[indexPath.item % gifs.count]
cell.infoLabel.text = String(indexPath.item)
return cell
}
Not sure how/why this fixed it but it works now.
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 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?