tableview different cells has different heights in swift - ios

1. I have a table view with two cells. I want these two cell’s height automatic dimension. In the first cell, a label is there, according to the label the cell height will increase. And in the second cell, a collection view is there, which contains user photos. As the numbers of photo increase, the collection view height will increase and according to that the table view cell size will increase.
2. I have the following code but I am not getting the desired result. In my code, I have two table view cells "CustomCell" and "image cell". The image cell has a collection view. Is it a good practice to take a collection view inside a table view cell or should I use a scroll view here? But as the number of photo increase, the collection view height will increase.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as? CustomCell
cell?.personName.text = "New User"
cell?.aboutPerson.text = "Near the beginning of his career"
return cell!
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath) as? imageCell
return cell!
}
return UITableViewCell()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var height:CGFloat = CGFloat()
if indexPath.row == 0 {
return UITableView.automaticDimension
}
else {
let indexPath = IndexPath(row: 0, section: 0)
let cell = tableView.cellForRow(at: indexPath) as? imageCell
return (cell?.personPhotoCollectionView.frame.size.height)!
}
return height
}
}
class imageCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = personPhotoCollectionView.dequeueReusableCell(withReuseIdentifier: "imageCollectionViewCell", for: indexPath) as? imageCollectionViewCell
cell?.personImage.image = UIImage(named: "azamat-zhanisov-om_aSsajsLw-unsplash.jpg")
return cell!
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberOfCollumns: CGFloat = 3
let width = collectionView.frame.size.width
let xInsets: CGFloat = 10
let cellaSpacing: CGFloat = 5
return CGSize(width: (width / numberOfCollumns) - (xInsets + cellaSpacing), height: (width / numberOfCollumns) - (xInsets + cellaSpacing))
}
#IBOutlet weak var personPhotoCollectionView: UICollectionView!
}

you should add two methods to your table view controller, heightForRowAt and estimatedHeightForRowAt
try this
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
return UITableView.automaticDimension
} else {
return 40
}
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
return UITableView.automaticDimension
} else {
return 40
}
}
or mabybe try with this line
self.tableView.rowHeight = 44.0
check this link for a little more information link
or this one, the apple official documentation link2

By adding the following code, it solved my issue.
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
personPhotoCollectionView.layoutIfNeeded()
personPhotoCollectionView.frame = CGRect(x: 0, y: 0, width: targetSize.width , height: 1)
return personPhotoCollectionView.collectionViewLayout.collectionViewContentSize
}

Related

tvOS: Unable to scroll through UICollectionView items when it is implemented like this: UICollectionView inside UITableView

I have implemented this:
By following this tutorial.
Here's the problem:
I am unable to scroll through the collection items.
I think this has something to do with the fact that the project I followed is for iOS and my project is for tvOS.
I've found a somewhat similar question. An answer linked to this GitHub Repo whose implementation doesn't seem that different from mine.
Here's the relevant code:
ViewController.swift
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell =
tableView.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath) as? TableViewCell
else {
fatalError("Unable to create explore table view cell")}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
}
tableViewCell.swift
class TableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
collectionView.delegate = self
collectionView.dataSource = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
extension TableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// For some reason he chose the measures of collectionViewCell and substracted 2
return CGSize(width: 139, height: 64)
}
// Highlight the current cell
// This doesn't work
func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
if let pindex = context.previouslyFocusedIndexPath, let cell = collectionView.cellForItem(at: pindex) {
cell.contentView.layer.borderWidth = 0.0
cell.contentView.layer.shadowRadius = 0.0
cell.contentView.layer.shadowOpacity = 0.0
}
if let index = context.nextFocusedIndexPath, let cell = collectionView.cellForItem(at: index) {
cell.contentView.layer.borderWidth = 8.0
cell.contentView.layer.borderColor = UIColor.orange.cgColor
cell.contentView.layer.shadowColor = UIColor.orange.cgColor
cell.contentView.layer.shadowRadius = 10.0
cell.contentView.layer.shadowOpacity = 0.9
cell.contentView.layer.shadowOffset = CGSize(width: 0, height: 0)
collectionView.scrollToItem(at: index, at: [.centeredHorizontally, .centeredVertically], animated: true)
}
}
}
In a separate project, I have implemented a collectionView independently. I.e: It was not embedded inside of a tableView. And it works just fine.
I copied the code from that project that highlights the selected cell and added it to the tableViewCell.swift but it had no impact at all.
So my question is:
Why am I unable to select a cell and/or scroll through the
collectionView?
Found the answer here:
By denying the focus of the table cell, the Focus engine will
automatically find the next focusable view on the screen. And in your
case, that is the collection view’s cell.
func tableView(_ tableView: UITableView, canFocusRowAt indexPath: IndexPath) -> Bool {
return false
}

How to call the indexPath.row of a UITableViewCell with a UICollectionView embedded within

Currently, I have a UITableView with a UICollectionView embedded in it. I have 3 arrays of information and I want each array to correspond to a UICollectionView, and therefore a UITableViewCell.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CookieCell", for: indexPath) as? MenuCollectionViewCell
cell?.fullView.layer.borderColor = UIColor.black.cgColor
cell?.labelView.layer.borderColor = UIColor.black.cgColor
cell?.fullView.layer.borderWidth = 0.3
cell?.labelView.layer.borderWidth = 0.3
return cell!
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
}
Where each of the return cell? is, I want to do something like:
if indexPathOfTableView.row == 0 {
//designated parameters
}
else if indexPathOfTableView.row == 1 {
//designated parameters
}
else {
//designated parameters
}
How would I do this?
Hello #helloworld12345
from your question I got the idea that you want to collectionview in tableviewcell
may be below code will help you.
first in you viewController or tableviewcontroller,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
//send indexPath of this cell
let cell = tblView.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath) as! tableViewCell
//Pass your array which you want to show in collectionViewCell and then reload collection view
cell.clnView.reloadData()
return cell
}
In your tableViewCell swift file:
class tableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
#IBOutlet weak var clnView: UICollectionView!
//after awakenib method write below code
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 4 //pass your array count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let flowayout = collectionViewLayout as? UICollectionViewFlowLayout
let space: CGFloat = (flowayout?.minimumInteritemSpacing ?? 0.0) + (flowayout?.sectionInset.left ?? 0.0) + (flowayout?.sectionInset.right ?? 0.0)
let size:CGFloat = (self.clnView.frame.size.width - space) / 2.0 //By this you can show two cell in one screen
return CGSize(width: size + 60, height: (size + 200))
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "clnViewCell", for: indexPath) as! clnViewCell
// do your stuff with your
return cell
}
}

How to update tableView data when a CollectionViewCell is clicked ( tableview.reloadData() not working inside CollectionView's didSelectItemAt )

Image 1:
I have a collection view and a table view in the same view controller. I want to update the data of my table view, based on the selected item in the collection view. So every time I click on any item of the collectionView, my tableView data should update.
My code:
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
for superSkill in skills!{
if superSkill.name == (skills?[indexPath.row].name)! {
childSkills = superSkill.skills!
}
}
DispatchQueue.main.async{
self.childSkillTableView.reloadData()
}
}
Is there any way i can achieve it
First thing is to make sure your childSkillTableView.dataSource = self and your superSkillsCollectionView.delegate = self
The second thing, there's no reason to use DispatchQueue.main.async({})
The third thing, though less important, instead of a for loop, you might use something like:
childSkills = skills?.first(where { superSkill in superSkill.name == (skills?[indexPath.row].name)! }
Though you should use some if let or guard let statements to check for optionals instead of force unwrapping.
extension PerformanceVC: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return skills.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "superSkillCell", for: indexPath) as! SuperSkillCell
cell.superSkillName.text = skills[indexPath.row].name
//loading image async
ImageAsyncLoader.loadImageAsync(url: (skills[indexPath.row].imageURL)!, imgView: cell.superSkillImage)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: skillCollections.frame.height * 0.9, height: skillCollections.frame.height) //use height whatever you wants.
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print((skills[indexPath.row].name)!)
for skill in skills{
if skill.name == (skills[indexPath.row].name)! {
childSkills = skill.skills!
}
}
self.subSkillTableView.reloadData()
}
}
extension PerformanceVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let openViewHeight: Int = 55
if (indexPath.row == selectedRowIndex.row && isExpanded == false){
isExpanded = true
return CGFloat(openViewHeight + 36 * (childSkills[indexPath.row].skills?.count)!)
} else if (indexPath.row == selectedRowIndex.row && isExpanded == true){
isExpanded = false
return CGFloat(openViewHeight)
} else {
isExpanded = false
return CGFloat(openViewHeight)
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return childSkills.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(childSkills[indexPath.row].name)
selectedRowIndex = indexPath
tableView.beginUpdates()
//let cell = tableView.dequeueReusableCell(withIdentifier: "SubSkillTableCell", for: indexPath) as! SubSkillTableCell
let cell = tableView.cellForRow(at: indexPath) as! SubSkillTableCell
for subview in cell.grandSkillStack.subviews {
subview.removeFromSuperview()
}
var grandSkillView: GrandChildSkillItem
grandChildSkills = (childSkills[indexPath.row].skills)!
for grandchildskill in grandChildSkills {
grandSkillView = GrandChildSkillItem(frame: CGRect(x: 0, y: 0, width: 300, height: 30))
grandSkillView.grandChildSkillNameLabel.text = grandchildskill.name
grandSkillView.grandChildSkillProgress.progress = Float(grandchildskill.percentage!)
cell.grandSkillStack.addArrangedSubview(grandSkillView)
}
tableView.endUpdates()
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//tableView.separatorStyle = .none
tableView.showsVerticalScrollIndicator = false
let cell = tableView.dequeueReusableCell(withIdentifier: "SubSkillTableCell", for: indexPath) as! SubSkillTableCell
cell.subSkillName.text = childSkills[indexPath.row].name
cell.subSkillProgress.progress = Float(childSkills[indexPath.row].percentage!)
if let uPoints = childSkills[indexPath.row].userPoints {
if let tPoints = childSkills[indexPath.row].totalPoints {
if let count = childSkills[indexPath.row].skills?.count {
cell.subSkillDetail.text = "\(uPoints)" + "/" + "\(tPoints)" + "XP \u{2022} " + "\(count)" + " subskills"
}
}
}
return cell
}
}

How to set UITableViewCell Height according to UICollectionView Content size?

I have a UITableView and Inside that I have two UICollectionViews.
My requirement is that I have to scroll first UICollectionView Horizontally.
And I have to scroll UITableView vertically Because I don't want to scroll Second UICollectionView.
In Image you can see that first (Header 'Folder') UICollectionView I have to scroll horizontally and second (Header 'Product') UICollectionView I don't want to scroll vertically because I have to scroll Complete UITableView.
Both collectionView are in TableViewCell and I have disable scrolling of second UICollectionView.
I have to add and delete a product then I how can I set the TableViewSecond Cell height that content should come.
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (0 == indexPath.section) {
var addFolderCell = tableView.dequeueReusableCell(withIdentifier: "AddFolderCell") as? AddFolderCell
if(nil == addFolderCell) {
let nib:Array = Bundle.main.loadNibNamed("AddFolderCell", owner: self, options: nil)!
addFolderCell = nib[0] as? AddFolderCell
}
addFolderCell?.collectionView.delegate = self
addFolderCell?.collectionView.dataSource = self
return addFolderCell!
} else {
let identifier = "CreateFolderCell"
var createFolderCell = tableView.dequeueReusableCell(withIdentifier: identifier) as? CreateFolderCell
if(createFolderCell == nil) {
let nib:Array = Bundle.main.loadNibNamed("CreateFolderCell", owner: self, options: nil)!
createFolderCell = nib[0] as? CreateFolderCell
}
return createFolderCell!
}
}
//Height for row.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//let index : NSIndexPath = NSIndexPath(row: 0, section: 1)
if 0 == indexPath.section {
return 135
}
return 300
}
//Header height.
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 20
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionsArray[section] as? String
}
}
extension GroupDataView : UICollectionViewDelegate,UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AddFolderCollectionCell", for: indexPath) as! AddFolderCollectionCell
//cell.backgroundColor = UIColor.white
cell.layer.borderWidth = 0.4
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.cornerRadius = 2
//CreateCardView(viewCorner: cell.cardView) //cell.cardView
return cell
}
}
//MARK: Collection View Layout
extension GroupDataView : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 90, height: 130)
}
}
What can I do? How can I set the height for tableView?
I think you can accomplish this, using some few steps:
Subclass your UITableViewCell, let's name it CollectionCell, and change it's class in IB interface to new subclass name.
Create outlet from UICollectionView to CollectionCell subclass, where UICollectionView is placed. Let's name it collectionView.
Add constraints to all sides of collectionView to cell bounds. (top,
right, bottom, left).
Add in IB for collectionView height constraint and create outlet to CollectionCell too. Let's name it collectionViewHeight.
In tableView:cellForRowAtIndexPath: add code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// ...
// normal cell declarations you wish
// ...
(cell as! CollectionCell).collectionView.reloadData()
let height = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
(cell as! CollectionCell).collectionViewHeight.constant = height
return cell
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100 // <- Your Desired Height
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}

How to load data dynamically using to API in Multiple UITableView and CollectionView

Wanted to load data using to api in collection view and table view.
The Table View is going to handle vertical scrolling. Each genre (Week Days) is a table section that contains one header and one cell. The key really is to nest a Collection View inside the Table View row. Just remember to point the Collection View DataSource to the table cell rather than the View Controller, and everything will be just fine.
Following code has been implemented
Controller - TimetableVC
In the controller showing static data ..
import UIKit
class TimetableVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
var categories = \["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"\]
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return categories.count
// return 1
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories\[section\]
}
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CategoryRow
return cell
}
}
CategoryRow:
// Collectionview data showing
import UIKit
class CategoryRow : UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
// var Array = [String]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// add no of item in the table like 2,3,5
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for: indexPath) as! VideoCell
// return cell
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "itemCell", for: indexPath as IndexPath) as! VideoCell
var Label1 = cell.viewWithTag(1) as! UILabel
Label1.text = "Subject"
var Label2 = cell.viewWithTag(2) as! UILabel
Label2.text = "Teacher"
var Label3 = cell.viewWithTag(3) as! UILabel
Label3.text = "Time"
// 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.cyan // make cell more visible in our example project
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemsPerRow:CGFloat = 4
let hardCodedPadding:CGFloat = 0
// let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
// let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
let itemWidth = 100
let itemHeight = 100
return CGSize(width: itemWidth, height: itemHeight)
}
}
Note: Please help me. I have implemented table view and collection view. I want to show dynamically table header with collection view data using to api response.
Output:

Resources