TableView in iCarousel finding nil while unwrapping optional value - swift

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

Related

Adding multiples labels (views) side-by-side dinamically in UITableViewCell

I would like to put programmatically buttons side-by-side in a cell and add actions, but firstly I am doing with UILabel to see how works. The problem is with UILabels isn't working.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let array = ["house", "bugs", "perl"] //comes from API
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! DetailViewCell
var hStackView = UIStackView()
var vStackView = UIStackView()
vStackView.axis = .vertical
vStackView.spacing = 8
vStackView.alignment = .top
var count: Int = 0
for ar in array {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 21)) //CGRectZero)
label.text = ar
label.textColor = .black
if count % 2 == 0 {
hStackView = UIStackView()
hStackView.axis = .horizontal
hStackView.spacing = 8
hStackView.alignment = .fill
hStackView.distribution = .fill
hStackView.addArrangedSubview(label)
if (count + 1) == array.count {
vStackView.addArrangedSubview(hStackView)
}
}
else {
hStackView.addArrangedSubview(label)
vStackView.addArrangedSubview(hStackView)
}
count += 1
}
cell.addSubview(vStackView)
cell.layoutIfNeeded()
return cell
}
You don't see anything because your vertical UIStackView does not know where it should be placed in your cell and how large it should be. You can do that with layout constraints:
vStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cell.leadingAnchor.constraint(equalTo: vStackView.leadingAnchor),
cell.trailingAnchor.constraint(equalTo: vStackView.trailingAnchor),
cell.topAnchor.constraint(equalTo: vStackView.topAnchor),
cell.bottomAnchor.constraint(equalTo: vStackView.bottomAnchor)
])
With the first line you tell it to use constraints for layouting. With the NSLayoutConstraints.activate you tell it how the vStackViews bounds are tied to the cell views bounds. In this case the vStackView now should take up the entire space provided by the cell.

BackgroundView for section in the tableView

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!

Carousel not displaying in iOS 11.3 (11.2 works fine)

Exact case:
I have a tableViewController presented as a popover in my app.
The first cell in the popover has an iCarousel in it (liner to be precise).
https://github.com/nicklockwood/iCarousel
Implemented all the datasource and delegate functions. They all get fired...
In iOS 11.2 all work as intended, but in iOS 11.3 the popover doesn't show up the iCarousel items. All the delegates still fire (fe. didSelect; didScroll). In the interface debugger mode I can see the items, but not on device.
Tried to reload the data after the initial setup / tried to remove all images from the item and leave it only with pink background / tried to force set alpha to 1 / tried to bringSubviewToFront / eliminated all background effects / modified the tableViewController to a table View and the cell to a view. Nothing works. The most annoying thing is that it shows in the interface debugger and I can't seem to get it working on the actual device.
Popover Presentation:
let storyboard = UIStoryboard(name: "SomeSTBName", bundle: nil)
let popControllerNav = storyboard.instantiateViewController(withIdentifier: "pTVCIdentifier")
let popController = popControllerNav as! PTVC
popController.dissmissDelegate = self
popController.t = t
popController.modalPresentationStyle = UIModalPresentationStyle.popover
popController.preferredContentSize = CGSize(width: 555, height: 120)
popController.presentedFrom = presentedFrom
popControllerNav.popoverPresentationController?.permittedArrowDirections = [.up, .down]
popControllerNav.popoverPresentationController?.backgroundColor = UIColor(red: 17.0/255.0, green: 17.0/255.0, blue: 71.0/255.0, alpha: 1.0)
popControllerNav.popoverPresentationController?.popoverLayoutMargins = UIEdgeInsets.init(top: 0, left: 100, bottom: 0, right: 0)
popControllerNav.popoverPresentationController?.delegate = self
popControllerNav.popoverPresentationController?.sourceView = mySender // button
popControllerNav.popoverPresentationController?.sourceRect = mySender.bounds
if(NViewController.instance?.pressedS != nil) {
popController.popoverPresentationController?.passthroughViews = [(NViewController.instance?.pressedS)!]
}
// present the popover
self.present(popControllerNav, animated: true, completion: nil)
Cell for row in TVC:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var returnCell = UITableViewCell()
if let upperCarousel = tableView.dequeueReusableCell(withIdentifier: "carouselPCell") as? CaruselPCell {
let playlists = CoreDataStack.fetchAllP() as? [UP]
upperCarousel.allP = p
upperCarousel.setup()
upperCarousel.delegate = self
returnCell = upperCarousel
}
return returnCell
}
iCarousel setup:
func setup(){
carusel = iCarousel(frame: CGRect(x: -415, y: 0, width: 950, height: 125))
carusel.scrollSpeed = 0.6
carusel.centerItemWhenSelected = false
carusel.isPagingEnabled = false
carusel.type = .linear
carusel.dataSource = self
carusel.delegate = self
carusel.bounces = false
carusel.reloadData()
carusel.clipsToBounds = true
self.contentView.addSubview(carusel)
}
CGRect(x: -415, y: 0, width: 950, height: 125) because I don't want the first item to be center, but aligned to the left - it doesn't generate the problem had tested

TableView rows dissapear when tapped

I have a table view embedded in an iCarousel item and firstly I can't seem to set the view frame whether it's in viewDidLayoutSubviews() but also when I tap the Tableview all the cells disappear. I have created the Tableview in its own ViewController as each carousel item will need editing buttons for the Tableviews they contain. If I don't put the Tableview in its own view controller the cells remain but I get an index out of range when scrolling a lot of views and I can't add editing buttons. I've run out of ideas and would love some pointers or tips.
class BillSplitterTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
var splitter: BillSplitter?
var tableView: UITableView!
var viewFrame: CGRect?
init(frame: CGRect, splitter: BillSplitter) {
super.init(nibName: nil, bundle: nil)
self.splitter = splitter
self.viewFrame = frame
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:)")
}
override func viewDidLoad() {
super.viewDidLoad()
let subView = UIView(frame: viewFrame!)
view.backgroundColor = .clear
tableView = UITableView(frame: view.frame)
tableView.delegate = self
tableView.dataSource = self
tableView.frame = subView.frame
tableView.separatorStyle = .none
tableView.estimatedRowHeight = 35
let tableViewBackground = UIImageView(image: UIImage(data: splitter?.image as! Data, scale:1.0))
tableViewBackground.contentMode = .scaleAspectFit
tableViewBackground.frame = tableView.frame
tableView.backgroundView = tableViewBackground
subView.addSubview(tableView)
view.addSubview(subView)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (splitter?.items?.count)! > 0 {
return (splitter!.items?.count)!
} else {
TableViewHelper.EmptyMessage("\(splitter?.name!) has no items to pay for.", tableView: tableView)
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.register(SplitterCarouselItemTableViewCell.classForCoder(), forCellReuseIdentifier: "splitterCarouselItemTableViewCell")
let cell: SplitterCarouselItemTableViewCell = tableView.dequeueReusableCell(withIdentifier: "splitterCarouselItemTableViewCell") as! SplitterCarouselItemTableViewCell
let item = (splitter?.items?.allObjects as! [Item])[indexPath.row]
let count = item.billSplitters?.count
cell.backgroundColor = UIColor(netHex: 0xe9edef).withAlphaComponent(0.5)
if count! > 1 {
cell.name!.text = "\(item.name!)\nsplit \(count!) ways"
cell.price!.text = "£\(Double(item.price)/Double(count!))"
} else {
cell.name!.text = item.name!
cell.price!.text = "£\(item.price)"
}
return cell
}
}
the carousel viewcontroller:
func numberOfItems(in carousel: iCarousel) -> Int {
return allBillSplitters.count
}
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
let splitter = allBillSplitters[index]
carouselIndex = index
let splitterView = SplitterCarouselItemView(frame: CGRect(x: 0, y: 0, width: width, height: height))
let viewWidth = Int(splitterView.frame.width)
let nameLabel = SplitterCarouselItemNameLabel(frame: CGRect(x: 0, y: 0, width: viewWidth, height: 30))
let emailLabel = SplitterCarouselItemEmailLabel(frame: CGRect(x: 0, y: 30, width: viewWidth, height: 20))
let addItemButton = SplitterCarouselItemButton(frame: CGRect(x: viewWidth - 45, y: 50, width: 35, height: 35), addsItem: true)
let deleteItemButton = SplitterCarouselItemButton(frame: CGRect(x: 15, y: 50, width: 35, height: 35), addsItem: false)
let itemHeight = Int(splitterView.frame.height)
let payTotalButton = SplitterCarouselItemPayButton(frame: CGRect(x: 0, y: itemHeight - 35, width: viewWidth + 1, height: 35))
let tableViewHeight = Int(height - 130)
let frame = CGRect(x: 0, y: 85, width: viewWidth, height: tableViewHeight)
let tableView = BillSplitterTableView(frame: frame, splitter: splitter)
splitterView.addSubview(nameLabel)
splitterView.addSubview(emailLabel)
splitterView.addSubview(addItemButton)
splitterView.addSubview(deleteItemButton)
splitterView.addSubview(tableView.view)
splitterView.addSubview(payTotalButton)
nameLabel.text = "\(allBillSplitters[index].name!)"
emailLabel.text = "\(allBillSplitters[index].email!)"
payTotalButton.setTitle("Pay £\(allBillSplitters[index].total)", for: .normal)
return splitterView
}
func carousel(_ carousel: iCarousel, valueFor option: iCarouselOption, withDefault value: CGFloat) -> CGFloat {
if allBillSplitters.count > 2 {
switch option {
case .spacing:
return value * 1.05
case .fadeMin:
return 0.0
case .fadeMinAlpha:
return 0.3
case .fadeMax:
return 0.0
default:
return value
}
}
return value
}
Thanks for any help in advance

Can you get a UITableView's intrinsic content size to update based on the number of rows shown if scrolling is disabled?

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)
}
}