190

I am creating an app which will have a question in a UILabel and a multiple choice answers displayed in UITableView, each row showing a multiple choice. Questions and answers will vary, so I need this UITableView to be dynamic in height.

I would like to find a sizeToFit work around for the table. Where the table's frame is set to the height of all it's content.

Can anyone advise on how I can achieve this?

Brian
  • 11,132
  • 7
  • 32
  • 43
MiMo
  • 4,515
  • 3
  • 20
  • 23
  • For anyone looking to do a similar thing on OSX, see this question: http://stackoverflow.com/questions/11322139/get-height-of-content-in-nstableview – Michael Teper May 27 '14 at 21:46
  • 1
    @MiMo hi, can you explain what's wrong with the "sizeToFit" approach please ?:) – iamdavidlam Feb 03 '15 at 08:39
  • _“I would like to find a "sizeToFit" work around”_. So have you or haven't you tried UIView's `- sizeToFit` method? – Slipp D. Thompson May 06 '15 at 02:30
  • can I ask question related to this? I have used your approach but haven't set parent tableview height to child tableview content size – Tekhe Feb 22 '21 at 12:48

23 Answers23

161

Actually I found the answer myself.

I just create a new CGRect for the tableView.frame with the height of table.contentSize.height

That sets the height of the UITableView to the height of its content. Since the code modifies the UI, do not forget to run it in the main thread:

dispatch_async(dispatch_get_main_queue(), ^{
        //This code will run in the main thread:
        CGRect frame = self.tableView.frame;
        frame.size.height = self.tableView.contentSize.height;
        self.tableView.frame = frame;
    });
damien murphy.
  • 363
  • 2
  • 16
MiMo
  • 4,515
  • 3
  • 20
  • 23
  • 3
    It's possible to run into problems with this method. If you have a large list, it's possible that the height of the frame will cutoff some of your cells. You can see this happen if you have bouncing enabled and scroll to the bottom to see some cells bounce out of view. – whyoz Aug 21 '12 at 23:44
  • Where exactly did you specify the height? Did you make a call to reloadData and resize it afterwords? – James Oct 25 '12 at 18:57
  • 31
    Where's the best place to put this code? I tried it in viewDidLoad and it didn't work presumably because the tableview delegate methods hadn't fired yet. Which delegate method is best? – Kyle Clegg Nov 03 '12 at 02:16
  • 5
    I did this is viewDidAppear, and it seems to have worked. Note that the tableview might be a bit taller than its content because it leaves a bit of space for a section header and footer – Jameo Oct 16 '13 at 01:50
  • the question is where to place this code if I have a dynamic table. It means I have clickable section view and it shows or hides its rows on click – user2083364 Oct 16 '13 at 06:50
  • Works like charm, just dont forget to run that code inside the main thread with: dispatch_async(dispatch_get_main_queue()) { //modify UI elements here} ...otherwise, the changes will not be visible – Josh May 29 '15 at 10:25
  • 2
    I've found that viewDidAppear is a great place to put stuff you want to happen after all initial tableView cellForRowAtIndexPath calls have finished – rigdonmr Jun 07 '16 at 15:31
  • This solution is hacky because you have to write this everytime you need this behaviour. Also it leads to cluttered code because even more TableView-related code is spread all over your view controller class. Have a look at my answer: https://stackoverflow.com/a/48623673/4846592 that completely relies on Auto-Layout and is super generic :-) – heyfrank Feb 26 '18 at 10:57
  • @KyleClegg best part is where you are setting up array. Add this code in didSet block – Muhammad Waqas Sep 25 '19 at 03:09
  • Superb. You saved me boss. – Dipendra Sharma Oct 10 '20 at 21:49
158

Swift 5 and 4.2 solution without KVO, DispatchQueue, or setting constraints yourself.

This solution is based on Gulz's answer.

1) Create a subclass of UITableView:

import UIKit

final class ContentSizedTableView: UITableView {
    override var contentSize:CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

2) Add a UITableView to your layout and set constraints on all sides. Set the class of it to ContentSizedTableView.

3) You should see some errors, because Storyboard doesn't take our subclass' intrinsicContentSize into account. Fix this by opening the size inspector and overriding the intrinsicContentSize to a placeholder value. This is an override for design time. At runtime it will use the override in our ContentSizedTableView class


Update: Changed code for Swift 4.2. If you're using a prior version, use UIViewNoIntrinsicMetric instead of UIView.noIntrinsicMetric

NSNoob
  • 5,320
  • 5
  • 35
  • 51
heyfrank
  • 4,302
  • 2
  • 26
  • 39
  • 2
    Very good answer, it worked out for me, thanks. One thing though - in my case I needed to change the class of the `tableView` to be `IntrinsicTableView` in the storyboard. I didn't need to embed my table view within another `UIView`. – dvp.petrov May 25 '18 at 11:57
  • Yeah you're right. I updated my answer. Step 2 could be read like you said. Of course I meant to set the `tableView` to `IntrinsicTableView`. Also an extra wrapping `UIView` isn't necessary. You can use any existing parent view of course :) – heyfrank May 25 '18 at 14:15
  • 2
    **Swift 4** This worked for me just fine ``tableView.sizeToFit()`` – Soufiane ROCHDI Sep 20 '18 at 22:08
  • Yeah that may be a good solution, if your content is static. With my approach you can use Auto-Layout to embed a tableView with dynamic content in other views and keep it in full height all the time, without thinking about at what places `sizeToFit()` needs to be called – heyfrank Sep 21 '18 at 12:25
  • FYI: Improved naming and removed unnecessary explicit `self`s. – Rudolf Adamkovič Jan 25 '19 at 23:39
  • Great and simple asnwer! +1 – Ely Dantas Feb 14 '19 at 01:09
  • it worked for me, but had to add this: override func viewWillLayoutSubviews() { DispatchQueue.main.async { self.tableViewHeightConstraint.constant = self.tableView.contentSize.height } } – ivoroto Apr 09 '19 at 08:03
  • I have a splitviewcontroller and would like to put a UIImage at the bottom of a static table view in the master. Would this approach, sub-classing UITableView be the correct approach? – aklektik Jul 15 '19 at 19:32
  • No, this solution is for occurrences where you embed a table view inside some other view and need to have it exactly the height its content has. You should take a look at [tableFooterView](https://developer.apple.com/documentation/uikit/uitableview/1614976-tablefooterview) or [viewForFooterInSection](https://developer.apple.com/documentation/uikit/uitableview/1614976-tablefooterview) – heyfrank Jul 15 '19 at 19:52
  • 2
    Wonderful Thankyou!. Finally match parent and wrap content for tableview... – Amber K Jul 31 '19 at 13:44
  • This solution does not work if you are not using storyboards. – Amber K Aug 16 '19 at 09:17
  • I use it all the time in storyboards, did you follow all the steps? If yes, could you explain your view hierarchy? @AmberK – heyfrank Aug 16 '19 at 13:57
  • Yes the only issue is it doesn't accounts automatic content insets behavior eg if navigation bar and status bar height. In this case it scrolls within that range. – Amber K Aug 19 '19 at 09:51
  • This is the only solution I found working great for me. Any other answers made me find the last cell cut off or search more with disappointment . xD – KoreanXcodeWorker Oct 15 '19 at 06:23
  • Doesn't work in smaller screens when your tableview is inside a cell. – Amber K Jan 20 '20 at 05:35
  • This also works in scenarios where the tableview has a border and I wanted the border to cover only the visible cells, but making the bottom constraint a placeholder may cause the tableview to grow outside the screen. Instead, I unchecked the placeholder box and made the bottom constraint >= 0, and it worked perfectly! – alobaili Apr 21 '20 at 13:30
  • This is a perfect answer – emreoktem May 14 '20 at 15:57
  • How can i update tableView's height to match reloaded data's height? in my case after fetching data tableView doesnt want to relayout with new height. This works when data is provided before tho – Kam Wo May 18 '20 at 12:46
  • Two things: 1) make sure to set the bottom constraint to >= (greaterThanOrEqual) 2) If you're setting backgroundView for an empty table message, replace `intrinsicContentSize` with: ``` override var intrinsicContentSize: CGSize { layoutIfNeeded() if contentSize.height > 0 { return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height) } else { return super.intrinsicContentSize } } ``` – cuddergambino Oct 03 '20 at 21:34
  • This solution is great but I can't find any way to resize the tableView's height in an animated fashion. I have a resizable cell which (when used with a regular tableView) "pushes" the bottom of the tableView when expanded and shrinks the tableView when collapsed. I achieved this when using the extension but it just won't animate it and it looks really bad – Dante Puglisi Jan 08 '21 at 21:05
  • @fl034 can I ask question related to this? I have used your approach but haven't set parent tableview height to child tableview content size. – Tekhe Feb 22 '21 at 12:37
  • @Tekhe I'd recommend you to open a new question. My answer doesn't cover nested tableviews – heyfrank Feb 22 '21 at 12:58
  • @fl034 Thank you, yes I have already opened a new question. Do you mind if you have a time can check please? Link: https://stackoverflow.com/questions/66313730/avoid-scrollbar-in-nested-tableview-for-the-dynamic-tableview-swift – Tekhe Feb 22 '21 at 13:06
107

Swift Solution

Follow these steps:

  1. Set the height constraint for the table from the storyboard.

  2. Drag the height constraint from the storyboard and create @IBOutlet for it in the view controller file.

    @IBOutlet var tableHeight: NSLayoutConstraint!
    
  3. Then you can change the height for the table dynamicaly using this code:

    override func viewWillLayoutSubviews() {
        super.updateViewConstraints()
        self.tableHeight?.constant = self.table.contentSize.height
    }
    

If the last row is cut off, try to call viewWillLayoutSubviews() in willDisplay cell function:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    self.viewWillLayoutSubviews()
}
pyrmont
  • 210
  • 5
  • 11
Musa almatri
  • 4,688
  • 1
  • 27
  • 28
21

I've tried this in iOS 7 and it worked for me

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView sizeToFit];
}
Alexey
  • 6,552
  • 8
  • 54
  • 92
  • 3
    If your UITableView is dynamic (meaning information loads to and from it on the fly), you'll need to put this elsewhere, not in viewDidLoad. For me, I put it in cellForRowAtIndexPath. Also had to change "tableView" to the name of my IBOutlet property to avoid the old "property tableView not found" error. – jeremytripp May 23 '14 at 15:33
  • thx, had problem with resizing tableview inside popover, it worked – Zaporozhchenko Oleksandr Oct 25 '17 at 19:30
19

Add an observer for the contentSize property on the table view, and adjust the frame size accordingly

[your_tableview addObserver:self forKeyPath:@"contentSize" options:0 context:NULL];

then in the callback:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
         CGRect frame = your_tableview.frame;
         frame.size = your_tableview.contentSize;
         your_tableview.frame = frame;
    }

Hope this will help you.

Anooj VM
  • 2,485
  • 19
  • 19
  • 2
    You should add one more thing. You should remove the observer for the table view. Because it will crash if cell deallocated. – Mahesh Agrawal Feb 08 '16 at 14:38
15

I had a table view inside scroll view and had to calculate tableView's height and resize it accordingly. Those are steps I've taken:

0) add a UIView to your scrollView (probably will work without this step but i did it to avoid any possible conflicts) - this will be a containr view for your table view. If you take this step , then set the views borders right to tableview's ones.

1) create a subclass of UITableView:

class IntrinsicTableView: UITableView {

    override var contentSize:CGSize {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        self.layoutIfNeeded()
        return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
    }

}

2) set class of a table view in Storyboard to IntrinsicTableView: screenshot: http://joxi.ru/a2XEENpsyBWq0A

3) Set the heightConstraint to your table view

4) drag the IBoutlet of your table to your ViewController

5) drag the IBoutlet of your table's height constraint to your ViewController

6) add this method into your ViewController:

override func viewWillLayoutSubviews() {
        super.updateViewConstraints()
        self.yourTableViewsHeightConstraint?.constant = self.yourTableView.intrinsicContentSize.height
    }

Hope this helps

Gulz
  • 1,613
  • 15
  • 15
8

In case you don't want to track table view's content size changes yourself, you might find this subclass useful.

protocol ContentFittingTableViewDelegate: UITableViewDelegate {
    func tableViewDidUpdateContentSize(_ tableView: UITableView)
}

class ContentFittingTableView: UITableView {

    override var contentSize: CGSize {
        didSet {
            if !constraints.isEmpty {
                invalidateIntrinsicContentSize()
            } else {
                sizeToFit()
            }

            if contentSize != oldValue {
                if let delegate = delegate as? ContentFittingTableViewDelegate {
                    delegate.tableViewDidUpdateContentSize(self)
                }
            }
        }
    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        return contentSize
    }
}
xinatanil
  • 969
  • 1
  • 12
  • 22
  • 1
    For swift 3, intrinsicContentSize has become a var, like `override public var intrinsicContentSize: CGSize { //... return someCGSize }` – xaphod Mar 25 '17 at 19:14
  • not working for me. still hiding bottom of the table – Gulz Feb 02 '18 at 08:43
5

In case your contentSize is not correct this is because it is based on the estimatedRowHeight (automatic), use this before

tableView.estimatedRowHeight = 0;

source : https://forums.developer.apple.com/thread/81895

Yohan Dahmani
  • 902
  • 12
  • 21
4

Swift 3, iOS 10.3

Solution 1: Just put self.tableview.sizeToFit() in cellForRowAt indexPath function. Make sure to set tableview height higher then you need. This is a good solution if you don't have views below tableview. However, if you have, bottom tableview constraint will not be updated (I didn't try to fix it because I came up with solution 2)

Example:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as? TestCell {
        cell.configureCell(data: testArray[indexPath.row])
        self.postsTableView.sizeToFit()
        return cell
    }

    return UITableViewCell()
}

Solution 2: Set tableview height constraint in storyboard and drag it to the ViewController. If you know the average height of your cell and you know how many elements your array contains, you can do something like this:

tableViewHeightConstraint.constant = CGFloat(testArray.count) * 90.0     // Let's say 90 is the average cell height

*EDIT:

After all the solutions I tried and every of them was fixing something, but not completely, this is the answer that explains and fixes this problem completely.

Đorđe Nilović
  • 2,772
  • 1
  • 19
  • 20
  • This is very simple solution, it worked for me, Thank you @Dorde Nilovic – R. Mohan May 28 '18 at 16:19
  • @DoedeNilovic I am trying to to use your second solution as I am using tableviewcell inside tableviewcell. I am not able to get insidetableviewcell height as the 90.0 is static I need it dynamic. Can you share some code for it. Please – Muju Aug 14 '20 at 05:36
4

I did in a bit different way, Actually my TableView was inside scrollview so i had to give height constraint as 0.

Then at runtime I made following changes,

       func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
            self.viewWillLayoutSubviews()
       }
    
       override func viewWillLayoutSubviews() {
            super.updateViewConstraints()
             DispatchQueue.main.async {
               self.tableViewHeightConstraint?.constant = self.myTableView.contentSize.height
               self.view.layoutIfNeeded()
          }
       }
Krunal Nagvadia
  • 820
  • 1
  • 6
  • 23
Niki
  • 1,315
  • 1
  • 8
  • 25
3

Swift 5 Solution

Follow these four steps:

  1. Set the height constraint for the tableview from the storyboard.

  2. Drag the height constraint from the storyboard and create @IBOutlet for it in the view controller file.

    @IBOutlet var tableViewHeightConstraint: NSLayoutConstraint!
    
  3. Add an observer for the contentSize property on the override func viewDidLoad()

override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
 
    }

  1. Then you can change the height for the table dynamicaly using this code:

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
         if(keyPath == "contentSize"){
             if let newvalue = change?[.newKey]
             {
                 DispatchQueue.main.async {
                 let newsize  = newvalue as! CGSize
                 self.tableViewHeightConstraint.constant = newsize.height
                 }
    
             }
         }
     }
    
Sreeraj VR
  • 1,218
  • 11
  • 28
2

There is a much better way to do it if you use AutoLayout: change the constraint that determines the height. Just calculate the height of your table contents, then find the constraint and change it. Here's an example (assuming that the constraint that determines your table's height is actually a height constraint with relation "Equal"):

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    for constraint in tableView.constraints {
        if constraint.firstItem as? UITableView == tableView {
            if constraint.firstAttribute == .height {
                constraint.constant = tableView.contentSize.height
            }
        }
    }
}
ChrisB
  • 1,118
  • 10
  • 8
  • You can improve on this. Add an additional constraint on your tableview, say, height <= 10000. Use control-drag from Interface Builder to your .h file to make this constraint a property of your view controller. Then you can avoid iterating through constraints and searching. Just directly set `myTableHeightConstraint.constant = tableView.contentSize.height` – RobP Jun 23 '18 at 20:18
1

Mimo's answer and Anooj VM 's answer both are awesome but there is a small problem if you have a large list, it's possible that the height of the frame will cutoff some of your cells.

So. I have modified the answer a little bit:

dispatch_async(dispatch_get_main_queue()) {
    //This code will run in the main thread:
    CGFloat newHeight=self.tableView.contentSize.height;
    CGFloat screenHeightPermissible=(self.view.bounds.size.height-self.tableView.frame.origin.y);
    if (newHeight>screenHeightPermissible)
    {
        //so that table view remains scrollable when 'newHeight'  exceeds the screen bounds
        newHeight=screenHeightPermissible;
    }

    CGRect frame = self.tableView.frame;
    frame.size.height = newHeight;
    self.tableView.frame = frame;
}
Community
  • 1
  • 1
SandeepAggarwal
  • 1,158
  • 3
  • 13
  • 32
1

This works for me using Auto Layout, with a table view with only one section.

func getTableViewContentHeight(tableView: UITableView) -> CGFloat {
        tableView.bounds = CGRect(x: 0, y: 0, width: 300, height: 40)
        let rows = tableView.numberOfRows(inSection: 0)
        var height = CGFloat(0)
        for n in 0...rows - 1 {
            height = height + tableView.rectForRow(at: IndexPath(row: n, section: 0)).height
        }
        return height
    }

I call this function when setting up Auto Layout (The sample here uses SnapKit, but you get the idea):

    let height = getTableViewContentHeight(tableView: myTableView)
    myTableView.snp.makeConstraints {
        ...
        ...
        $0.height.equalTo(height)
    }

I want the UITableView only to be as tall as the combined height of the cells; I loop through the cells and accumulate the total height of the cells. Since the size of the table view is CGRect.zero at this point, I need to set the bounds to be able to respect the Auto Layout rules defined by the cell. I set the size to an arbitrary value that should be large enough. The actual size will be calculated later by the Auto Layout system.

André
  • 23
  • 2
  • 5
1

based on fl034's answer

SWift 5

var tableViewHeight: NSLayoutConstraint?

    tableViewHeight = NSLayoutConstraint(item: servicesTableView, 
    attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute,
    multiplier: 0.0, constant: 10)
    tableViewHeight?.isActive = true


  func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    tableViewHeight?.constant = tableView.contentSize.height
    tableView.layoutIfNeeded()
}
Aalaa
  • 41
  • 6
0

As an extension of Anooj VM's answer, I suggest the following to refresh content size only when it changes.

This approach also disable scrolling properly and support larger lists and rotation. There is no need to dispatch_async because contentSize changes are dispatched on main thread.

- (void)viewDidLoad {
        [super viewDidLoad];
        [self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:NULL]; 
}


- (void)resizeTableAccordingToContentSize:(CGSize)newContentSize {
        CGRect superviewTableFrame  = self.tableView.superview.bounds;
        CGRect tableFrame = self.tableView.frame;
        BOOL shouldScroll = newContentSize.height > superviewTableFrame.size.height;
        tableFrame.size = shouldScroll ? superviewTableFrame.size : newContentSize;
        [UIView animateWithDuration:0.3
                                    delay:0
                                    options:UIViewAnimationOptionCurveLinear
                                    animations:^{
                            self.tableView.frame = tableFrame;
        } completion: nil];
        self.tableView.scrollEnabled = shouldScroll;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([change[NSKeyValueChangeKindKey] unsignedIntValue] == NSKeyValueChangeSetting &&
        [keyPath isEqualToString:@"contentSize"] &&
        !CGSizeEqualToSize([change[NSKeyValueChangeOldKey] CGSizeValue], [change[NSKeyValueChangeNewKey] CGSizeValue])) {
        [self resizeTableAccordingToContentSize:[change[NSKeyValueChangeNewKey] CGSizeValue]];
    } 
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
    [self resizeTableAccordingToContentSize:self.tableView.contentSize]; }

- (void)dealloc {
    [self.tableView removeObserver:self forKeyPath:@"contentSize"];
}
Ramiro
  • 365
  • 5
  • 8
0

objc version of Musa almatri

(void)viewWillLayoutSubviews
{
    [super updateViewConstraints];
    CGFloat desiredHeight = self.tableView.contentSize.height;
    // clamp desired height, if needed, and, in that case, leave scroll Enabled
    self.tableHeight.constant = desiredHeight;
    self.tableView.scrollEnabled = NO;
}
Anton Tropashko
  • 4,300
  • 3
  • 31
  • 53
0

You can try Out this Custom AGTableView

To Set a TableView Height Constraint Using storyboard or programmatically. (This class automatically fetch a height constraint and set content view height to yourtableview height).

class AGTableView: UITableView {

    fileprivate var heightConstraint: NSLayoutConstraint!

    override init(frame: CGRect, style: UITableViewStyle) {
        super.init(frame: frame, style: style)
        self.associateConstraints()
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.associateConstraints()
    }

    override open func layoutSubviews() {
        super.layoutSubviews()

        if self.heightConstraint != nil {
            self.heightConstraint.constant = self.contentSize.height
        }
        else{
            self.sizeToFit()
            print("Set a heightConstraint to Resizing UITableView to fit content")
        }
    }

    func associateConstraints() {
        // iterate through height constraints and identify

        for constraint: NSLayoutConstraint in constraints {
            if constraint.firstAttribute == .height {
                if constraint.relation == .equal {
                    heightConstraint = constraint
                }
            }
        }
    }
}

Note If any problem to set a Height then yourTableView.layoutSubviews().

AshvinGudaliya
  • 2,696
  • 14
  • 29
0

Based on answer of fl034. But for Xamarin.iOS users:

    [Register("ContentSizedTableView")]
    public class ContentSizedTableView : UITableView
    {
        public ContentSizedTableView(IntPtr handle) : base(handle)
        {
        }

        public override CGSize ContentSize { get => base.ContentSize; set { base.ContentSize = value; InvalidateIntrinsicContentSize(); } }
        public override CGSize IntrinsicContentSize
        {
            get
            {
                this.LayoutIfNeeded();
                return new CGSize(width: NoIntrinsicMetric, height: ContentSize.Height);
            }
        }
    }
Jelle
  • 356
  • 2
  • 11
0

My Swift 5 implementation is to set the hight constraint of the tableView to the size of its content (contentSize.height). This method assumes you are using auto layout. This code should be placed inside the cellForRowAt tableView method.

tableView.heightAnchor.constraint(equalToConstant: tableView.contentSize.height).isActive = true
0

I am using a UIView extension , approach is close to @ChrisB approach above

 extension UIView {
func updateHeight(_ height:NSLayoutConstraint)
{
    
    let newSize = CGSize(width: self.frame.size.width, height: CGFloat(MAXFLOAT))
    let fitSize : CGSize = self.sizeThatFits(newSize)
    
    height.constant = fitSize.height
    
   
}
}

implementation : :

@IBOutlet weak var myTableView: UITableView!
@IBOutlet weak var myTableVieweHeight: NSLayoutConstraint!
//(call it whenever tableView is updated inside/outside delegate methods)
myTableView.updateHeight(myTableVieweHeigh)

Bonus : Can be used on any other UIViews eg:your own dynamic label

Atka
  • 189
  • 1
  • 5
-1

If you want your table to be dynamic, you will need to use a solution based on the table contents as detailed above. If you simply want to display a smaller table, you can use a container view and embed a UITableViewController in it - the UITableView will be resized according to the container size.

This avoids a lot of calculations and calls to layout.

green_knight
  • 1,119
  • 13
  • 23
-3

Mu solution for this in swift 3: Call this method in viewDidAppear

func UITableView_Auto_Height(_ t : UITableView)
{
        var frame: CGRect = t.frame;
        frame.size.height = t.contentSize.height;
        t.frame = frame;        
}
brahimm
  • 636
  • 1
  • 9
  • 26