ios - गतिशील सेल लेआउट और चर पंक्ति पंक्तियों के लिए UITableView में ऑटो लेआउट का उपयोग करना




autolayout nsautolayout row-height (21)

चिकनी स्क्रॉलिंग प्रदर्शन को बनाए रखते हुए, प्रत्येक सेल की सामग्री और उप-दृश्य पंक्ति ऊंचाई (स्वयं / स्वचालित) निर्धारित करने के लिए आप तालिका दृश्य में UITableViewCell भीतर ऑटो लेआउट का उपयोग कैसे करते हैं?


Answers

I just did some dumb try and error with the 2 values of rowHeight and estimatedRowHeight and just thought it might provide some debugging insight:

If you set them both OR only set the estimatedRowHeight you will get the desired behavior:

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1.00001 // MUST be greater than 1

It's suggested that you do your best to get the correct estimate, but the end result isn't different. It will just affect your performance.

If you only set the rowHeight ie only do:

tableView.rowHeight = UITableViewAutomaticDimension

your end result would not be as desired:

If you set the estimatedRowHeight to 1 or smaller then you will crash regardless of the rowHeight .

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1 

I crashed with the following error message:

Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'table view row height
must not be negative - provided height for index path (<NSIndexPath:
0xc000000000000016> {length = 2, path = 0 - 0}) is -1.000000'
    ...some other lines...

libc++abi.dylib: terminating with uncaught exception of type
NSException

The solution proposed by @smileyborg is almost perfect. If you have a custom cell and you want one or more UILabel with dynamic heights then the systemLayoutSizeFittingSize method combined with AutoLayout enabled returns a CGSizeZero unless you move all your cell constraints from the cell to its contentView (as suggested by @TomSwift here How to resize superview to fit all subviews with autolayout? ).

To do so you need to insert the following code in your custom UITableViewCell implementation (thanks to @Adrian).

- (void)awakeFromNib{
    [super awakeFromNib];
    for (NSLayoutConstraint *cellConstraint in self.constraints) {
        [self removeConstraint:cellConstraint];
        id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
        id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
        NSLayoutConstraint *contentViewConstraint =
        [NSLayoutConstraint constraintWithItem:firstItem
                                 attribute:cellConstraint.firstAttribute
                                 relatedBy:cellConstraint.relation
                                    toItem:seccondItem
                                 attribute:cellConstraint.secondAttribute
                                multiplier:cellConstraint.multiplier
                                  constant:cellConstraint.constant];
        [self.contentView addConstraint:contentViewConstraint];
    }
}

Mixing @smileyborg answer with this should works.


As long as your layout in your cell is good.

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];

    return [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}

Update: You should use dynamic resizing introduced in iOS 8.


Another "solution": skip all this frustration and use a UIScrollView instead to get a result that looks and feels identical to UITableView.

That was the painful "solution" for me, after having put in literally 20+ very frustrating hours total trying to build something like what smileyborg suggested and failing over many months and three versions of App Store releases.

My take is that if you really need iOS 7 support (for us, it's essential) then the technology is just too brittle and you'll pull your hair out trying. And that UITableView is complete overkill generally unless you're using some of the advanced row editing features and/or really need to support 1000+ "rows" (in our app, it's realistically never more than 20 rows).

The added bonus is that the code gets insanely simple versus all the delegate crap and back and forth that comes with UITableView. It's just one single loop of code in viewOnLoad that looks elegant and is easy to manage.

Here's some tips on how to do it:

1) Using either Storyboard or a nib file, create a ViewController and associated root view.

2) Drag over a UIScrollView onto your root view.

3) Add constraints top, bottom, left and right constraints to the top-level view so the UIScrollView fills the entire root view.

4) Add a UIView inside the UIScrollView and call it "container". Add top, bottom, left and right constraints to the UIScrollView (its parent). KEY TRICK: Also add a "Equal widths" constraints to link the UIScrollView and UIView.

You will get an error "scroll view has ambiguous scrollable content height" and that your container UIView should have a height of 0 pixels. Neither error seems to matter when the app is running.

5) Create nib files and controllers for each of your "cells". Use UIView not UITableViewCell.

5) In your root ViewController, you essentially add all the "rows" to the container UIView and programmatically add constraints linking their left and right edges to the container view, their top edges to either the container view top (for the first item) or the previous cell. Then link the final cell to the container bottom.

For us, each "row" is in a nib file. So the code looks something like this:

class YourRootViewController {

    @IBOutlet var container: UIView! //container mentioned in step 4

    override func viewDidLoad() {

        super.viewDidLoad()

        var lastView: UIView?
        for data in yourDataSource {

            var cell = YourCellController(nibName: "YourCellNibName", bundle: nil)
            UITools.addViewToTop(container, child: cell.view, sibling: lastView)
            lastView = cell.view
            //Insert code here to populate your cell
        }

        if(lastView != nil) {
            container.addConstraint(NSLayoutConstraint(
                item: lastView!,
                attribute: NSLayoutAttribute.Bottom,
                relatedBy: NSLayoutRelation.Equal,
                toItem: container,
                attribute: NSLayoutAttribute.Bottom,
                multiplier: 1,
                constant: 0))
        }

        ///Add a refresh control, if you want - it seems to work fine in our app:
        var refreshControl = UIRefreshControl()
        container.addSubview(refreshControl!)
    }
}

And here's the code for UITools.addViewToTop:

class UITools {
    ///Add child to container, full width of the container and directly under sibling (or container if sibling nil):
    class func addViewToTop(container: UIView, child: UIView, sibling: UIView? = nil)
    {
        child.setTranslatesAutoresizingMaskIntoConstraints(false)
        container.addSubview(child)

        //Set left and right constraints so fills full horz width:

        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Leading,
            relatedBy: NSLayoutRelation.Equal,
            toItem: container,
            attribute: NSLayoutAttribute.Left,
            multiplier: 1,
            constant: 0))

        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Trailing,
            relatedBy: NSLayoutRelation.Equal,
            toItem: container,
            attribute: NSLayoutAttribute.Right,
            multiplier: 1,
            constant: 0))

        //Set vertical position from last item (or for first, from the superview):
        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Top,
            relatedBy: NSLayoutRelation.Equal,
            toItem: sibling == nil ? container : sibling,
            attribute: sibling == nil ? NSLayoutAttribute.Top : NSLayoutAttribute.Bottom,
            multiplier: 1,
            constant: 0))
    }
}

The only "gotcha" I've found with this approach so far is that UITableView has a nice feature of "floating" section headers at the top of the view as you scroll. The above solution won't do that unless you add more programming but for our particular case this feature wasn't 100% essential and nobody noticed when it went away.

If you want dividers between your cells, just add a 1 pixel high UIView at the bottom of your custom "cell" that looks like a divider.

Be sure to turn on "bounces" and "bounce vertically" for the refresh control to work and so it seems more like a tableview.

TableView shows some empty rows and dividers under your content, if it doesn't fill the full screen where as this solution doesn't. But personally, I prefer if those empty rows weren't there anyway - with variable cell height it always looked "buggy" to me anyway to have the empty rows in there.

Here's hoping some other programmer reads my post BEFORE wasting 20+ hours trying to figure it out with Table View in their own app. :)


आईओएस 8 के लिए यह वास्तव में सरल है:

override func viewDidLoad() {  
    super.viewDidLoad()

    self.tableView.estimatedRowHeight = 80
    self.tableView.rowHeight = UITableViewAutomaticDimension
}

या

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

लेकिन आईओएस 7 के लिए, कुंजी ऑटोलायआउट के बाद ऊंचाई की गणना की जाती है,

func calculateHeightForConfiguredSizingCell(cell: GSTableViewCell) -> CGFloat {
    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize).height + 1.0
    return height
}

जरूरी

  • यदि एकाधिक रेखाएं लेबल हैं, तो numberOfLines को 0 सेट न करें।

  • label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds) न भूलें label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

पूरा उदाहरण कोड here


yet another iOs7+iOs8 solution in Swift

var cell2height:CGFloat=44

override func viewDidLoad() {
    super.viewDidLoad()
    theTable.rowHeight = UITableViewAutomaticDimension
    theTable.estimatedRowHeight = 44.0;
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell =  tableView.dequeueReusableCellWithIdentifier("myTableViewCell", forIndexPath: indexPath) as! myTableViewCell
    cell2height=cell.contentView.height
    return cell
}

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if #available(iOS 8.0, *) {
        return UITableViewAutomaticDimension
    } else {
        return cell2height
    }
}


एक परिवर्तनीय ऊंचाई UITableViewCell का स्विफ्ट उदाहरण

स्विफ्ट 3 के लिए अपडेट किया गया

विलियम हू का स्विफ्ट जवाब अच्छा है, लेकिन यह पहली बार कुछ करने के लिए सीखते समय मुझे कुछ सरल लेकिन विस्तृत कदम रखने में मदद करता है। वेरिएबल सेल हाइट्स के साथ UITableView बनाने के दौरान सीखने के दौरान नीचे दिया गया उदाहरण मेरा परीक्षण प्रोजेक्ट है। मैं इसे स्विफ्ट के लिए इस मूल UITableView उदाहरण पर आधारित करता हूं।

तैयार परियोजना इस तरह दिखनी चाहिए:

एक नई परियोजना बनाएँ

यह सिर्फ एक एकल दृश्य आवेदन हो सकता है।

कोड जोड़ें

अपनी परियोजना में एक नई स्विफ्ट फ़ाइल जोड़ें। इसे MyCustomCell नाम दें। यह कक्षा स्टोरीबोर्ड में आपके सेल में जोड़े गए विचारों के लिए आउटलेट रखेगी। इस मूल उदाहरण में हमारे पास प्रत्येक सेल में केवल एक लेबल होगा।

import UIKit
class MyCustomCell: UITableViewCell {
    @IBOutlet weak var myCellLabel: UILabel!
}

हम बाद में इस आउटलेट को जोड़ देंगे।

Open ViewController.swift खोलें और सुनिश्चित करें कि आपके पास निम्न सामग्री है:

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // These strings will be the data for the table view cells
    let animals: [String] = [
        "Ten horses:  horse horse horse horse horse horse horse horse horse horse ",
        "Three cows:  cow, cow, cow",
        "One camel:  camel",
        "Ninety-nine sheep:  sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep baaaa sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep",
        "Thirty goats:  goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat "]

    // Don't forget to enter this in IB also
    let cellReuseIdentifier = "cell"

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // delegate and data source
        tableView.delegate = self
        tableView.dataSource = self

        // Along with auto layout, these are the keys for enabling variable cell height
        tableView.estimatedRowHeight = 44.0
        tableView.rowHeight = UITableViewAutomaticDimension
    }

    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }

    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
        cell.myCellLabel.text = self.animals[indexPath.row]
        return cell
    }

    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }
}

महत्वपूर्ण लेख:

  • यह कोड की निम्नलिखित दो पंक्तियां (ऑटो लेआउट के साथ) है जो परिवर्तनीय सेल ऊंचाई को संभव बनाता है:

    tableView.estimatedRowHeight = 44.0
    tableView.rowHeight = UITableViewAutomaticDimension
    

स्टोरीबोर्ड सेटअप करें

अपने व्यू कंट्रोलर में एक टेबल व्यू जोड़ें और इसे चार तरफ पिन करने के लिए ऑटो लेआउट का उपयोग करें। फिर टेबल व्यू पर टेबल व्यू सेल खींचें। और प्रोटोटाइप सेल पर, एक लेबल खींचें। तालिका दृश्य कक्ष के सामग्री दृश्य के चार किनारों पर लेबल पिन करने के लिए ऑटो लेआउट का उपयोग करें।

महत्वपूर्ण लेख:

  • ऑटो लेआउट ऊपर वर्णित कोड की महत्वपूर्ण दो पंक्तियों के साथ मिलकर काम करता है। यदि आप ऑटो लेआउट का उपयोग नहीं करते हैं तो यह काम नहीं करेगा।

अन्य आईबी सेटिंग्स

कस्टम वर्ग का नाम और पहचानकर्ता

टेबल व्यू सेल का चयन करें और कस्टम क्लास को MyCustomCell (हमारे द्वारा जोड़े गए स्विफ्ट फ़ाइल में कक्षा का नाम) सेट करें। पहचानकर्ता को cell होने के लिए भी सेट करें (उसी स्ट्रिंग जिसे हमने उपरोक्त कोड में cellReuseIdentifier लिए उपयोग किया था।

लेबल के लिए शून्य रेखाएं

अपने लेबल में 0 से लाइनों की संख्या सेट करें। इसका अर्थ बहु-रेखा है और लेबल को अपनी सामग्री के आधार पर स्वयं का आकार बदलने की अनुमति देता है।

दुकानों को हुक अप करें

  • स्टोरीबोर्ड में तालिका दृश्य से तालिका दृश्य से ड्रैग को नियंत्रित करें ViewController कोड में चर देखें।
  • MyCustomCell क्लास में myCellLabel चर में अपने प्रोटोटाइप सेल में लेबल के लिए ऐसा ही करें।

ख़त्म होना

अब आप अपनी परियोजना को चलाने में सक्षम होना चाहिए और वेरिएबल हाइट्स वाले सेल प्राप्त कर सकते हैं।

टिप्पणियाँ

  • यह उदाहरण केवल आईओएस 8 और बाद के लिए काम करता है। यदि आपको अभी भी आईओएस 7 का समर्थन करने की आवश्यकता है तो यह आपके लिए काम नहीं करेगा।
  • आपकी भविष्य की परियोजनाओं में आपकी खुद की कस्टम कोशिकाओं में शायद एक से अधिक लेबल होंगे। सुनिश्चित करें कि आप सबकुछ ठीक पिन कर लें ताकि ऑटो लेआउट उपयोग करने के लिए सही ऊंचाई निर्धारित कर सके। आपको ऊर्ध्वाधर संपीड़न प्रतिरोध और गले लगाने का भी उपयोग करना पड़ सकता है। इस लेख को इसके बारे में और जानने के लिए देखें।
  • यदि आप अग्रणी और पिछला (बाएं और दाएं) किनारों को पिन नहीं कर रहे हैं, तो आपको लेबल की preferredMaxLayoutWidth विड्थ सेट करने की भी आवश्यकता हो सकती है ताकि यह पता चल सके कि लाइन को कब लपेटना है। उदाहरण के लिए, यदि आपने अग्रणी और पिछला किनारों को पिन करने के बजाय उपर्युक्त प्रोजेक्ट में लेबल को क्षैतिज रूप से बाधा डाली है, तो आपको tableView:cellForRowAtIndexPath यह लाइन जोड़नी होगी tableView:cellForRowAtIndexPath विधि:

     cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
    

यह भी देखें


If you do you layout programmatically, here is what to consider for iOS 10 using anchors in Swift.

There are three rules/ steps

NUMBER 1: set this two properties of tableview on viewDidLoad, the first one is telling to the tableview that should expect dynamic sizes on their cells, the second one is just to let the app calculate the size of the scrollbar indicator, so it helps for performance.

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 100

NUMBER 2: This is important you need to add the subviews to the contentView of the cell not to the view, and also use its layoutsmarginguide to anchor the subviews to the top and bottom, this is a working example of how to do it.

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    setUpViews()
}

private func setUpViews() {

    contentView.addSubview(movieImageView)
    contentView.addSubview(descriptionLabel)
    let marginGuide = contentView.layoutMarginsGuide

    NSLayoutConstraint.activate([
        movieImageView.heightAnchor.constraint(equalToConstant: 80),
        movieImageView.widthAnchor.constraint(equalToConstant: 80),
        movieImageView.leftAnchor.constraint(equalTo: marginGuide.leftAnchor),
        movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 20),

        descriptionLabel.leftAnchor.constraint(equalTo: movieImageView.rightAnchor, constant: 15),
        descriptionLabel.rightAnchor.constraint(equalTo: marginGuide.rightAnchor),
        descriptionLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -15),
        descriptionLabel.topAnchor.constraint(equalTo: movieImageView.topAnchor)

        ])
}

Create a method that will add the subviews and perform the layout, call it in the init method.

NUMBER 3: DON'T CALL THE METHOD:

  override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    }

If you do it you will override your implementation.

Follow this 3 rules for dynamic cells in tableviews.

here is a working implementation https://github.com/jamesrochabrun/MinimalViewController


मेरा समाधान यहाँ है। दृश्य को लोड करने से पहले आपको अनुमानित हाईइट टेबलव्यू बताना होगा। अन्यथा यह उम्मीद की तरह व्यवहार करने में सक्षम नहीं होगा।

- (void)viewWillAppear:(BOOL)animated {
    _messageField.delegate = self;
    _tableView.estimatedRowHeight = 65.0;
    _tableView.rowHeight = UITableViewAutomaticDimension;
}


In my case i have to create a custom cell with a image which is coming from server and can be of any width and height. And two UILabels with dynamic size(both width & height)

i have achieved the same here in my answer with autolayout and programmatically:

Basically above @smileyBorg answer helped but systemLayoutSizeFittingSize never worked for me, In my approach :

1. No use of automatic row height calculation property. 2.No use of estimated height 3.No need of unnecessary updateConstraints. 4.No use of Automatic Preferred Max Layout Width. 5. No use of systemLayoutSizeFittingSize (should have use but not working for me, i dont know what it is doing internally), but instead my method -(float)getViewHeight working and i know what it's doing internally.

Is it possible to have differing heights in a UITableView Cell when I use several different ways of displaying the cell?


With regard to the accepted answer by @smileyborg, I have found

[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]

to be unreliable in some cases where constraints are ambiguous. Better to force the layout engine to calculate the height in one direction, by using the helper category on UIView below:

-(CGFloat)systemLayoutHeightForWidth:(CGFloat)w{
    [self setNeedsLayout];
    [self layoutIfNeeded];
    CGSize size = [self systemLayoutSizeFittingSize:CGSizeMake(w, 1) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
    CGFloat h = size.height;
    return h;
}

Where w: is the width of the tableview


Let's say you have a cell with a subview, and you want the cell's height to be high enough to encompass the subview + padding.

1) Set the subview's bottom constraint equal to the cell.contentView minus the padding you want. Do not set constraints on the cell or cell.contentView itself.

2) Set either the tableView's rowHeight property or tableView:heightForRowAtIndexPath: to UITableViewAutomaticDimension .

3) Set either the tableView's estimatedRowHeight property or tableView:estimatedHeightForRowAtIndexPath: to a best guess of the height.

बस।


In my case, the padding was because of the sectionHeader and sectionFooter heights, where storyboard allowed me to change it to minimum 1. So in viewDidLoad method:

tableView.sectionHeaderHeight = 0
tableView.sectionFooterHeight = 0

टीएल; डीआर: पढ़ना पसंद नहीं है? गिटहब पर सीधे नमूना परियोजनाओं पर जाएं:

अवधारणात्मक विवरण

नीचे दिए गए पहले 2 चरण लागू हैं, भले ही आप किस आईओएस संस्करण के लिए विकास कर रहे हों।

1. सेट अप करें और बाधाएं जोड़ें

अपने UITableViewCell सबक्लास में, बाधाएं जोड़ें ताकि सेल के सबव्यूज़ में उनके किनारों को सेल की सामग्री के किनारों पर पिन किया गया हो (सबसे महत्वपूर्ण रूप से ऊपर और नीचे किनारों पर)। नोट: सेल पर सबव्यूज़ पिन न करें; केवल सेल की contentView के लिए contentView ! इन सबव्यूज़ के आंतरिक सामग्री आकार को टेबल दृश्य की सेल ड्राइव की ऊंचाई को ड्राइव करने के लिए सुनिश्चित करें कि प्रत्येक संपीड़न के लिए लंबवत आयाम में सामग्री संपीड़न प्रतिरोध और सामग्री hugging बाधाओं को आपके द्वारा जोड़े गए उच्च प्राथमिकता बाधाओं से ओवरराइड नहीं किया जा रहा है। ( हू? यहां क्लिक करें। )

याद रखें, विचार है कि सेल के सबव्यूज सेल के कंटेंट व्यू पर लंबवत रूप से जुड़े हुए हैं ताकि वे "दबाव डालें" और सामग्री दृश्य को फिट करने के लिए विस्तार कर सकें। कुछ सबव्यूज़ के साथ एक उदाहरण सेल का उपयोग करके, यहां एक दृश्य चित्रण है कि आपकी बाधाओं के कुछ (सभी नहीं!) को इस तरह दिखने की आवश्यकता होगी:

आप कल्पना कर सकते हैं कि उपर्युक्त उदाहरण सेल में बहु-पंक्ति निकाय लेबल में अधिक टेक्स्ट जोड़ा गया है, इसलिए पाठ को फिट करने के लिए इसे लंबवत रूप से बढ़ने की आवश्यकता होगी, जो सेल को ऊंचाई में बढ़ने के लिए मजबूर कर देगा। (बेशक, इसे सही ढंग से काम करने के लिए आपको बाधाओं को सही करने की आवश्यकता है!)

अपनी बाधाओं को सही ढंग से प्राप्त करना ऑटो लेआउट के साथ काम कर रहे गतिशील सेल हाइट्स प्राप्त करने का सबसे कठिन और सबसे महत्वपूर्ण हिस्सा है। यदि आप यहां कोई गलती करते हैं, तो यह सब कुछ काम करने से रोक सकता है - तो अपना समय लें! मैं कोड में अपनी बाधाओं को स्थापित करने की अनुशंसा करता हूं क्योंकि आप जानते हैं कि वास्तव में कौन सी बाधाओं को जोड़ा जा रहा है, और जब चीजें गलत होती हैं तो डीबग करना बहुत आसान होता है। कोड में बाधाओं को जोड़ना लेआउट एंकरों का उपयोग करके इंटरफ़ेस बिल्डर की तुलना में उतना आसान और महत्वपूर्ण रूप से अधिक शक्तिशाली हो सकता है, या गिटहब पर उपलब्ध शानदार ओपन सोर्स एपीआई में से एक।

  • यदि आप कोड में बाधाओं को जोड़ रहे हैं, तो आपको इसे अपने UITableViewCell updateConstraints के updateConstraints विधि के भीतर से एक बार करना चाहिए। ध्यान दें कि updateConstraints को एक से अधिक बार बुलाया जा सकता है, इसलिए एक ही बाधाओं को एक से अधिक बाधाओं को जोड़ने से बचने के लिए, सुनिश्चित करें कि एक updateConstraints प्रॉपर्टी के लिए चेक में अपनी बाधा-जोड़ कोड को लपेटना सुनिश्चित करें जैसे कि didSetupConstraints (जिसे आप YES के बाद सेट करते हैं एक बार अपनी बाधा-जोड़ कोड चलाएं)। दूसरी तरफ, यदि आपके पास कोड है जो मौजूदा बाधाओं को अपडेट करता है (जैसे कि कुछ बाधाओं पर constant संपत्ति को समायोजित करना), इसे updateConstraints में updateConstraints लेकिन updateConstraints के लिए चेक के didSetupConstraints ताकि यह हर बार विधि कहलाए जा सके।

2. अद्वितीय तालिका दृश्य सेल पुन: उपयोग पहचानकर्ता निर्धारित करें

सेल में बाधाओं के हर अद्वितीय सेट के लिए, एक अद्वितीय सेल पुन: उपयोग पहचानकर्ता का उपयोग करें। दूसरे शब्दों में, यदि आपकी कोशिकाओं में एक से अधिक अद्वितीय लेआउट हैं, तो प्रत्येक अद्वितीय लेआउट को अपना स्वयं का पुन: उपयोग पहचानकर्ता प्राप्त करना चाहिए। (एक अच्छा संकेत है कि आपको एक नए पुन: उपयोग पहचानकर्ता का उपयोग करने की आवश्यकता है, जब आपके सेल संस्करण में अलग-अलग सबव्यूज़ होते हैं, या सबव्यूव एक अलग फैशन में व्यवस्थित होते हैं।)

उदाहरण के लिए, यदि आप प्रत्येक सेल में एक ईमेल संदेश प्रदर्शित कर रहे थे, तो आपके पास 4 अद्वितीय लेआउट हो सकते हैं: केवल एक विषय वाले संदेश, किसी विषय के साथ संदेश और एक शरीर, किसी विषय के साथ संदेश और एक फोटो अनुलग्नक, और किसी विषय के साथ संदेश, शरीर, और फोटो लगाव। प्रत्येक लेआउट में इसे प्राप्त करने के लिए पूरी तरह से अलग बाधाएं होती हैं, इसलिए सेल शुरू होने के बाद और इन सेल प्रकारों में से किसी एक के लिए बाधाओं को जोड़ा जाता है, तो सेल को उस सेल प्रकार के लिए विशिष्ट अनूठा पुन: उपयोग पहचानकर्ता प्राप्त करना चाहिए। इसका मतलब यह है कि जब आप पुन: उपयोग के लिए सेल को हटाते हैं, तो बाधाओं को पहले ही जोड़ा जा चुका है और उस सेल प्रकार के लिए जाने के लिए तैयार हैं।

ध्यान दें कि आंतरिक सामग्री आकार में मतभेदों के कारण, समान बाधाओं (प्रकार) वाले कोशिकाओं में अभी भी अलग-अलग ऊंचाई हो सकती है! सामग्री के विभिन्न आकारों के कारण अलग-अलग गणना दृश्य फ्रेम (समान बाधाओं से हल) के साथ मौलिक रूप से अलग-अलग लेआउट (विभिन्न बाधाओं) को भ्रमित न करें।

  • एक ही पुन: उपयोग पूल (यानी एक ही पुन: उपयोग पहचानकर्ता का उपयोग करें) में बाधाओं के पूरी तरह से अलग सेट के साथ कोशिकाओं को न जोड़ें और फिर पुरानी बाधाओं को हटाने का प्रयास करें और प्रत्येक डेक्यू के बाद खरोंच से नई बाधाएं स्थापित करें। आंतरिक ऑटो लेआउट इंजन को बाधाओं में बड़े पैमाने पर परिवर्तनों को संभालने के लिए डिज़ाइन नहीं किया गया है, और आप बड़े प्रदर्शन के मुद्दों को देखेंगे।

आईओएस 8 के लिए - स्व-आकार देने वाले सेल

3. पंक्ति ऊंचाई अनुमान सक्षम करें

स्वयं आकार देने वाली तालिका दृश्य कक्षों को सक्षम करने के लिए, आपको तालिका दृश्य की पंक्ति हाइट प्रॉपर्टी को UITableViewAutomaticDimension पर सेट करना होगा। आपको अनुमानित RowHeight संपत्ति को भी मान असाइन करना होगा। जैसे ही इन दोनों गुणों को सेट किया गया है, सिस्टम पंक्ति की वास्तविक ऊंचाई की गणना करने के लिए ऑटो लेआउट का उपयोग करता है

ऐप्पल: स्व-आकार देने वाली टेबल व्यू सेल के साथ काम करना

आईओएस 8 के साथ, ऐप्पल ने अधिकांश काम को आंतरिक रूप से कार्यान्वित किया है जिसे पहले आईओएस 8 से पहले आपके द्वारा लागू किया जाना था। स्व-आकार देने वाले सेल तंत्र को काम करने की अनुमति देने के लिए, आपको पहले तालिका दृश्य पर rowHeight प्रॉपर्टी सेट करनी होगी निरंतर UITableViewAutomaticDimension । फिर, आपको तालिका दृश्य की estimatedRowHeight RowHeight प्रॉपर्टी को nonzero मान में सेट करके पंक्ति ऊंचाई अनुमान सक्षम करने की आवश्यकता है, उदाहरण के लिए:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is

यह क्या करता है उन तालिकाओं की पंक्ति ऊंचाई के लिए एक अस्थायी अनुमान / प्लेसहोल्डर के साथ तालिका दृश्य प्रदान करता है जो अभी तक स्क्रीन पर नहीं हैं। फिर, जब ये कक्ष ऑनस्क्रीन स्क्रॉल करने वाले हैं, तो वास्तविक पंक्ति ऊंचाई की गणना की जाएगी। प्रत्येक पंक्ति के लिए वास्तविक ऊंचाई निर्धारित करने के लिए, तालिका दृश्य स्वचालित रूप से प्रत्येक सेल से पूछता है कि इसकी contentView किस ऊंचाई पर है दृश्य दृश्य सामग्री की ज्ञात निश्चित चौड़ाई के आधार पर देखने की आवश्यकता है (जो तालिका दृश्य की चौड़ाई पर आधारित है, किसी भी अतिरिक्त चीज को किसी सेक्शन की तरह घटाएं इंडेक्स या एक्सेसरी व्यू) और ऑटो लेआउट बाधाओं को आपने सेल के कंटेंट व्यू और सबव्यू में जोड़ा है। एक बार यह वास्तविक सेल ऊंचाई निर्धारित हो जाने के बाद, पंक्ति के लिए पुरानी अनुमानित ऊंचाई नई वास्तविक ऊंचाई के साथ अपडेट की जाती है (और तालिका दृश्य की सामग्री में कोई समायोजन आकार / सामग्री ऑफ़सेट आपके लिए आवश्यकतानुसार किया जाता है)।

आम तौर पर, आपके द्वारा प्रदान किए जाने वाले अनुमान को बहुत सटीक नहीं होना चाहिए - यह केवल तालिका दृश्य में स्क्रॉल सूचक को सही ढंग से आकार देने के लिए उपयोग किया जाता है, और तालिका दृश्य गलत अनुमानों के लिए स्क्रॉल सूचक को समायोजित करने का अच्छा काम करता है स्क्रॉल सेल ऑनस्क्रीन। आपको estimatedRowHeight viewDidLoad प्रॉपर्टी को टेबल व्यू ( viewDidLoad या इसी तरह) पर एक स्थिर मान पर सेट करना चाहिए जो "औसत" पंक्ति ऊंचाई है। केवल तभी जब आपकी पंक्ति ऊंचाइयों में अत्यधिक परिवर्तनशीलता हो (उदाहरण के लिए परिमाण के क्रम से अलग हो) और आप स्क्रॉल सूचक को "कूदते" देखते हैं जैसे आप स्क्रॉल करते हैं, आपको tableView:estimatedHeightForRowAtIndexPath: लागू करने से परेशान होना चाहिए tableView:estimatedHeightForRowAtIndexPath: न्यूनतम गणना करने के लिए आवश्यक न्यूनतम गणना करने के लिए हर एक पंक्ति।

आईओएस 7 समर्थन के लिए (स्वयं को आकार देने वाले ऑटो सेल को कार्यान्वित करना)

3. लेआउट पास करें और सेल ऊंचाई प्राप्त करें

सबसे पहले, तालिका दृश्य कक्ष का एक ऑफस्क्रीन उदाहरण तत्काल करें, प्रत्येक पुन: उपयोग पहचानकर्ता के लिए एक उदाहरण , जिसका उपयोग ऊंचाई गणना के लिए कड़ाई से किया जाता है। (ऑफस्क्रीन का मतलब है कि कक्ष संदर्भ दृश्य नियंत्रक पर किसी संपत्ति / ivar में संग्रहीत किया जाता है और कभी भी तालिका से वापस नहीं tableView:cellForRowAtIndexPath: तालिका दृश्य के लिए वास्तव में ऑनस्क्रीन प्रस्तुत करने के लिए।) अगला, सेल को सटीक सामग्री (जैसे टेक्स्ट, छवियों, इत्यादि) कि अगर यह तालिका दृश्य में प्रदर्शित किया गया था तो यह होगा।

फिर, सेल को इसके systemLayoutSizeFittingSize: को तत्काल लेआउट करने के लिए मजबूर करें, और उसके बाद systemLayoutSizeFittingSize: उपयोग करें systemLayoutSizeFittingSize: UITableViewCell की सामग्री पर विधि यह contentView लिए कि कक्ष की आवश्यक ऊंचाई क्या है। सेल की सभी सामग्री को फिट करने के लिए आवश्यक सबसे छोटा आकार प्राप्त करने के लिए UILayoutFittingCompressedSize का उपयोग करें। ऊंचाई को tableView:heightForRowAtIndexPath: से वापस किया जा सकता है tableView:heightForRowAtIndexPath: प्रतिनिधि विधि।

4. अनुमानित पंक्ति ऊंचाई का प्रयोग करें

यदि आपके टेबल व्यू में दो दर्जन से अधिक पंक्तियां हैं, तो आप पाएंगे कि ऑटो लेआउट बाधा हल करने से टेबल थ्रेड को पहली बार लोड करने पर मुख्य थ्रेड को तुरंत दबाया जा सकता है, तालिका दृश्य के रूप में tableView:heightForRowAtIndexPath: प्रत्येक पंक्ति पर कॉल किया जाता है पहले लोड पर (स्क्रॉल सूचक के आकार की गणना करने के लिए)।

आईओएस 7 के रूप में, आप तालिका दृश्य पर estimatedRowHeight प्रॉपर्टी का उपयोग कर सकते हैं (और बिल्कुल चाहिए)। यह क्या करता है उन तालिकाओं की पंक्ति ऊंचाई के लिए एक अस्थायी अनुमान / प्लेसहोल्डर के साथ तालिका दृश्य प्रदान करता है जो अभी तक स्क्रीन पर नहीं हैं। फिर, जब ये कक्ष ऑनस्क्रीन स्क्रॉल करने वाले हैं, तो वास्तविक पंक्ति ऊंचाई की गणना की जाएगी ( tableView:heightForRowAtIndexPath: को कॉल करके tableView:heightForRowAtIndexPath: , और अनुमानित ऊंचाई वास्तविक के साथ अपडेट की गई है।

आम तौर पर, आपके द्वारा प्रदान किए जाने वाले अनुमान को बहुत सटीक नहीं होना चाहिए - यह केवल तालिका दृश्य में स्क्रॉल सूचक को सही ढंग से आकार देने के लिए उपयोग किया जाता है, और तालिका दृश्य गलत अनुमानों के लिए स्क्रॉल सूचक को समायोजित करने का अच्छा काम करता है स्क्रॉल सेल ऑनस्क्रीन। आपको estimatedRowHeight viewDidLoad प्रॉपर्टी को टेबल व्यू ( viewDidLoad या इसी तरह) पर एक स्थिर मान पर सेट करना चाहिए जो "औसत" पंक्ति ऊंचाई है। केवल तभी जब आपकी पंक्ति ऊंचाइयों में अत्यधिक परिवर्तनशीलता हो (उदाहरण के लिए परिमाण के क्रम से अलग हो) और आप स्क्रॉल सूचक को "कूदते" देखते हैं जैसे आप स्क्रॉल करते हैं, आपको tableView:estimatedHeightForRowAtIndexPath: लागू करने से परेशान होना चाहिए tableView:estimatedHeightForRowAtIndexPath: न्यूनतम गणना करने के लिए आवश्यक न्यूनतम गणना करने के लिए हर एक पंक्ति।

5. (यदि आवश्यक हो) पंक्ति ऊंचाई कैशिंग जोड़ें

यदि आपने उपर्युक्त सभी किया है और अभी भी यह निष्कर्ष tableView:heightForRowAtIndexPath: रहा है कि tableView:heightForRowAtIndexPath: में बाधा को हल करते समय यह प्रदर्शन अस्वीकार्य रूप से धीमा है tableView:heightForRowAtIndexPath: दुर्भाग्यवश आपको सेल हाइट्स के लिए कुछ कैशिंग लागू करने की आवश्यकता होगी। (यह ऐप्पल के इंजीनियरों द्वारा सुझाया गया दृष्टिकोण है।) सामान्य विचार ऑटो लेआउट इंजन को पहली बार बाधाओं को हल करने देना है, फिर उस सेल के लिए गणना की गई ऊंचाई को कैश करें और उस सेल की ऊंचाई के लिए सभी भावी अनुरोधों के लिए कैश किए गए मान का उपयोग करें। निश्चित रूप से यह सुनिश्चित करना है कि आप सेल के लिए कैश की गई ऊंचाई को साफ़ करते हैं, जब कुछ भी होता है जो सेल की ऊंचाई को बदलने का कारण बन सकता है - मुख्य रूप से, यह तब होगा जब उस सेल की सामग्री में परिवर्तन हो या जब अन्य महत्वपूर्ण घटनाएं होती हैं (जैसे उपयोगकर्ता समायोजन गतिशील प्रकार पाठ आकार स्लाइडर)।

आईओएस 7 जेनेरिक नमूना कोड (बहुत सारे रसदार टिप्पणियों के साथ)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path, depending on the particular layout required (you may have
    // just one, or may have many).
    NSString *reuseIdentifier = ...;

    // Dequeue a cell for the reuse identifier.
    // Note that this method will init and return a new cell if there isn't
    // one available in the reuse pool, so either way after this line of 
    // code you will have a cell with the correct constraints ready to go.
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // If you are using multi-line UILabels, don't forget that the 
    // preferredMaxLayoutWidth needs to be set correctly. Do it at this 
    // point if you are NOT doing it within the UITableViewCell subclass 
    // -[layoutSubviews] method. For example: 
    // cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path.
    NSString *reuseIdentifier = ...;

    // Use a dictionary of offscreen cells to get a cell for the reuse 
    // identifier, creating a cell and storing it in the dictionary if one 
    // hasn't already been added for the reuse identifier. WARNING: Don't 
    // call the table view's dequeueReusableCellWithIdentifier: method here 
    // because this will result in a memory leak as the cell is created but 
    // never returned from the tableView:cellForRowAtIndexPath: method!
    UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
    if (!cell) {
        cell = [[YourTableViewCellClass alloc] init];
        [self.offscreenCells setObject:cell forKey:reuseIdentifier];
    }

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // Set the width of the cell to match the width of the table view. This
    // is important so that we'll get the correct cell height for different
    // table view widths if the cell's height depends on its width (due to 
    // multi-line UILabels word wrapping, etc). We don't need to do this 
    // above in -[tableView:cellForRowAtIndexPath] because it happens 
    // automatically when the cell is used in the table view. Also note, 
    // the final width of the cell may not be the width of the table view in
    // some cases, for example when a section index is displayed along 
    // the right side of the table view. You must account for the reduced 
    // cell width.
    cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));

    // Do the layout pass on the cell, which will calculate the frames for 
    // all the views based on the constraints. (Note that you must set the 
    // preferredMaxLayoutWidth on multi-line UILabels inside the 
    // -[layoutSubviews] method of the UITableViewCell subclass, or do it 
    // manually at this point before the below 2 lines!)
    [cell setNeedsLayout];
    [cell layoutIfNeeded];

    // Get the actual height required for the cell's contentView
    CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    // Add an extra point to the height to account for the cell separator, 
    // which is added between the bottom of the cell's contentView and the 
    // bottom of the table view cell.
    height += 1.0;

    return height;
}

// NOTE: Set the table view's estimatedRowHeight property instead of 
// implementing the below method, UNLESS you have extreme variability in 
// your row heights and you notice the scroll indicator "jumping" 
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Do the minimal calculations required to be able to return an 
    // estimated row height that's within an order of magnitude of the 
    // actual height. For example:
    if ([self isTallCellAtIndexPath:indexPath]) {
        return 350.0;
    } else {
        return 40.0;
    }
}

नमूना परियोजनाएं

ये परियोजनाएं UILabels में गतिशील सामग्री वाले तालिका दृश्य कक्षों के कारण चर पंक्तियों के साथ तालिका दृश्यों के पूरी तरह से काम कर रहे उदाहरण हैं।

एक्समरिन (सी # / एनईटी)

यदि आप @KentBoogaart का उपयोग कर रहे हैं, तो इस नमूना प्रोजेक्ट को @KentBoogaart द्वारा एक साथ @KentBoogaart ।


मैंने एक श्रेणी में @ स्माइलीबोर्ग के आईओएस 7 समाधान को लपेट लिया

मैंने @smileyborg द्वारा इस चालाक समाधान को UICollectionViewCell+AutoLayoutDynamicHeightCalculation श्रेणी में लपेटने का निर्णय लिया।

यह श्रेणी @ वाइल्डमोन्की के जवाब में उल्लिखित मुद्दों को भी सुधारती है (एक निब और systemLayoutSizeFittingSize: से सेल लोड करना systemLayoutSizeFittingSize: CGRectZero लौटा CGRectZero )

यह किसी भी कैशिंग को ध्यान में रखता है लेकिन अभी मेरी ज़रूरतों को पूरा करता है। इसे कॉपी, पेस्ट और हैक करने के लिए स्वतंत्र महसूस करें।

UICollectionViewCell + AutoLayoutDynamicHeightCalculation.h

#import <UIKit/UIKit.h>

typedef void (^UICollectionViewCellAutoLayoutRenderBlock)(void);

/**
 *  A category on UICollectionViewCell to aid calculating dynamic heights based on AutoLayout contraints.
 *
 *  Many thanks to @smileyborg and @wildmonkey
 *
 *  @see .com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights
 */
@interface UICollectionViewCell (AutoLayoutDynamicHeightCalculation)

/**
 *  Grab an instance of the receiving type to use in order to calculate AutoLayout contraint driven dynamic height. The method pulls the cell from a nib file and moves any Interface Builder defined contrainsts to the content view.
 *
 *  @param name Name of the nib file.
 *
 *  @return collection view cell for using to calculate content based height
 */
+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name;

/**
 *  Returns the height of the receiver after rendering with your model data and applying an AutoLayout pass
 *
 *  @param block Render the model data to your UI elements in this block
 *
 *  @return Calculated constraint derived height
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width;

/**
 *  Directly calls `heightAfterAutoLayoutPassAndRenderingWithBlock:collectionViewWidth` assuming a collection view width spanning the [UIScreen mainScreen] bounds
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block;

@end

UICollectionViewCell + AutoLayoutDynamicHeightCalculation.m

#import "UICollectionViewCell+AutoLayout.h"

@implementation UICollectionViewCell (AutoLayout)

#pragma mark Dummy Cell Generator

+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name
{
    UICollectionViewCell *heightCalculationCell = [[[NSBundle mainBundle] loadNibNamed:name owner:self options:nil] lastObject];
    [heightCalculationCell moveInterfaceBuilderLayoutConstraintsToContentView];
    return heightCalculationCell;
}

#pragma mark Moving Constraints

- (void)moveInterfaceBuilderLayoutConstraintsToContentView
{
    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        [self removeConstraint:constraint];
        id firstItem = constraint.firstItem == self ? self.contentView : constraint.firstItem;
        id secondItem = constraint.secondItem == self ? self.contentView : constraint.secondItem;
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:firstItem
                                                                     attribute:constraint.firstAttribute
                                                                     relatedBy:constraint.relation
                                                                        toItem:secondItem
                                                                     attribute:constraint.secondAttribute
                                                                    multiplier:constraint.multiplier
                                                                      constant:constraint.constant]];
    }];
}

#pragma mark Height

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block
{
    return [self heightAfterAutoLayoutPassAndRenderingWithBlock:block
                                            collectionViewWidth:CGRectGetWidth([[UIScreen mainScreen] bounds])];
}

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width
{
    NSParameterAssert(block);

    block();

    [self setNeedsUpdateConstraints];
    [self updateConstraintsIfNeeded];

    self.bounds = CGRectMake(0.0f, 0.0f, width, CGRectGetHeight(self.bounds));

    [self setNeedsLayout];
    [self layoutIfNeeded];

    CGSize calculatedSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

    return calculatedSize.height;

}

@end

उपयोग उदाहरण:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MYSweetCell *cell = [MYSweetCell heightCalculationCellFromNibWithName:NSStringFromClass([MYSweetCell class])];
    CGFloat height = [cell heightAfterAutoLayoutPassAndRenderingWithBlock:^{
        [(id<MYSweetCellRenderProtocol>)cell renderWithModel:someModel];
    }];
    return CGSizeMake(CGRectGetWidth(self.collectionView.bounds), height);
}

शुक्र है कि हमें आईओएस 8 में यह जाज नहीं करना पड़ेगा, लेकिन अभी यह है!


tableView.estimatedRowHeight = 343.0
tableView.rowHeight = UITableViewAutomaticDimension


I had to use dynamic views (setup views and constraints by code) and when I wanted to set preferredMaxLayoutWidth label's width was 0. So I've got wrong cell height.

Then I added

[cell layoutSubviews];

before executing

[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];

After that label's width was as expected and dynamic height was calculating right.


(for Xcode 8.x / Xcode 9.x read at the bottom)

Beware of the following issue in in Xcode 7.x, which might be a source of confusion:

Interface Builder does not handle auto-sizing cell set-up properly. Even if your constraints are absolutely valid, IB will still complain and give you confusing suggestions and errors. The reason is that IB is unwilling to change the row's height as your constraints dictate (so that the cell fits around your content). Instead, it keeps the row's height fixed and starts suggesting you change your constraints, which you should ignore .

For example, imagine you've set up everything fine, no warnings, no errors, all works.

Now if you change the font size (in this example I'm changing the description label font size from 17.0 to 18.0).

Because the font size increased, the label now wants to occupy 3 rows (before that it was occupying 2 rows).

If Interface Builder worked as expected, it would resize the cell's height to accommodate the new label height. However what actually happens is that IB displays the red auto-layout error icon and suggest that you modify hugging/compression priorities.

You should ignore these warnings. What you can* do instead is to manually change the row's height in (select Cell > Size Inspector > Row Height).

I was changing this height one click at a time (using the up/down stepper) until the red arrow errors disappear! (you will actually get yellow warnings, at which point just go ahead and do 'update frames', it should all work).

* Note that you don't actually have to resolve these red errors or yellow warnings in Interface Builder - at runtime, everything will work correctly (even if IB shows errors/warnings). Just make sure that at runtime in the console log you're not getting any AutoLayout errors.

In fact trying to always update row height in IB is super annoying and sometimes close to impossible (because of fractional values).

To prevent the annoying IB warnings/errors, you can select the views involved and in Size Inspector for the property Ambiguity choose Verify Position Only

Xcode 8.x / Xcode 9.x seems to (sometimes) be doing things differently than Xcode 7.x, but still incorrectly. For example even when compression resistance priority / hugging priority are set to required (1000), Interface Builder might stretch or clip a label to fit the cell (instead of resizing cell height to fit around the label). And in such a case it might not even show any AutoLayout warnings or errors. Or sometimes it does exactly what Xcode 7.x did, described above.


यहां बताया गया है कि मैं अपना समाधान प्राप्त करने के लिए अपने uiviews को फिर से संरेखित करूंगा:

  1. एक UIImageView ड्रॉप करें और इसे बाईं ओर रखें।
  2. एक UIView ड्रॉप खींचें और इसे UIImageView के दाईं ओर रखें।
  3. UIView के अंदर दो UILabels ड्रॉप खींचें जिनकी अग्रणी और पिछली बाधाएं शून्य हैं।
  4. UIIiewView की बजाय पर्यवेक्षण में 2 लेबल युक्त UIView की अग्रणी बाधा सेट करें।
  5. अगर UIImageView छुपा हुआ है, तो पर्यवेक्षण के लिए अग्रणी बाधा स्थिर 10 पीएक्स सेट करें। ईएलएसई, अग्रणी बाधा को लगातार 10 पीएक्स + UIImageView.width + 10 पीएक्स पर सेट करें।

मैंने अपने अंगूठे का नियम बनाया। जब भी आपको किसी भी यूवीव को छिपाना / दिखाया जाना चाहिए, जिसकी बाधाओं को प्रभावित किया जा सकता है, तो यूवीव के अंदर सभी प्रभावित / आश्रित सबव्यू जोड़ें और प्रोग्रामिंग के बाद अपनी अग्रणी / पीछे / शीर्ष / नीचे बाधा अद्यतन करें।





ios uitableview autolayout nsautolayout row-height