iOS8: Possible to use “tableview.rowHeight = UITableViewAutomaticDimension” for static cells?

前端 未结 8 978
慢半拍i
慢半拍i 2020-12-14 17:00

I have a UITableView that has five static cells. I need the cell height of one cell to adjust automatically to its contents, which is one UILabel.

Is there any way I

相关标签:
8条回答
  • 2020-12-14 17:29

    NOTE: This is based on @CamelBase 's answer, which is written for Objective-C, so mostly just a conversion to Swift for those that made the switch and still struggle converting stuff themselves.


    For those wanting to do this using Swift that want a table with static cells, here's a working solution.

    I didn't need to use this code (tried putting it in viewDidLoad() and tried without, made no difference):

    tableView.estimatedRowHeight = 42.0
    tableView.rowHeight = UITableViewAutomaticDimension
    

    First you want to add UILabel to each cell you want auto-grow content in.

    You'll need to add 4 constraints to the UILabel all relative to the parent (Content View of the cell).

    • Leading Margin 0
    • Trailing Margin 0
    • Top Margin 0
    • Bottom Margin 0

    This will make the label grow when the cell grows (which our code below does).

    Now just add this code:

    override func tableView(tableView: UITableView, 
    heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 
    {
        let sectionICareAbout = 2
        let rowICareAbout = 1
    
        // repeat for as many section/row combos you need dynamic sizing on
        if indexPath.section == sectionICareAbout 
          && indexPath == rowICareAbout {
            // replace with how you get the content for the cell
            // in section 2 row 1
            let text = "line1\nline2\nline3"
            return rowHeightForText(text)
        }
    
        // use auto-dimension if we didn't set something else
        return UITableViewAutomaticDimension
    }
    
    func rowHeightForText(text: String) -> CGFloat {
        let labelWidth = tableView.bounds.size.width
        let attributedText = NSAttributedString(string: text)
        let options = NSStringDrawingOptions.UsesLineFragmentOrigin
        let size = CGSize(width: labelWidth, height: CGFloat.max)
        let boundingRect = attributedText.boundingRectWithSize(size, 
            options: options, context: nil)
        return boundingRect.size.height
    }
    
    0 讨论(0)
  • 2020-12-14 17:32

    I assume that I would need to tell the cell to redraw using something like

    [self.tableView beginUpdates];
    [self.tableView reloadRowsAtIndexPaths:@[indexPathOfYourCell] withRowAnimation:UITableViewRowAnimationNone];
    [self.tableView endUpdates];
    

    When it comes to dynamically changing the height of the tableviewcells.

    -(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return Yourvalue; //return whatever u want ur cell to have size.
      }
    
    0 讨论(0)
  • 2020-12-14 17:35

    Expanding over Victors' answer, Static Cells based table views seem to autosize cells based on content and constraints once the following delegates are implemented:

    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return UITableViewAutomaticDimension;
    }
    
    -(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return 44;
    }
    

    EXCEPT for when there are labels. For some reason the label intrinsic content heights do not seem to contribute towards the height calculation of the cell when they have . My current work around for this is to nest the labels in an UIView.

    Do the following:

    1. Embed the label (or labels) in a view. (Select label(s) on IB and hit Menu > Editor > Embed In > View)
    2. Establish horizontal and vertical margin constraints between this view and the cell
    3. Establish horizontal and vertical margin constrains between your label(s) and this view.

    Certainly feels like a hack but works for me in all the cases I have tried.

    0 讨论(0)
  • 2020-12-14 17:39

    Ended up doing this. Not what I wanted, but it kind of works for my simple requirements and text lengths.

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        if (indexPath.row == 2)
        {
            CGFloat padding = 30.0f;
    
            CGFloat labelWidth = self.tableView.bounds.size.width - padding*2;
            NSAttributedString *text = [[NSAttributedString alloc] initWithString:_freeTextLabel.text];
            NSStringDrawingOptions options = NSStringDrawingUsesLineFragmentOrigin;
            CGRect boundingRect = [text boundingRectWithSize:CGSizeMake(labelWidth, CGFLOAT_MAX)
                                                 options:options
                                                 context:nil];
    
            return (CGFloat) (ceil(boundingRect.size.height) + padding*2);
        }
        return [super tableView:tableView heightForRowAtIndexPath:indexPath];
    }
    
    0 讨论(0)
  • 2020-12-14 17:42

    Here is a simple example with Swift 3. We have a UITableViewController in Storyboard set with type grouped and content of type static cells. Its tableView contains one UITableViewCell. The cell contains one UILabel that has a number of lines set to 0 and some very long text to display. The label also has four Auto layout constraints with its superView (top, bottom, leading and trailing).

    Now, you may choose one of the four following code snippets in order to allow your static cell to automatically resize itself according to its content.


    #1. Using tableView(_:estimatedHeightForRowAt:) and tableView(_:heightForRowAt:)

    Note: tableView(_:estimatedHeightForRowAt:) method requires iOS7

    import UIKit
    
    class TableViewController: UITableViewController {
    
        override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return UITableViewAutomaticDimension
        }
    
        override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
            return 100 // Return `UITableViewAutomaticDimension` if you have no estimate
        }
    
    }
    

    #2. Using estimatedRowHeight property and tableView(_:heightForRowAt:)

    Note: estimatedRowHeight property requires iOS7

    import UIKit
    
    class TableViewController: UITableViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            tableView.estimatedRowHeight = 100
        }
    
        override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return UITableViewAutomaticDimension
        }
    
    }
    

    #3. Using tableView(_:heightForRowAt:)

    import UIKit
    
    class TableViewController: UITableViewController {
    
        // Bind this IBOutlet to the cell in Storyboard
        @IBOutlet weak var cell: UITableViewCell!
    
        override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            let fittingSize = CGSize(width: tableView.bounds.size.width, height: 0)
            let systemLayoutSize = cell.systemLayoutSizeFitting(fittingSize, withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel)
            return systemLayoutSize.height
        }
    
    }
    

    #4. Using UITableViewCell subclass and tableView(_:heightForRowAt:)

    import UIKit
    
    class TableViewController: UITableViewController {
    
        // 1. Set custom class to `CustomCell` for cell in Storyboard
        // 2. Bind this IBOutlet to the cell in Storyboard
        @IBOutlet weak var cell: CustomCell!
    
        override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            cell.layoutIfNeeded()
            let contentViewSize = cell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
            return contentViewSize.height + 1 // + 1 for the separator
        }
    
    }
    
    class CustomCell: UITableViewCell {
    
        // 1. Set custom class to `CustomCell` for cell in Storyboard
        // 2. Bind this IBOutlet to the cell's label in Storyboard
        @IBOutlet weak var label: UILabel!
    
        override func layoutSubviews() {
            super.layoutSubviews()
            label.preferredMaxLayoutWidth = frame.width
        }
    
    }
    

    All four previous code snippets will result in the following display:

    0 讨论(0)
  • 2020-12-14 17:44

    In IOS8, I've found that explicitly setting the row height/width or label height/width in code causes more problems than it fixes. I have created dynamic and static tables with varying cell height and multiline labels using autolayout, but you really have to follow Apple standard to the letter or weird things happen (like separators disappearing or rows randomly collapsing in height).

    • [edited] Set estimatedHeightForRowAtIndexPath and heightForRowAtIndexPath to automatic dimensions in your UITableViewDelegate

      override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
          return UITableViewAutomaticDimension
      }
      override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
          return UITableViewAutomaticDimension
      }
      
    • Make sure every single element in the cell has a top/bottom/left/right = constraint (not >=, <=, or alignX or alignY - it has to be =). You can make this a low priority constraint and provide a better constraint of higher priority, but you have to give it an exact starting value to size the cell.

    0 讨论(0)
提交回复
热议问题