I use this function to add background view in my TableView when it's empty.
func emptyTableViewMessage(with message: String) {
let messageLabel: UILabel = UILabel(frame: CGRect(x: todoTableView.bounds.size.width/2, y: todoTableView.bounds.size.height / 2 , width: todoTableView.bounds.size.width, height: todoTableView.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = UIColor.gray
messageLabel.font = UIFont(name: "Open Sans", size: 15)
messageLabel.textAlignment = .center
todoTableView.backgroundView = messageLabel
}
I call it in numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearching {
if searchedData?.count == 0 {
emptyTableViewMessage(with: "Not found")
} else {
emptyTableViewMessage.backgroundView = .none
}
return (searchedData?.count)!
}
But if I have few section, For example one of them is empty, how Can I add this kind of backgroundView inside the section (not in the header, because I have different items in the header)
Many thanks
First, you could created an empty cell prototype, this one will be use when there is no data to show. Then let's modify your emptyTableViewMessage method to return a UILabel.
func emptyTableViewMessage(with message: String) -> UIView {
let messageLabel: UILabel = UILabel(frame: CGRect(x: table.bounds.size.width/2, y: table.bounds.size.height / 2 , width: table.bounds.size.width, height: table.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = UIColor.gray
messageLabel.font = UIFont(name: "Open Sans", size: 15)
messageLabel.textAlignment = .center
return messageLabel
}
In numberOfRowsInSection when there is no data to show lets return 1 (to display the Not Found text). If there is data to show let's return how many rows.
guard let dataFound = searchedData?.count else { return 1 }
return dataFound > 0 ? dataFound : 1
Finally in cellForRowAt you dequeue the appropriate prototype cell based on searchData?.count.
let notFound = searchData?.count > 0 ?? false
guard let cell = tableView.dequeueReusableCell(withIdentifier: notFound ? "NotFoundCellId" : "CellId") else { return UITableViewCell() }
if notFound {
cell.backgroundView = emptyTableViewMessage(with: "Not Found")
} else {
cell.textLabel?.text = countries[indexPath.row]
}
I quickly created a small project to test this and seems to work fine, you can check it at Github. The project doesn't manage the data how it should be done for a production code, the search bar doesn't displays well on simulator for iPhone X and newer, etc.; it's just a quick sample on how to display the Not Found text in a table view with many sections so you can have an idea how to do it.
Some screenshots:
No filter
Filtered by "ica"
Filtered by "an"
Hope this helps you!
Related
So my goal is to properly separate the cells accessory. Here is my first set of cells when the segment control is at the first index. The accessory type is a normal disclosure indicator.
Now when I switch the value of the segment index, i set my cells with a custom accessory view.
Now the issue is when I switch back to the first segment, the custom accessory view comes over to the first 2 cells as well like so :
I just want to figure out how I can prevent this from happening and keep the cell accessories properly separated. I will attach my necessary code for the issue.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.CellDetails.studentDashCell, for: indexPath)
let cellImage = UIImage(systemName: "checkmark.seal.fill")
let cellImageView = UIImageView(image: cellImage)
cellImageView.tintColor = #colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)
cellImageView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
if selectorOfEvents.selectedSegmentIndex == 0 {
cell.textLabel?.font = UIFont(name: Constants.AppFonts.consistentFont, size: 22)
cell.textLabel?.text = gothereEvents[indexPath.row].eventName
cell.accessoryType = .disclosureIndicator
} else if selectorOfEvents.selectedSegmentIndex == 1 {
cell.textLabel?.font = UIFont(name: Constants.AppFonts.menuTitleFont, size: 22)
cell.textLabel?.text = gotherePurchasedEventNames[indexPath.row].purchasedEventName
cell.accessoryView = cellImageView
}
return cell
}
The cellForRowAt() method. ^
#IBAction func eventsSegmented(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
navigationItem.title = "Events"
tableView.reloadData()
} else {
navigationItem.title = "Purchased Events"
tableView.reloadData()
}
}
The IBAction func for the segmented control. Thanks in advance.
My suspicion is that the accessory view / type is getting carried over on some dequeued cells. You should explicitly set both the accessoryView and cell.accessoryType type in both cases:
if selectorOfEvents.selectedSegmentIndex == 0 {
cell.textLabel?.font = UIFont(name: Constants.AppFonts.consistentFont, size: 22)
cell.textLabel?.text = gothereEvents[indexPath.row].eventName
cell.accessoryType = .disclosureIndicator
cell.accessoryView = nil //<-- here
} else if selectorOfEvents.selectedSegmentIndex == 1 {
cell.textLabel?.font = UIFont(name: Constants.AppFonts.menuTitleFont, size: 22)
cell.textLabel?.text = gotherePurchasedEventNames[indexPath.row].purchasedEventName
cell.accessoryView = cellImageView
cell.accessoryType = .none //<-- here
}
I'm trying to generate cells and put labels inside of it. However, when i scroll down labels got mixed between cells. Here is my code and i'm trying to solve it.
let lblTitle = UILabel()
let lblMetro = UILabel()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: indexPath) as? UICustomCollectionViewCell
if indexPath.row == 0 {
lblTitle.frame = CGRect(x: 0, y: 0, width: 195, height: 40)
lblTitle.font = UIFont.systemFont(ofSize: 14)
lblTitle.textColor = UIColor.white
lblTitle.backgroundColor = colorLiteral(red: 0.2122299671, green: 0.4379466176, blue: 0.8993332386, alpha: 1)
lblTitle.text = " 1”
cell?.contentView.addSubview(lblTitle)
}
if indexPath.row == 1 {
lblMetro.frame = CGRect(x: 55, y: 290, width: 100, height: 20)
lblMetro.font = UIFont.boldSystemFont(ofSize: 17)
lblMetro.textColor = colorLiteral(red: 0, green: 0.3117707968, blue: 0.5609284043, alpha: 1)
lblMetro.text = “2”
cell?.contentView.addSubview(lblMetro)
}
return cell ?? UICollectionViewCell()
}
}
Here cells are dequeued
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: indexPath) as? UICustomCollectionViewCell
so you might get a 1 with previously added label , you need to clear them after dequeue , it would be messy but it's better to isolate the vc's labels from the cells 1 so add them inisde the cell configure or make them an outlets , to remove give them a tag and after the above line do
cell.contentView.subviews.forEach {
if $0.tag == 200 {
$0.removeFromSuperview()
}
}
Not optimised but this might solve it, remove the subview from superview before adding it:
cell?.contentView.lblTitle.removeFromSuperview()
cell?.contentView.addSubview(lblTitle)
And:
cell?.contentView.lblMetro.removeFromSuperview()
cell?.contentView.addSubview(lblMetro)
I suggest using a very rarely used method of UICollectionViewCell or UITableViewCell prepareForReuse.
In definition of UICustomCollectionViewCell insert the function:
class UICustomCollectionViewCell: UICollectionViewCell {
func prepareForReuse() {
// This method is immediately called when a cell is about to be dequeued.
super.prepareForReuse()
if let view = contentView.viewWithTag(100) {
view.removeFromSuperView()
}
if let view = contentView.viewWithTag(101) {
view.removeFromSuperView()
}
}
}
Then give tags to the labels
lblMetro.tag = 100
lblTitle.tag = 101
This solution is efficient if you only use a limited labels and cells. For a more generic approach create labels dynamically and share the tag. In the prepareForReuse() just remove subview with that tag.
I have created a Collapsible table view using sections and which in-turn contains a single row.Initially tableview has 4 sections and My last section has ability of adding 3rd section multiple times when the user taps on the last.
Now am facing an issue when the user expands the section and enters the data and after that when he expands the dynamically added section the same data appearing in the newly added only also and it is vice-versa if the user modifies any data on newly added section row then it is overriding the existing content in 3rd section too.
The code is as follows:
override func viewDidLoad() {
super.viewDidLoad()
//creating section items where 1st three are static and the third section will add dynamically when the last section tapped by the user.
tableMenuItems = [["sectionHeader": "Ship To","isCollapsed":true, "image" : "shipto.png" ],
["sectionHeader": "Ship From","isCollapsed":true,"image" : "shipfrom.png"],
["sectionHeader": "Package Details","isCollapsed":true,"image" : "package.png"],
["sectionHeader": "Add a Package","isCollapsed":true,"image" : "newaddpack.png"]]
}
func numberOfSections(in tableView: UITableView) -> Int {
return tableMenuItems.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let isCollapsed = tableMenuItems[section]["isCollapsed"] as! Bool
return isCollapsed ? 0 : 1
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: tableView.frame.size.width, height: 120))
view.tag = 200 + section
view.backgroundColor = .white
let prefixIconBtn = UIButton.init(frame: CGRect.init(x: 12, y: 20, width: 48, height: 48))
prefixIconBtn.setImage(UIImage.init(named: tableMenuItems[section]["image"] as! String), for: .normal)
prefixIconBtn.tag = 500 + section
prefixIconBtn.addTarget(self, action: #selector (displayOrHideSection(_sender:)), for: .touchUpInside)
let titleLabel = UILabel.init(frame: CGRect.init(x: 75, y: 18, width: 250, height: 60))
titleLabel.tag = 100 + section
let sectionHeaderBtn = UIButton.init(frame: titleLabel.frame)
sectionHeaderBtn.addTarget(self, action: #selector (displayOrHideSection(_sender:)), for: .touchUpInside)
sectionHeaderBtn.tag = section
titleLabel.font = UIFont(name:"SFUIText-Regular", size: 20.0)
let plusOrMinus = UIButton.init(frame: CGRect.init(x: 340, y: 20, width: 38, height: 38))
plusOrMinus.tag = section
plusOrMinus.addTarget(self, action: #selector (displayOrHideSection(_sender:)), for: .touchUpInside)
titleLabel.text = tableMenuItems[section]["sectionHeader"] as? String
if titleLabel.text == "Add a Package" {
plusOrMinus.isHidden = true
}
if tableMenuItems[section]["isCollapsed"] as! Bool == true
{
plusOrMinus.setBackgroundImage(UIImage.init(named: "Log_plus"), for: .normal)
tableMenuItems[section]["isCollapsed"] = true
}
else
{
plusOrMinus.setBackgroundImage(UIImage.init(named: "Log_minus"), for: .normal)
tableMenuItems[section]["isCollapsed"] = false
}
view.addSubview(titleLabel)
view.addSubview(prefixIconBtn)
view.addSubview(sectionHeaderBtn)
view.addSubview(plusOrMinus)
plusOrMinus.tag = section
let seperatorLine = UILabel.init(frame: CGRect.init(x: 78, y: 78, width: tableView.frame.size.width, height: 1))
seperatorLine.backgroundColor = UIColor.lightGray
view.addSubview(seperatorLine)
return view
}
#objc func displayOrHideSection( _sender : AnyObject)
{
let section = _sender.tag
if section == 0
{
if tableMenuItems[0]["isCollapsed"] as! Bool == false
{
tableMenuItems[0]["isCollapsed"] = true
}
else
{
tableMenuItems[0]["isCollapsed"] = false
}
}
else if section == 1
{
if tableMenuItems[1]["isCollapsed"] as! Bool == false
{
tableMenuItems[1]["isCollapsed"] = true
}
else
{
tableMenuItems[1]["isCollapsed"] = false
}
}
else if section == 2
{
if tableMenuItems[2]["isCollapsed"] as! Bool == false
{
tableMenuItems[2]["isCollapsed"] = true
}
else
{
tableMenuItems[2]["isCollapsed"] = false
}
}
else if section! >= 3
{
if section! > 502
{
// to delete a dynamically added package
tableMenuItems.remove(at: section! - 500)
}
else if section! < 502
{
// adding a new package section when the user taps on Add a package section(Last section)
let titleLabel = self.logTable?.viewWithTag(100 + section!) as! UILabel
print(titleLabel)
// when add a package tapped
if titleLabel.text == "Add a Package"
{
tableMenuItems.insert(["sectionHeader": "Package Details","isCollapsed":true,"image" : "package.png"], at: tableMenuItems.count-1)
}
// when plus or minus button tapped on Add a package
else
{
if tableMenuItems[section!]["isCollapsed"] as! Bool == false
{
// expanding section
tableMenuItems[section!]["isCollapsed"] = true
tableMenuItems[section!]["image"] = "package.png"
}
else
{
// collapsing section
tableMenuItems[section!]["isCollapsed"] = false
tableMenuItems[section!]["image"] = "newdeletepackage.png"
}
}
}
}
logTable!.reloadData()
}
So, I have a carousel of "BillSplitters" and on each carousel item it should display the uniques items a BillSplitter is having. So I'm getting fatal error: unexpectedly found nil while unwrapping an Optional value Normally i can slowly hone in on an error like this i find the issue but when following on from a breakpoint line by line it enters into the iCarousel code which i cant follow. Im also sure theres nothing going wrong in i carousel as if i dont addSubview(tableView) then it runs fine. It also seems to create the first couple of tableviews and add them fine and then gets the error. Here is the code im using:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let splitter = allBillSplitters[carouselIndex]
if (splitter.items?.count)! > 0 {
return (splitter.items?.count)!
} else {
TableViewHelper.EmptyMessage("\(splitter.name!) has no items to pay for.\nGo back to assign some items to their name.", tableView: tableView)
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ItemCell = tableView.dequeueReusableCell(withIdentifier: "SplitterItemCell") as! ItemCell
let itemsSet = allBillSplitters[carouselIndex].items
let items = itemsSet?.allObjects as! [Item]
let item = items[indexPath.row]
let count = item.billSplitters?.count
if count! > 1 {
cell.name!.text = "\(item.name!) split \(count!) ways"
cell.price!.text = "£\(Double(item.price)/Double(count!))"
} else {
cell.name!.text = item.name!
cell.price!.text = "£\(item.price)"
}
return cell
}
func numberOfItems(in carousel: iCarousel) -> Int {
return allBillSplitters.count
}
I've read in a few places that I should remove the if let view = view statement in the following function as it's not re-using the items and always creating new ones. If I leave it in I get the same error immediately when creating the first carousel item and when I remove it, it happens on the creating the third i carousel item.
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
carouselIndex = index
var splitterView: UIImageView
var nameLabel: UILabel
var emailLabel: UILabel
var totalLabel: UILabel
var tableView: UITableView
let splitter = allBillSplitters[index]
//reuse view if available, otherwise create a new view
if let view = view as? UIImageView {
splitterView = view
//get a reference to the label in the recycled view
nameLabel = splitterView.viewWithTag(1) as! UILabel
emailLabel = splitterView.viewWithTag(2) as! UILabel
totalLabel = splitterView.viewWithTag(3) as! UILabel
tableView = splitterView.viewWithTag(4) as! UITableView
} else {
let height = carousel.contentView.frame.height - 85
let width = carousel.contentView.frame.width - 80
//don't do anything specific to the index within
//this `if ... else` statement because the view will be
//recycled and used with other index values later
splitterView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
splitterView.layer.cornerRadius = 10
splitterView.clipsToBounds = true
splitterView.image = UIImage(data: splitter.image as! Data, scale:1.0)
splitterView.contentMode = .scaleAspectFit
splitterView.backgroundColor = UIColor(netHex: 0xCA9875)
let viewWidth = Int(splitterView.frame.width)
nameLabel = UILabel(frame: CGRect(x: 5, y: 0, width: viewWidth, height: 30))
nameLabel.backgroundColor = .clear
nameLabel.backgroundColor?.withAlphaComponent(0.1)
nameLabel.textAlignment = .left
nameLabel.font = nameLabel.font.withSize(20)
nameLabel.tag = 1
emailLabel = UILabel(frame: CGRect(x: 5, y: 30, width: viewWidth, height: 15))
emailLabel.backgroundColor = .clear
emailLabel.textAlignment = .left
emailLabel.font = emailLabel.font.withSize(15)
emailLabel.tag = 2
totalLabel = UILabel(frame: CGRect(x: 5, y: 45, width: viewWidth, height: 15))
totalLabel.backgroundColor = .clear
totalLabel.textAlignment = .left
totalLabel.font = totalLabel.font.withSize(15)
totalLabel.tag = 3
let tableViewHeight = height - 65
let frame = CGRect(x: 0, y: 65, width: width, height: tableViewHeight)
tableView = UITableView(frame: frame)
tableView.delegate = self
tableView.dataSource = self
tableView.tag = 4
totalLabel.backgroundColor = .clear
splitterView.addSubview(nameLabel)
splitterView.addSubview(emailLabel)
splitterView.addSubview(totalLabel)
splitterView.addSubview(tableView)
}
//set item label
//remember to always set any properties of your carousel item
//views outside of the `if (view == nil) {...}` check otherwise
//you'll get weird issues with carousel item content appearing
//in the wrong place in the carousel
nameLabel.text = "\(allBillSplitters[index].name!)"
emailLabel.text = "\(allBillSplitters[index].email!)"
totalLabel.text = "£\(allBillSplitters[index].total)"
return splitterView
}
func carousel(_ carousel: iCarousel, valueFor option: iCarouselOption, withDefault value: CGFloat) -> CGFloat {
switch option {
case .spacing:
return value * 1.2
case .fadeMin:
return 0.0
case .fadeMinAlpha:
return 0.3
case .fadeMax:
return 0.0
default:
return value
}
}
I've looked all over and can't find a solution so any help would be great. Thanks
I'm an idiot. Forgot the following:
tableView.register(CarouselTableViewCell.classForCoder(), forCellReuseIdentifier: "carouselTableViewCell")
in tableviews cellForRowAt function
We have a portion of our UI which is a small list of labels with color swatches next to them. The design I'm taking over has six of these hard-coded in the layout even though the actual data is dynamic, meaning if we only need to show three, we have to explicitly hide three, which also throws off the balance of the page. Making matters worse is each one of those 'items' is actually made up of several sub-views so a screen with six hard-coded items has eighteen IBOutlets.
What I'm trying to do is to instead use a UITableView to represent this small portion of the screen, and since it won't scroll, I was wondering if you can use AutoLayout to configure the intrinsic content height of the UITableView to be based on the number of rows.
Currently I have a test page with a UITableView vertically constrained to the center, but without a height constraint because I am hoping to have the table's intrinsic content size reflect the visible rows. I have also disabled scrolling on the table. When I reload the table, I call updateConstraints. But the table still does not resize.
Note: We can't use a UIStackView (which would have been perfect for this) because we have to target iOS8 and that wasn't introduced until iOS9, hence this solution.
Has anyone been able to do something similar to our needs?
Ok, so unlike UITextView, it doesn't look like UITableView ever returns an intrinsic size based on the visible rows. But that's not that big a deal to implement via a subclass, especially if there's a single section, no headers or footers, and the rows are of a fixed height.
class AutoSizingUiTableView : UITableView
{
override func intrinsicContentSize() -> CGSize
{
let requiredHeight = rowCount * rowHeight
return CGSize(width: UIView.noIntrinsicMetric, height: CGFloat(requiredHeight))
}
}
I'll leave it up to the reader to figure out how to get their own rowCount. The same if you have variable heights, multiple sections, etc. You just need more logic.
By doing this, it works great with AutoLayout. I just wish it handled this automatically.
// Define this puppy:
class AutoTableView: UITableView {
override func layoutSubviews() {
super.layoutSubviews()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
get {
var height:CGFloat = 0;
for s in 0..<self.numberOfSections {
let nRowsSection = self.numberOfRows(inSection: s)
for r in 0..<nRowsSection {
height += self.rectForRow(at: IndexPath(row: r, section: s)).size.height;
}
}
return CGSize(width: UIView.noIntrinsicMetric, height: height)
}
set {
}
}
}
and make it your class in IB.
obs: this is if your class is only cells and shit. if it has header, footer or some other thign, dunno. it'll not work. for my purposes it works
peace
This can be done, please see below for a very simple (and rough - rotation does not work properly!) example, which allows you to update the size of the table view by entering a number in the text field and resetting with a button.
import UIKit
class ViewController: UIViewController {
var tableViewController : FlexibleTableViewController!
var textView : UITextView!
var button : UIButton!
var count : Int! {
didSet {
self.refreshDataSource()
}
}
var dataSource : [Int]!
let rowHeight : CGFloat = 50
override func viewDidLoad() {
super.viewDidLoad()
// Configure
self.tableViewController = FlexibleTableViewController(style: UITableViewStyle.plain)
self.count = 10
self.tableViewController.tableView.backgroundColor = UIColor.red
self.textView = UITextView()
self.textView.textAlignment = NSTextAlignment.center
self.textView.textColor = UIColor.white
self.textView.backgroundColor = UIColor.blue
self.button = UIButton()
self.button.setTitle("Reset", for: UIControlState.normal)
self.button.setTitleColor(UIColor.white, for: UIControlState.normal)
self.button.backgroundColor = UIColor.red
self.button.addTarget(self, action: #selector(self.updateTable), for: UIControlEvents.touchUpInside)
self.layoutFrames()
// Assemble
self.view.addSubview(self.tableViewController.tableView)
self.view.addSubview(self.textView)
self.view.addSubview(self.button)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func refreshDataSource() -> Void {
if let _ = self.dataSource {
if !self.dataSource.isEmpty {
self.dataSource.removeAll()
}
}
else
{
self.dataSource = [Int]()
}
for count in 0..<self.count {
self.dataSource.append(count)
}
self.tableViewController.dataSource = self.dataSource
self.tableViewController.tableView.reloadData()
if let _ = self.view {
self.layoutFrames()
self.view.setNeedsDisplay()
}
}
func updateTable() -> Void {
guard let _ = self.textView.text else { return }
guard let validNumber = Int(self.textView.text!) else { return }
self.count = validNumber
}
func layoutFrames() -> Void {
if self.tableViewController.tableView != nil {
self.tableViewController.tableView.frame = CGRect(origin: CGPoint(x: self.view.frame.width / 2 - 100, y: 100), size: CGSize(width: 200, height: CGFloat(self.dataSource.count) * self.rowHeight))
NSLog("\(self.tableViewController.tableView.frame)")
}
if self.textView != nil {
self.textView.frame = CGRect(origin: CGPoint(x: 50, y: 100), size: CGSize(width: 100, height: 100))
}
if self.button != nil {
self.button.frame = CGRect(origin: CGPoint(x: 50, y: 150), size: CGSize(width: 100, height: 100))
}
}
}
class FlexibleTableViewController : UITableViewController {
var dataSource : [Int]!
override init(style: UITableViewStyle) {
super.init(style: style)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell()
cell.frame = CGRect(origin: CGPoint(x: 10, y: 5), size: CGSize(width: 180, height : 40))
cell.backgroundColor = UIColor.green
return cell
}
}
Whether it is a good idea or not, is, as has been pointed out, another question! Hope that helps!
Version from no_ripcord accounting for header and footer height
final // until proven otherwise
class IntrinsicallySizedTableView: UITableView {
override func layoutSubviews() {
super.layoutSubviews()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
guard let dataSource = self.dataSource else {
return super.intrinsicContentSize
}
var height: CGFloat = (tableHeaderView?.intrinsicContentSize.height ?? 0)
+ contentInset.top + contentInset.bottom
if let footer = tableFooterView {
height += footer.intrinsicContentSize.height
}
let nsections = dataSource.numberOfSections?(in: self) ?? self.numberOfSections
for section in 0..<nsections {
let sectionheader = rectForHeader(inSection: section)
height += sectionheader.height
let sectionfooter = rectForFooter(inSection: section)
height += sectionfooter.height
let nRowsSection = self.numberOfRows(inSection: section)
for row in 0..<nRowsSection {
height += self.rectForRow(at: IndexPath(row: row, section: section)).size.height
}
}
return CGSize(width: UIView.noIntrinsicMetric, height: height)
}
}