ios - सेल संरचना




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

एक परिवर्तनीय ऊंचाई 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
    

यह भी देखें

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


मैंने एक श्रेणी में @ स्माइलीबोर्ग के आईओएस 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 में यह जाज नहीं करना पड़ेगा, लेकिन अभी यह है!


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

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

(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.


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

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

नीचे दिए गए पहले 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 ।


An important enough gotcha I just ran into to post as an answer.

@smileyborg's answer is mostly correct. However, if you have any code in the layoutSubviews method of your custom cell class, for instance setting the preferredMaxLayoutWidth , then it won't be run with this code:

[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];

It confounded me for awhile. Then I realized it's because those are only triggering layoutSubviews on the contentView , not the cell itself.

My working code looks like this:

TCAnswerDetailAppSummaryCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailAppSummaryCell"];
[cell configureWithThirdPartyObject:self.app];
[cell layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;

Note that if you are creating a new cell, I'm pretty sure you don't need to call setNeedsLayout as it should already be set. In cases where you save a reference to a cell, you should probably call it. Either way it shouldn't hurt anything.

Another tip if you are using cell subclasses where you are setting things like preferredMaxLayoutWidth . As @smileyborg mentions, "your table view cell hasn't yet had its width fixed to the table view's width". This is true, and trouble if you are doing your work in your subclass and not in the view controller. However you can simply set the cell frame at this point using the table width:

For instance in the calculation for height:

self.summaryCell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailDefaultSummaryCell"];
CGRect oldFrame = self.summaryCell.frame;
self.summaryCell.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, self.tableView.frame.size.width, oldFrame.size.height);

(I happen to cache this particular cell for re-use, but that's irrelevant).


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.


Here is solution if it is containing text only:

private func estimateFrameForText(text: String) -> CGRect {
    let size = CGSize(width: 300.0, height: 1000)
    let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
    return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17.0)], context: nil)
}

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



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?


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.

बस।


Like I ran into an important enough gotcha that I'm posting this as an answer.

I struggled with @smileyborg's answer for a while. The gotcha that I ran into is if you've defined your prototype cell in IB with additional elements ( UILabels , UIButtons , etc.) in IB when you instantiate the cell with [ [YourTableViewCellClass alloc] init] it will not instantiate all the other elements within that cell unless you've written code to do that. (I had a similar experience with initWithStyle .)

To have the storyboard instantiate all the additional elements obtain your cell with [tableView dequeueReusableCellWithIdentifier:@"DoseNeeded"] (Not [tableView dequeueReusableCellWithIdentifier:forIndexPath:] as this'll cause interesting problems.) When you do this all the elements you defined in IB will be instantiated.


To set automatic dimension for row height & estimated row height, ensure following steps to make, auto dimension effective for cell/row height layout.

  • Assign and implement tableview dataSource and delegate
  • Assign UITableViewAutomaticDimension to rowHeight & estimatedRowHeight
  • Implement delegate/dataSource methods (ie heightForRowAt and return a value UITableViewAutomaticDimension to it)

-

Objective C:

// in ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

  @property IBOutlet UITableView * table;

@end

// in ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    self.table.dataSource = self;
    self.table.delegate = self;

    self.table.rowHeight = UITableViewAutomaticDimension;
    self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    return UITableViewAutomaticDimension;
}

स्विफ्ट:

@IBOutlet weak var table: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Don't forget to set dataSource and delegate for table
    table.dataSource = self
    table.delegate = self

    // Set automatic dimensions for row height
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension
}



// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

For label instance in UITableviewCell

  • Set number of lines = 0 (& line break mode = truncate tail)
  • Set all constraints (top, bottom, right left) with respect to its superview/ cell container.
  • Optional : Set minimum height for label, if you want minimum vertical area covered by label, even if there is no data.

Note : If you've more than one labels (UIElements) with dynamic length, which should be adjusted according to its content size: Adjust 'Content Hugging and Compression Resistance Priority` for labels which you want to expand/compress with higher priority.


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


tableView.estimatedRowHeight = 343.0
tableView.rowHeight = UITableViewAutomaticDimension





row-height