I have a problem: I have UITableView with UITableViewCell, at cell I have UICollectionView with a different number UICollectionViewCell. I want to UITableViewCell height corresponds to the height of UICollectionView.
I see real height UICollectionView here:
class TimeViewCell: UITableViewCell {
#IBOutlet weak var labelTime: UILabel!
var dataForShow = [String]() {
didSet{
self.collectionView?.reloadData()
}
}
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension TimeViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataForShow.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CustomCollectionCell
cell.labelCollection.text = dataForShow[indexPath.row]
let realValueHeightOfCollection = collectionView.contentSize.height
//when print realValueHeightOfCollection I see 50.0 (if dataForShow.count <4) or 110.0 (if dataForShow.count >5)
return cell
}
}
but how to pass that height to height UITableViewCell?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellTime", for: indexPath) as! TimeViewCell
let showHour = self.infoWripper.sorted(by: { $0.hour < $1.hour })[indexPath.row]
cell.labelTime.text = showHour.showHour()
cell.dataForShow = showHour.showFullTime()
//here may be set the height of cell?
return cell
}
upd Storyboard:
I Have an approach for you:
I used this in my code and m sharing with you
in TableView height method:
// MARK: - TableView Delegate Methods:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var height: CGFloat = 0.0
// // //print("arrTA[indexPath.row] = \(arrTA[indexPath.row])")
let dict = arrTA[indexPath.row] as! NSDictionary
let str = dict[ISSELECTED] as! String
let strFlage = dict[FLAGE] as! String
let arrFileCount = dict[DATA]!["file_list"] as! NSArray//dict.objectForKey(DATA)?.objectForKey("file_list").count as NSMutableArray
if(strFlage == "MA") {
height = 50.0
} else {
height = self.setExpandedViewHeight(arrFileCount.count) + 0.0
}
return height
}
And I used this function in managing height:
func setExpandedViewHeight(intArrCount: Int) -> CGFloat {
var height: CGFloat = 0.0
var generatedHeight: Int = 0
generatedHeight = intArrCount / 3 /// We get number of rows
if(intArrCount % 3 == 0) {
height = CGFloat(generatedHeight) * 100
} else {
height = (CGFloat(generatedHeight) + 1 ) * 100
}
return height
}
I hope you will read and debug the code at your end and execute it.
I decided my problem by adding one line:
tableTime.rowHeight = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {...}
If you are using autolayout then you can try this...
self.tableView.estimatedRowHeight = <estimatedRowHeight>;
self.tableView.rowHeight = UITableViewAutomaticDimension;
UITableViewAutomaticDimension will work if you've set leading, trailing, bottom, and top constraints relative to cell container view.
estimatedRowHeight : set this value with possible row height.
When you have set these properties, you neither need to implement heightForRowAtIndexpath nor estimatedRowHeight.
Related
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
}
I have a collectionview embedded within a tableview. The collectionview can have either a vertical or horizontal flowlayout. When the user makes an action, the tableviewcell height changes from 200 to full tableview height and the collection view changes flowlayout. However, the collectionView flickers since there are not enough cells loaded yet when performing the layout change from horizontal to vertical. Also, when reverting to the horizontal flowLayout, there is no transition at all. Not sure why the transition occurs when changing flowLayout from horizontal to vertical but not vertical to horiztontal. How can I avoid the flicker when changing the layout and continue to utilize Apple's transition animation when reverting to the horizontal layout?
GitHub
class ViewController: UIViewController {
// If horizontal layout, then display 3 rows (with heights 200)
// If veritcal layout, then display 1 row (with height of full tableview height)
var isHorizontal = true
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.dataSource = self
tableView.delegate = self
}
}
#IBAction func barButtonTapped(_ sender: UIBarButtonItem) {
isHorizontal = !isHorizontal
sender.title = isHorizontal ? "Vertical" : "Horizontal"
tableView.reloadData()
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return isHorizontal ? 3 : 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EmbeddedTableViewCell", for: indexPath) as! EmbeddedTableViewCell
cell.setup(isHorizontal: isHorizontal)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return isHorizontal ? 200 : tableView.frame.height
}
}
class EmbeddedTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView! {
didSet {
collectionView.dataSource = self
collectionView.delegate = self
}
}
fileprivate let horizontalFlowLayout: UICollectionViewFlowLayout = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.minimumInteritemSpacing = 10
flowLayout.minimumLineSpacing = 10
flowLayout.scrollDirection = .horizontal
return flowLayout
}()
fileprivate let verticalFlowLayout: UICollectionViewFlowLayout = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.minimumInteritemSpacing = 10
flowLayout.minimumLineSpacing = 10
flowLayout.scrollDirection = .vertical
return flowLayout
}()
func setup(isHorizontal: Bool) {
guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
if isHorizontal && flowLayout.scrollDirection != .horizontal {
collectionView.setCollectionViewLayout(horizontalFlowLayout, animated: true)
} else if !isHorizontal && flowLayout.scrollDirection != .vertical {
collectionView.setCollectionViewLayout(verticalFlowLayout, animated: true)
}
}
}
extension EmbeddedTableViewCell: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SimpleCollectionViewCell", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: 150)
}
}
I know that adjusting UILabel height and UITableViewCell is probably a pretty standard issue, but I'm finding lots of answers that are based on the storyboard and inspectors, but not by just using Swift 3. I've created a table which covers the screen. The height for the cell is being determined as follows:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
My tableViewCell has a couple of objects in it, a UIImageView (myImage) and a UILabel (myText), set-up in a custom class. The positioning and sizing takes place in cellForRowAt.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
cell.myImage.image = UIImage(named: "MyImage")
cell.myImage.layer.frame = CGRect(origin: CGPoint(x: 0, y: 10), size: (cell.myImage?.image?.size)!)
cell.myText.text = myArray[indexPath.row]
cell.myText.frame = CGRect(x: UIScreen.main.bounds.size.width * 0.25, y: 0, width: UIScreen.main.bounds.size.width * 0.7, height: 90)
cell.myText.numberOfLines = 0
return cell
}
The result is a bunch of stacked cells, overlapping each other. What should I do to adjust the height of the UILabel frame to the amount of text coming from myArray and adjust the cell height so that it's at least the height of myImage or myText?
You can make multiline label inside table view in Swift 3.
import UIKit
private let useAutosizingCells = true
class TableViewController: UITableViewController {
fileprivate let cellIdentifier = "Cell"
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
//setup initial row height here
if useAutosizingCells && tableView.responds(to: #selector(getter: UIView.layoutMargins)) {
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
}
}
// MARK: - UITableViewDataSource
extension TableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return detailsModel.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
let details = detailsModel[indexPath.row]
cell.textLabel?.text = details.title
cell.detailTextLabel?.text = details.description
if useAutosizingCells && tableView.responds(to: #selector(getter: UIView.layoutMargins)) {
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
}
return cell
}
}
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:
I have a collectionView and my collectionViewCell contains an imageView, so when I'm loading my view for the first time it seems fine, but when I scroll through it, it gets misplaced , see the images
i have 3 images in my collectionView cell the first image is at place but not the second and third images
before scrolling (image1)
after scrolling (image2) (when am horizontally switching to next image)
my collectinView supports Horizontal Scroll and (pink background) is my cell , (yellow background) is CollectionView as you can see once I scroll to next image my cell gets a little out of screens, any idea how to fix this ???
my codes :
in tableView:
extension TableViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return contentImages[collectionView.tag].count
}
func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell",
forIndexPath: indexPath) as! CollectionViewCell
//cell.imgView.frame = CGRectMake(0, 0, collectionView.frame.width, collectionView.frame.height)
cell.imgViewOfCell.contentMode = .ScaleAspectFit
cell.imgViewOfCell.image = ResizeImage(UIImage(named: contentImages[collectionView.tag][indexPath.item])!, targetSize: CGSizeMake( cell.imgViewOfCell.frame.width , cell.imgViewOfCell.frame.height))
//imageView.image = UIImage(named: imageModel[collectionView.tag][indexPath.item])
return cell
}
}
collectionViewCell :
class CollectionViewCell: UICollectionViewCell {
#IBOutlet var imgViewOfCell: UIImageView!
override func awakeFromNib() {
imgViewOfCell.frame = self.bounds
self.contentView.setNeedsLayout()
}
}
in tableViewCell:
func setCollectionViewDataSourceDelegate
<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>
(dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.reloadData()
}
var collectionViewOffset: CGFloat {
get {
return collectionView.contentOffset.x
}
set {
collectionView.contentOffset.x = newValue
}
}
Any idea of how to fix this? Please let me know what am doing wrong or what I'm missing.
ended up with this ,after debugging i got to know that my Cell's x origin was not accurate , so am calculating this manually on my cellForItemAtIndexPath like this :
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell",
forIndexPath: indexPath) as! CollectionViewCell
cell.imgViewOfCell.contentMode = .ScaleAspectFit
if indexPath.row == 0 {
cell.frame.origin.x = 0
}else if indexPath.row == 1 {
cell.frame.origin.x = self.view.bounds.width
}else if indexPath.row == 2 {
cell.frame.origin.x = (self.view.bounds.width*2)
}else if indexPath.row == 3 {
cell.frame.origin.x = (self.view.bounds.width*3)
}
....... }