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


1501

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


यहाँ नमूना कोड स्विफ्ट 2.3 github.com/dpakthakur/DynamicCellHeight
दीपक ठाकुर

बहुभाषी यूआईलैब होने के संबंध में अधिकांश उत्तरों को समझने के लिए, आपको यह समझने की आवश्यकता है कि कैसे contentSizeऔर कैसे preferredMaxLayoutWidthकाम करना है। देखें यहाँ । यह कहा जा रहा है कि यदि आप अपनी बाधाओं को सही ढंग से निर्धारित करते हैं तो आपको आवश्यकता नहीं होनी चाहिए preferredMaxLayoutWidthऔर वास्तव में अप्रत्याशित परिणाम पैदा कर सकते हैं।
हनी

जवाबों:


2386

TL; DR: पढ़ना पसंद नहीं है? GitHub पर नमूना परियोजनाओं के लिए सीधे कूदें:

वैचारिक विवरण

नीचे दिए गए पहले 2 चरण इस बात पर ध्यान दिए बिना लागू होते हैं कि आप किन iOS संस्करणों के लिए विकास कर रहे हैं।

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

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

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

टेबल व्यू सेल पर बाधाओं का उदाहरण चित्रण।

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

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

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

2. यूनीक टेबल व्यू सेल का पुन: उपयोग पहचानकर्ता निर्धारित करें

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

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

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

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

IOS 8 के लिए - सेल्फ-सिजिंग सेल

3. सक्षम ऊँचाई अनुमान

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

Apple: सेल्फ-साइज़िंग टेबल व्यू सेल के साथ काम करना

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

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

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

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

IOS 7 सपोर्ट के लिए (ऑटो सेल को खुद को लागू करते हुए)

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

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

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

4. अनुमानित पंक्ति हाइट का उपयोग करें

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

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

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

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

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

iOS 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 multiline 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 में गतिशील सामग्री वाले तालिका दृश्य कोशिकाओं के कारण चर पंक्ति ऊंचाइयों के साथ तालिका विचारों के पूरी तरह से काम कर रहे उदाहरण हैं।

ज़मीरिन (C # /। NET)

यदि आप Xamarin का उपयोग कर रहे हैं, तो @KentBoogaart द्वारा लगाई गई इस नमूना परियोजना को देखें


1
जबकि यह अच्छी तरह से काम कर रहा है, मुझे लगता है कि यह आवश्यक आकारों से थोड़ा कम है (संभवतः विभिन्न दौर के मुद्दों के कारण) और मुझे अपने सभी पाठों को लेबल के अंदर फिट करने के लिए एक जोड़े को अंतिम ऊंचाई तक जोड़ना होगा
DBD

5
@ Alex311 इस उदाहरण को प्रदान करने के लिए बहुत दिलचस्प है, धन्यवाद। मैंने अपने अंत में थोड़ा परीक्षण किया और यहां कुछ टिप्पणियां लिखीं: github.com/Alex311/TableCellWithAutoLayout/commit/…
smileyborg

3
मैं प्रत्येक सेल पुनः उपयोग पहचानकर्ता प्रकार के लिए सेल को कैशिंग करने की सलाह दूंगा। हर बार जब आप ऊँचाई के लिए छलावा करते हैं तो आप उस कतार से एक सेल निकाल रहे होते हैं जिसे वापस नहीं जोड़ा जा रहा है। कैशिंग आपकी टेबल कोशिकाओं के लिए आरंभीकरण की मात्रा को काफी कम कर सकता है। किसी कारण से, जब मैं कैशिंग नहीं कर रहा था, तो उपयोग की जा रही मेमोरी की मात्रा बढ़ रही थी जैसे मैं स्क्रॉल करूंगा।
एलेक्स ३११

1
स्टोरीबोर्ड्स, वास्तव में। मुझे नहीं लगता कि यह प्रोटोटाइप कोशिकाओं के लिए काम करता है (कम से कम पूरे वीसी को फिर से इंस्टेंट किए बिना)। यह संभव हो सकता है कि पूरी तरह से रिसाव से बचने के लिए heightForRowAtIndexPath, सेल में रखते हुए और अगली बार इसे वापस करने के लिए cellForRowAtIndexPathकहा जाता है।
nschum

2
है iOS8कार्यान्वयन अभी भी के लिए सिफारिश की iOS9है और iOS10, या के बाद से इस सवाल का जवाब प्रकाशित किया गया था वहाँ नए तरीकों कर रहे हैं?
कोएन

175

ऊपर iOS 8 के लिए यह वास्तव में सरल है:

override func viewDidLoad() {  
    super.viewDidLoad()

    self.tableView.estimatedRowHeight = 80
    self.tableView.rowHeight = UITableView.automaticDimension
}

या

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

लेकिन iOS 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)

पूर्ण उदाहरण कोड यहाँ है


7
मुझे लगता है कि हमें OS8 मामले में
heightForRowAtIndexPath

एक अन्य उत्तर पर @eddwinpaz नोटों के रूप में, यह महत्वपूर्ण है कि पंक्ति ऊंचाई में लॉक करने के लिए उपयोग की जाने वाली बाधाओं में मार्जिन शामिल नहीं है।
रिचर्ड

1
+1 के लिए "यदि कई लाइनें लेबल हैं, तो नंबरऑफलाइन को 0 पर सेट न करें" यह मेरी कोशिकाओं को आकार में गतिशील नहीं होने का कारण बना रहा था।
इयान-फोगेलमैन

मुझे एक कंट्रोल फ्रीक कहें लेकिन यहां तक ​​कि iOS 11 के दिनों में भी मुझे यूआईटेबल व्यूऑटोमेटिकडिमेशन का उपयोग करना पसंद नहीं है। अतीत में इसके साथ कुछ बुरे अनुभव थे। जैसे, मैं आमतौर पर यहां सूचीबद्ध iOS7 समाधान का उपयोग करता हूं। विलियम के नोट लेबल को नहीं भूलना चाहिए। यहां दिए गए MaxLayoutWidth ने मुझे बचाया।
ब्रायन सचेटा

जब हम स्क्रॉल करते हैं, तो लेबल कई लाइनों में दिखाई देने लगता है और पंक्ति की ऊंचाई भी कम नहीं होती है
करणवीर सिंह

93

एक चर ऊंचाई UITableViewCell का स्विफ्ट उदाहरण

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

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

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

यहां छवि विवरण दर्ज करें

एक नया प्रोजेक्ट बनाएं

यह सिर्फ सिंगल व्यू एप्लिकेशन हो सकता है।

कोड जोड़ें

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

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

हम इस आउटलेट को बाद में कनेक्ट करेंगे।

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अपने लेबल में लाइनों की संख्या निर्धारित करें । इसका मतलब बहु-पंक्ति है और लेबल को अपनी सामग्री के आधार पर आकार बदलने की अनुमति देता है।

यहां छवि विवरण दर्ज करें

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

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

ख़त्म होना

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

टिप्पणियाँ

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

     cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width

यह सभी देखें


यह वास्तव preferredMaxLayoutWidthमें सेल के खिलाफ सेट करने के लिए बेहतर नहीं है contentSize? इस तरह अगर आपके पास एक एक्सेसरी व्यू है या इसे एडिट के लिए स्वाइप किया गया है तो इसे अभी भी ध्यान में रखा जाएगा?
Honey

@ अच्छा, आप बहुत सही हो सकते हैं। मैंने iOS पर लगभग एक साल तक काम नहीं किया है और मैं अभी आपको अच्छी तरह से जवाब देने के लिए बहुत चिंतित हूं।
सुरगाछ

65

मैंने एक श्रेणी में @ स्माइबॉर्ग के iOS7 समाधान को लपेटा

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

यह श्रेणी @ Wildmonkey's answer में उल्लिखित मुद्दों को भी सुधारती है (एक निब से सेल को लोड करना और systemLayoutSizeFittingSize:वापस लौटना 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 stackoverflow.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);
}

शुक्र है कि हमें iOS8 में इस जैज को नहीं करना पड़ेगा, लेकिन यह अभी के लिए है!


2
आपको बस एक का उपयोग करने में सक्षम होना चाहिए: [YourCell new]और डमी के रूप में उपयोग करें। जब तक बाधा कोड बिल्डिंग कोड को आपके उदाहरण में निकाल दिया जाता है, और आप एक लेआउट पास को प्रोग्रामेटिक रूप से ट्रिगर करते हैं, तो आपको जाने के लिए अच्छा होना चाहिए।
एडम वाइट

1
धन्यवाद! यह काम। आपकी श्रेणी महान है। यह मुझे एहसास हुआ कि इस तकनीक के साथ भी काम करता है UICollectionViews
रिकार्डो सांचेज-साज़

2
स्टोरीबोर्ड में परिभाषित प्रोटोटाइप सेल का उपयोग करके आप यह कैसे करेंगे?
डेविड पोटर

57

यहाँ मेरा समाधान है:

आप बता करने की जरूरत है इससे पहले कि यह लोड होगा। अन्यथा यह अपेक्षित व्यवहार करने में सक्षम नहीं होगा।TableViewestimatedHeight

उद्देश्य सी

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

अद्यतन करने के लिए स्विफ्ट 4.2

override func viewWillAppear(_ animated: Bool) {
    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 65.0
}

2
ऑटोलैयट को सही तरीके से सेट करना, साथ में इस कोड को देखने में जोड़ा जा रहा है।
ब्रूस

1
लेकिन क्या होगा अगर estimatedRowHeightपंक्ति से पंक्ति अलग हो? क्या मुझे अनुमान से अधिक या कम करना चाहिए? मेरे द्वारा उपयोग की जाने वाली न्यूनतम या अधिकतम ऊंचाई का उपयोग करें tableView?
जन्नोस

1
@ János यह रोहाइट का बिंदु है। ऐसा करने के लिए अपेक्षा के अनुसार व्यवहार करें कि आपको मार्जिन के बिना अवरोधों का उपयोग करने की आवश्यकता है और ऑब्जेक्ट्स को TableViewCell में संरेखित करें। और मुझे लगता है कि आप एक UITextView का उपयोग कर रहे हैं इसलिए अभी भी आपको ऑटोस्कोप = झूठ को हटाने की आवश्यकता है अन्यथा यह ऊंचाई और सापेक्ष ऊंचाई को बनाए रखेगा, जैसा कि अपेक्षित था।
एडविन पाज़

1
यह अब तक का सबसे मजबूत समाधान है। चिंता न करें estimatedRowHeight, यह ज्यादातर स्क्रॉल बार के आकार को प्रभावित करता है, वास्तविक कोशिकाओं की ऊंचाई कभी नहीं। आपके द्वारा चुनी गई ऊंचाई में बोल्ड रहें: यह प्रविष्टि / विलोपन एनीमेशन को प्रभावित करेगा।
SwiftArchitect

यहाँ नमूना कोड स्विफ्ट में 2.3 github.com/dpakthakur/DynamicCellHeight
दीपक ठाकुर

47

@Smileyborg द्वारा प्रस्तावित समाधान लगभग सही है। यदि आपके पास एक कस्टम सेल है और आप UILabelडायनेमिक हाइट्स के साथ एक या एक से अधिक चाहते हैं, तो AutoLayout के साथ संयुक्त systemLayoutSizeFittingSize विधि रिटर्न देता है CGSizeZeroजब तक कि आप सेल से अपने सभी सेल बाधाओं को उसके contentView में नहीं ले जाते हैं (जैसा कि @TomSwift के लिए यहां दिया गया है) ऑटोलॉयट के साथ सभी साक्षात्कार फिट? )।

ऐसा करने के लिए आपको अपने कस्टम UITableViewCell कार्यान्वयन (@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];
    }
}

इसके साथ @smileyborg उत्तर को मिलाकर काम करना चाहिए।


systemLayoutSizeFittingSizeकंटेंट व्यू पर कॉल करने की जरूरत है, सेल नहीं
onmyway133

25

एक महत्वपूर्ण पर्याप्त गोच मैं सिर्फ एक उत्तर के रूप में पोस्ट करने के लिए दौड़ा।

@ स्माइबॉर्ग का जवाब ज्यादातर सही है। हालाँकि, यदि आपके पास layoutSubviewsअपने कस्टम सेल वर्ग की विधि में कोई कोड है , उदाहरण के लिए preferredMaxLayoutWidth, तो यह इस कोड के साथ नहीं चलाया जाएगा:

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

इसने मुझे थोड़ी देर के लिए उलझन में डाल दिया। तब मुझे एहसास हुआ कि यह केवल लेआउट को ट्रिगर कर रहा है contentView, सेल पर ही नहीं।

मेरा काम कोड इस तरह दिखता है:

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

ध्यान दें कि यदि आप एक नया सेल बना रहे हैं, तो मुझे पूरा यकीन है कि आपको कॉल करने की आवश्यकता नहीं है setNeedsLayoutक्योंकि इसे पहले ही सेट किया जाना चाहिए। ऐसे मामलों में जहां आप किसी सेल के संदर्भ को सहेजते हैं, आपको संभवतः इसे कॉल करना चाहिए। किसी भी तरह से यह कुछ भी चोट नहीं करना चाहिए।

एक और टिप यदि आप सेल उपवर्गों का उपयोग कर रहे हैं जहां आप चीजों को सेट कर रहे हैं preferredMaxLayoutWidth। जैसा कि @smileyborg का उल्लेख है, "आपके टेबल व्यू सेल में अभी तक इसकी चौड़ाई टेबल व्यू की चौड़ाई तय नहीं हुई है"। यह सच है, और परेशानी यह है कि अगर आप अपना काम अपने उपवर्ग में कर रहे हैं न कि व्यू कंट्रोलर में। हालाँकि आप टेबल की चौड़ाई का उपयोग करके बस इस बिंदु पर सेल फ्रेम सेट कर सकते हैं:

उदाहरण के लिए ऊंचाई के लिए गणना में:

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

(मैं पुन: उपयोग के लिए इस विशेष सेल को कैश करने के लिए होता हूं, लेकिन यह अप्रासंगिक है)।


21

मामले में लोग अभी भी इससे परेशान हैं। मैंने डायनेमिक सेल हाइट्स के लिए यूआईटेबल व्यूज लीवरेजिंग ऑटोलैयट के साथ ऑटोलैयूट का उपयोग करने के बारे में एक त्वरित ब्लॉग पोस्ट लिखा और साथ ही इसे और अधिक अमूर्त और लागू करने में आसान बनाने के लिए एक खुला स्रोत घटक। https://github.com/Raizlabs/RZCellSizeManager


19

जब तक आपके सेल में आपका लेआउट अच्छा है।

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

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

अद्यतन: आपको iOS 8 में पेश किए गए डायनामिक आकार का उपयोग करना चाहिए।


यह iOS7 पर मेरे लिए काम कर रहा है, यह अब कॉल करने के लिए ठीक है tableView:cellForRowAtIndexPath:में tableView:heightForRowAtIndexPath:अब?
चींटियाँ

ठीक है यह तो काम नहीं कर रहा, लेकिन जब मैं फोन systemLayoutSizeFittingSize:में tableView:cellForRowAtIndexPath:और उसके बाद परिणाम कैश और उसके बाद का उपयोग करें कि में tableView:heightForRowAtIndexPath:यह रूप में लंबे समय के रूप में अच्छी तरह से काम की कमी सेटअप निश्चित रूप से सही ढंग से कर रहे हैं!
चींटियों

यह केवल तभी काम करता है जब आप dequeueReusableCellWithIdentifier का उपयोग करें: dequeueReusableCellWithIdentifier के बजाय: forIndexPath:
Antoine

1
मुझे सच में नहीं लगता है कि कॉलिंग टेबल व्यू: सेलफोररौटइंडेक्सपैथ: सीधे एक अच्छा तरीका है।
इटाची

19

(नीचे Xcode 8.x / Xcode 9.x नीचे पढ़ें)

Xcode 7.x में निम्नलिखित समस्या से सावधान रहें, जो भ्रम की स्थिति हो सकती है:

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

उदाहरण के लिए, कल्पना करें कि आपने सब कुछ ठीक कर दिया है, कोई चेतावनी नहीं, कोई त्रुटि नहीं, सभी काम।

यहां छवि विवरण दर्ज करें

अब यदि आप फ़ॉन्ट आकार बदलते हैं (इस उदाहरण में मैं 17.0 से 18.0 तक वर्णन लेबल फ़ॉन्ट आकार बदल रहा हूं)।

यहां छवि विवरण दर्ज करें

क्योंकि फ़ॉन्ट आकार में वृद्धि हुई, लेबल अब 3 पंक्तियों पर कब्जा करना चाहता है (इससे पहले कि यह 2 पंक्तियों पर कब्जा कर रहा था)।

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

यहां छवि विवरण दर्ज करें

आपको इन चेतावनियों को नजरअंदाज करना चाहिए। आप इसके बजाय * क्या कर सकते हैं मैन्युअल रूप से पंक्ति की ऊंचाई (सेल चुनें> साइज़ इंस्पेक्टर> रो ऊँचाई) बदलें।

यहां छवि विवरण दर्ज करें

मैं इस ऊँचाई को एक समय में एक क्लिक (अप / डाउन स्टेपर का उपयोग करके) बदल रहा था जब तक कि लाल तीर की त्रुटियां गायब नहीं हो जाती! (आपको वास्तव में पीले रंग की चेतावनी मिलेगी, जिस बिंदु पर बस आगे बढ़ें और frames अपडेट फ़्रेम ’करें, यह सब काम करना चाहिए)।

* ध्यान दें कि आपको वास्तव में इंटरफ़ेस बिल्डर में इन लाल त्रुटियों या पीले चेतावनियों को हल करने की आवश्यकता नहीं है - रनटाइम पर, सब कुछ सही ढंग से काम करेगा (भले ही आईबी त्रुटियों / चेतावनियों को दिखाता हो)। बस सुनिश्चित करें कि कंसोल लॉग में रनटाइम पर आपको कोई AutoLayout त्रुटियाँ नहीं मिल रही हैं।

वास्तव में आईबी में हमेशा पंक्ति की ऊंचाई को अपडेट करने की कोशिश करना सुपर कष्टप्रद है और कभी-कभी असंभव (भिन्नात्मक मूल्यों के कारण) के करीब है।

कष्टप्रद आईबी चेतावनियों / त्रुटियों को रोकने के लिए, आप इसमें शामिल विचार चुन सकते हैं और Size Inspectorसंपत्ति Ambiguityचुन सकते हैंVerify Position Only

यहां छवि विवरण दर्ज करें


Xcode 8.x / Xcode 9.x लगता है (कभी-कभी) Xcode 7.x की तुलना में अलग तरीके से काम कर रहा है, लेकिन फिर भी गलत तरीके से। उदाहरण के लिए भी जब compression resistance priority/ hugging priority(आवश्यक 1000) पर सेट किया जाता है, तो इंटरफ़ेस बिल्डर सेल को फिट करने के लिए एक लेबल को खींच या क्लिप कर सकता है (सेल के चारों ओर फिट करने के लिए सेल ऊंचाई को आकार देने के बजाय)। और ऐसे मामले में यह किसी भी AutoLayout चेतावनी या त्रुटियों को भी नहीं दिखा सकता है। या कभी-कभी यह ठीक वही करता है जो Xcode 7.x ने किया था, ऊपर वर्णित है।


नमस्ते क्या सेल के लिए गतिशील ऊंचाई देना संभव है, कि सेल के साथ गतिशील सेल सामग्री के साथ टेबलव्यू हो?
जिग्नेश बी

18

पंक्ति ऊंचाई और अनुमानित पंक्ति ऊंचाई के लिए स्वचालित आयाम सेट करने के लिए, निम्न चरणों को सुनिश्चित करें, सेल / पंक्ति ऊंचाई लेआउट के लिए ऑटो आयाम प्रभावी।

  • टेबलव्यू डेटा को असाइन करें और लागू करेंस्रोत और प्रतिनिधि
  • UITableViewAutomaticDimensionरोहाइट और अनुमानित रोहाइट को असाइन करें
  • प्रतिनिधि / डेटा स्रोत विधियों को लागू करें (अर्थात इसका heightForRowAtमान लौटाएं UITableViewAutomaticDimension)

-

उद्देश्य सी:

// 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
    // Swift 4.2 onwards
    table.rowHeight = UITableView.automaticDimension
    table.estimatedRowHeight = UITableView.automaticDimension


    // Swift 4.1 and below
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension

}



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

    // Swift 4.1 and below
    return UITableViewAutomaticDimension
}

UITableviewCell में लेबल उदाहरण के लिए

  • पंक्तियों की संख्या निर्धारित करें = 0 (और पंक्ति विराम मोड = छोटी पूंछ)
  • अपने पर्यवेक्षक / सेल कंटेनर के संबंध में सभी बाधाओं (ऊपर, नीचे, दाएं बाएं) को सेट करें।
  • वैकल्पिक : लेबल के लिए न्यूनतम ऊंचाई निर्धारित करें, यदि आप लेबल द्वारा कवर किया गया न्यूनतम ऊर्ध्वाधर क्षेत्र चाहते हैं, भले ही कोई डेटा न हो।

यहां छवि विवरण दर्ज करें

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


1
धन्यवाद, एक स्पष्ट और सरल समाधान की तरह लगता है, लेकिन मुझे एक समस्या है कि मैं लेबल का उपयोग नहीं कर रहा हूं, लेकिन टेक्स्टव्यू इसलिए मुझे डेटा बढ़ाने के लिए पंक्ति की ऊंचाई बढ़ाने की आवश्यकता है। मेरी समस्या तो ऊँचाई के लिए जानकारी पारित करने के लिए है। मैं अपने बदलते पाठों की ऊंचाई को माप सकता हूं लेकिन अब पंक्ति की ऊंचाई को बदलने की आवश्यकता है। कृपया कुछ मदद की सराहना करेंगे
जेरेमी एंड्रयूज

@JeremyAndrews ज़रूर आपकी मदद करेंगे। अपने प्रश्न को स्रोत कोड के साथ उठाएं जो आपने आजमाया है और समस्या के बारे में विस्तार से।
क्रुणाल

यह मेरे लिए काम कर रहा है बिना tableView: heightForRowडेटासोर्स को लागू किए ।
हेमांग

@ हेमांग - iOS 11+ के बाद से यह बिना काम करता है tableView: heightForRow। (iOS 10- के tableView: heightForRowलिए आवश्यक है)
क्रुणाल

1
@ हेमांग - समाधान सटीक क्वेरी परिभाषा पर निर्भर करता है। यहां मैं आपकी क्वेरी के लिए सामान्य सामान्य समाधान हूं। अंदर शर्त रखें tableView: heightForRow..if (indexPath.row == 0) { return 100} else { return UITableView.automaticDimension }
क्रुणाल

16

जैसा @ बॉब-Spryn मैं एक महत्वपूर्ण पर्याप्त पकड़ लिया है कि मैं एक जवाब के रूप में इस पोस्टिंग कर रहा हूँ में फंस गयी।

मैं थोड़ी देर @ स्माइबॉर्ग के जवाब से जूझता रहा । जब मैं आपके साथ सेल में आईबी में अतिरिक्त तत्वों ( UILabels, UIButtonsआदि) के साथ अपने प्रोटोटाइप सेल को परिभाषित करता हूं, तो जब आप सेल को एनालाइज करते हैं, तो [YourTableViewCellClass alloc] init]यह उस सेल के भीतर अन्य सभी तत्वों को तत्काल नहीं करेगा। ऐसा करने के लिए लिखित कोड। (मुझे इसी तरह का अनुभव था initWithStyle।)

स्टोरीबोर्ड को तुरंत करने के लिए सभी अतिरिक्त तत्व आपके सेल को प्राप्त करते हैं [tableView dequeueReusableCellWithIdentifier:@"DoseNeeded"]( [tableView dequeueReusableCellWithIdentifier:forIndexPath:]जैसा कि यह दिलचस्प समस्याएं पैदा करेगा।) जब आप ऐसा करते हैं तो आईबी में परिभाषित सभी तत्व तत्काल हो जाएंगे।


15

डायनेमिक टेबल व्यू सेल ऊंचाई और ऑटो लेआउट

स्टोरीबोर्ड ऑटो लेआउट के साथ समस्या को हल करने का एक अच्छा तरीका:

- (CGFloat)heightForImageCellAtIndexPath:(NSIndexPath *)indexPath {
  static RWImageCell *sizingCell = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sizingCell = [self.tableView dequeueReusableCellWithIdentifier:RWImageCellIdentifier];
  });

  [sizingCell setNeedsLayout];
  [sizingCell layoutIfNeeded];

  CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
  return size.height;
}

1
इस प्रश्न के स्वीकृत उत्तर में यह पहले से ही व्यापक रूप से शामिल है।
स्माइबॉर्ग

2
हाँ, मुझे पता है ... लेकिन मैं PureLayout का उपयोग नहीं करना चाहता था और डिस्पैच_ऑनस 'ट्रिक' ने मुझे बहुत मदद की, इसे केवल स्टोरीबोर्ड का उपयोग करके काम करने के लिए।
मोहनमास


13

एक और "समाधान": इस सभी हताशा को छोड़ दें और एक परिणाम प्राप्त करने के बजाय एक UIScrollView का उपयोग करें जो UITCView के समान दिखता है और महसूस करता है।

मेरे लिए यह दर्दनाक "समाधान" था, जो वास्तव में 20+ बहुत निराशाजनक घंटों में डाल देने के बाद स्माइलीबोर्ग ने कई महीनों तक असफल रहने और ऐप स्टोर रिलीज़ के तीन संस्करणों की तरह कुछ बनाने की कोशिश की।

मेरा लेना यह है कि अगर आपको वास्तव में iOS 7 सपोर्ट (हमारे लिए, यह आवश्यक है) की जरूरत है, तो तकनीक बहुत भंगुर है और आप अपने बालों को खींचने की कोशिश करेंगे। और यह कि UITableView पूरी तरह से ओवरकिल है, जब तक कि आप उन्नत पंक्ति संपादन सुविधाओं में से कुछ का उपयोग नहीं कर रहे हैं और / या वास्तव में 1000+ "पंक्तियों" का समर्थन करने की आवश्यकता है (हमारे ऐप में, यह वास्तविक रूप से कभी 20 पंक्तियों से अधिक नहीं है)।

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

यह कैसे करना है पर कुछ सुझाव दिए गए हैं:

  1. स्टोरीबोर्ड या नायब फ़ाइल का उपयोग करके, एक ViewController और संबंधित रूट दृश्य बनाएं।

  2. अपने मूल दृश्य पर एक UIScrollView पर खींचें।

  3. ऊपर-नीचे के दृश्य में बाधाएँ ऊपर, नीचे, बाएँ और दाएँ अवरोध जोड़ें ताकि UIScrollView पूरे रूट दृश्य को भर दे।

  4. UIScrollView के अंदर एक UIView जोड़ें और इसे "कंटेनर" कहें। UIScrollView (इसके अभिभावक) में ऊपर, नीचे, बाएँ और दाएँ अवरोध जोड़ें। प्रमुख TRICK: इसके अलावा UIScrollView और UIView को जोड़ने के लिए "समान चौड़ाई" की बाधाएँ जोड़ें।

    नोट: आपको एक त्रुटि मिलेगी "स्क्रॉल दृश्य में अस्पष्ट स्क्रॉल करने योग्य सामग्री ऊँचाई है" और आपके कंटेनर UIView की ऊंचाई 0 पिक्सेल होनी चाहिए। जब ऐप चल रहा हो तो न तो कोई त्रुटि प्रतीत होती है।

  5. अपने प्रत्येक "सेल" के लिए निब फाइल्स और कंट्रोलर बनाएँ। UIVable का उपयोग करें UITableViewCell का नहीं।

  6. अपने मूल ViewController में, आप अनिवार्य रूप से कंटेनर के लिए सभी "पंक्तियों" को जोड़ते हैं UIView और प्रोग्रामेटिक रूप से कंटेनर के दृश्य में उनके बाएं और दाएं किनारों को जोड़ने वाली बाधाएं जोड़ते हैं, उनके शीर्ष किनारों को या तो कंटेनर व्यू टॉप (पहले आइटम या पिछले के लिए) सेल। फिर अंतिम सेल को कंटेनर के नीचे से लिंक करें।

हमारे लिए, प्रत्येक "पंक्ति" एक निब फ़ाइल में है। तो कोड कुछ इस तरह दिखता है:

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

और यहाँ 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))
    }
}

इस दृष्टिकोण के साथ मैंने अभी तक जो एकमात्र "गोचा" पाया है वह यह है कि UITableView के पास स्क्रॉल करते हुए दृश्य के शीर्ष पर "फ्लोटिंग" अनुभाग हेडर की एक अच्छी सुविधा है। उपरोक्त समाधान यह नहीं करेगा कि जब तक आप अधिक प्रोग्रामिंग नहीं जोड़ते हैं लेकिन हमारे विशेष मामले के लिए यह सुविधा 100% आवश्यक नहीं थी और जब यह चला गया तो किसी ने भी इस पर ध्यान नहीं दिया।

यदि आप अपनी कोशिकाओं के बीच डिवाइडर चाहते हैं, तो बस अपने कस्टम "सेल" के निचले भाग में एक 1 पिक्सेल उच्च UIView जोड़ें जो डिवाइडर की तरह दिखता है।

ताज़ा नियंत्रण के लिए "बाउंस" और "लंबवत उछाल" चालू करना सुनिश्चित करें और इसलिए यह एक टेबलव्यू की तरह लगता है।

TableView आपकी सामग्री के तहत कुछ खाली पंक्तियों और डिवाइडर को दिखाता है, अगर यह पूरी स्क्रीन को नहीं भरता है जहां यह समाधान नहीं है। लेकिन व्यक्तिगत रूप से, मैं पसंद करता हूं कि अगर वे खाली पंक्तियाँ वैसे भी नहीं थीं - तो चर सेल की ऊँचाई के साथ यह हमेशा मेरे लिए "छोटी गाड़ी" लगती थी, जहाँ भी खाली पंक्तियाँ थीं।

उम्मीद है कि कुछ अन्य प्रोग्रामर मेरे पोस्ट को पढ़कर 20+ घंटे बर्बाद कर रहे हैं, इसे अपने ऐप में टेबल व्यू के साथ जानने की कोशिश कर रहे हैं। :)


11

मुझे डायनामिक व्यूज़ (कोड द्वारा सेटअप दृश्य और अवरोध) का उपयोग करना था और जब मैं पसंदीदा सेट करना चाहता था तब मैक्लोडआउट की लेबल की चौड़ाई 0. थी। इसलिए मुझे गलत सेल ऊंचाई मिली है।

फिर मैंने जोड़ा

[cell layoutSubviews];

निष्पादित करने से पहले

[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];

उसके बाद लेबल की चौड़ाई अपेक्षा के अनुरूप थी और गतिशील ऊंचाई सही गणना कर रही थी।


9

मान लें कि आपके पास सबव्यू वाला एक सेल है, और आप चाहते हैं कि सबवे + पेडिंग को घेरने के लिए सेल की ऊँचाई पर्याप्त हो।

1) सेल के बराबर सबव्यू का निचला अवरोध सेट करें। जो आप चाहते हैं वह पैडिंग माइनस करें। सेल या cell.contentView पर ही अड़चनें न लगाएँ।

2) सेट या तो tableView की rowHeightसंपत्ति या tableView:heightForRowAtIndexPath:करने के लिए UITableViewAutomaticDimension

3) या तो टेबल व्यू की estimatedRowHeightसंपत्ति सेट करें या tableView:estimatedHeightForRowAtIndexPath:ऊंचाई का सबसे अच्छा अनुमान लगाएं ।

बस।


7

यदि आप प्रोग्रामेटिक रूप से लेआउट करते हैं, तो यहां स्विफ्ट में एंकर का उपयोग करके आईओएस 10 के लिए क्या विचार करना है।

तीन नियम / चरण हैं

नंबर 1: व्यूविलाड पर टेबलव्यू के इस दो गुणों को सेट करें, पहला टेबल टेबलव्यू को बता रहा है कि उनकी कोशिकाओं पर गतिशील आकारों की अपेक्षा करनी चाहिए, दूसरा केवल ऐप को स्क्रॉलबार संकेतक के आकार की गणना करने के लिए है, इसलिए यह इसके लिए मदद करता है प्रदर्शन।

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 100

नंबर 2: यह महत्वपूर्ण है कि आपको सेल के कंटेंट व्यू में सबवे को देखने के लिए नहीं जोड़ना है, और इसके लेआउट्स को भी ऊपर और नीचे से सब-एंकर को जोड़ने के लिए इसके लेआउट का उपयोग करना है, यह कैसे करना है, इसका एक कार्यशील उदाहरण है।

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)

        ])
}

एक विधि बनाएँ जो सबव्यू को जोड़ेगी और लेआउट का प्रदर्शन करेगी, इसे इनिट विधि में कहेंगे।

नंबर 3: धातु का चयन न करें:

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

यदि आप इसे करते हैं तो आप अपने कार्यान्वयन को ओवरराइड करेंगे।

सारणी में गतिशील कोशिकाओं के लिए इस 3 नियमों का पालन करें।

यहाँ एक कार्यान्‍वयन https://github.com/jamesrochabrun/MinimalViewController है


स्विफ्ट 5 में UITableViewAutomaticDimension का नाम बदलकर UITableView.automaticDimension
James Rochabrun

4

यदि आपके पास एक लंबी स्ट्रिंग है। उदाहरण के लिए, जिसके पास लाइन ब्रेक नहीं है। तब आप कुछ समस्याओं में भाग सकते हैं।

"कथित" फिक्स का उल्लेख स्वीकृत उत्तर और कुछ अन्य उत्तरों द्वारा किया गया है। आपको बस जोड़ने की जरूरत है

cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width

मुझे लगता है कि सुरग का उत्तर सबसे पूर्ण और संक्षिप्त है , इसलिए भ्रामक नहीं है।

हालांकि गैर व्याख्या करते हैं कि इन परिवर्तनों की आवश्यकता क्यों है । चलो करते हैं।

किसी प्रोजेक्ट के लिए निम्न कोड ड्रॉप करें।

import UIKit

class ViewController: UIViewController {

    lazy var label : UILabel = {
        let lbl = UILabel()
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.backgroundColor = .red
        lbl.textColor = .black
        return lbl
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // step0: (0.0, 0.0)
        print("empty Text intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step1: (29.0, 20.5)
        label.text = "hiiiii"
        print("hiiiii intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step2: (328.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints"
        print("1 translate intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step3: (992.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints"
        print("3 translate intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step4: (328.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints"
        print("3 translate w/ line breaks (but the line breaks get ignored, because numberOfLines is defaulted to `1` and it will force it all to fit into one line! intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step5: (328.0, 61.0)
        label.numberOfLines = 0
        print("3 translate w/ line breaks and '0' numberOfLines intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step6: (98.5, 243.5)
        label.preferredMaxLayoutWidth = 100
        print("3 translate w/ line breaks | '0' numberOfLines | preferredMaxLayoutWidth: 100 intrinsicContentSize: \(label.intrinsicContentSize)")

        setupLayout()
    }
    func setupLayout(){
        view.addSubview(label)
        label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
}

ध्यान दें कि मैंने कोई आकार की कमी नहीं जोड़ी है । मैंने केवल centerX, centerY बाधाओं को जोड़ा है। लेकिन फिर भी लेबल सही ढंग से आकार दिया जाएगा क्यों?

की वजह से contentSize

इसे बेहतर करने के लिए, पहले चरण 0 रखें, फिर चरण 1-6 पर टिप्पणी करें। रहने दो setupLayout()। व्यवहार का निरीक्षण करें।

फिर चरण 1 को अनकंफर्ट करें, और देखें।

फिर चरण 2 को अनकंफर्ट करें और निरीक्षण करें।

ऐसा तब तक करें जब तक कि आप सभी 6 चरणों को अनलॉक्ड न कर लें और उनके व्यवहार का अवलोकन न करें।

इस सब से क्या निष्कर्ष निकल सकता है? कौन से कारक बदल सकते हैं contenSize?

  1. पाठ की लंबाई: यदि आप लंबा टेक्स्ट है तो अपने intrinsicContentSize की चौड़ाई में वृद्धि होगी
  2. पंक्ति विराम: यदि आप जोड़ते हैं \nतो आंतरिक सीमा का विस्तार सभी लाइनों की अधिकतम चौड़ाई होगी। यदि एक पंक्ति में 25 वर्ण हैं, तो दूसरे में 2 वर्ण हैं और दूसरे में 21 वर्ण हैं तो आपकी चौड़ाई की गणना 25 वर्णों के आधार पर की जाएगी
  3. अनुमति लाइनों की संख्या: आप सेट करना होगा numberOfLinesकरने के लिए 0अन्यथा आप कई पंक्तियों की ज़रूरत नहीं होगी। आपका numberOfLinesअपने intrinsicContentSize के समायोजित करेगा ऊंचाई
  4. समायोजन करना: कल्पना करें कि आपके पाठ के आधार पर, आपके आंतरिक आंतरिक आकार की चौड़ाई थी 200और ऊँचाई थी 100, लेकिन आप लेबल के कंटेनर की चौड़ाई को सीमित करना चाहते थे कि आप क्या करने जा रहे हैं? इसका समाधान एक वांछित चौड़ाई पर सेट करना है। आपको लगता है कि निर्धारित करके करना preferredMaxLayoutWidthकरने के लिए 130फिर अपने नए intrinsicContentSize मोटे तौर पर की चौड़ाई होगा 130। ऊंचाई स्पष्ट रूप से अधिक 100होगी क्योंकि आपको अधिक लाइनों की आवश्यकता होगी। कहा जा रहा है कि अगर आपकी अड़चनें सही तरीके से सेट हो जाती हैं, तो आपको इसका उपयोग करने की आवश्यकता नहीं होगी! उस पर और अधिक के लिए यह उत्तर और इसकी टिप्पणियाँ देखें। आपको केवल उपयोग करने की आवश्यकता है preferredMaxLayoutWidthयदि आपके पास चौड़ाई / ऊंचाई को सीमित करने में बाधा नहीं है जैसा कि कोई कह सकता है "पाठ को लपेटें नहीं जब तक कि इससे अधिक न होpreferredMaxLayoutWidth"। आप प्रमुख / अनुगामी और निर्धारित करते हैं लेकिन 100% निश्चितता के साथ numberOfLinesकरने के लिए 0तो आप कर रहे हैं अच्छा! लंबी कहानी कम सबसे यहाँ जवाब है जो इसे का उपयोग करना चाहिये गलत कर रहे हैं! आप इसकी आवश्यकता नहीं है। जरूरत यह एक संकेत है कि है अपने की कमी सही तरीके से सेट नहीं हैं या आपके पास सिर्फ अड़चन नहीं है

  5. फ़ॉन्ट का आकार: इस बात पर भी ध्यान दें कि यदि आप अपने फ़ॉन्ट को बढ़ाते हैं तो आंतरिक सीमा का आकार बढ़ जाएगा। मैंने अपने कोड में यह नहीं दिखाया। आप अपने दम पर कोशिश कर सकते हैं।

तो वापस अपने tableViewCell उदाहरण के लिए:

आपको बस इतना करना है:

  • सेट numberOfLinesकरने के लिए0
  • हाशिये / किनारों पर सही ढंग से लेबल को कसना
  • सेट करने की कोई आवश्यकता नहीं है preferredMaxLayoutWidth

1

मेरे मामले में मुझे एक छवि के साथ एक कस्टम सेल बनाना होगा जो सर्वर से आ रहा है और किसी भी चौड़ाई और ऊंचाई का हो सकता है। और गतिशील आकार के साथ दो UILabels (चौड़ाई और ऊंचाई दोनों)

मैंने अपने उत्तर में ऑटोलाययट और प्रोग्रामेटिक रूप से वही हासिल किया है:

मूल रूप से @smileyBorg से ऊपर के उत्तर ने मदद की लेकिन systemLayoutSizeFittingSize ने मेरे लिए कभी काम नहीं किया, मेरे दृष्टिकोण में:

1. स्वचालित पंक्ति ऊंचाई गणना संपत्ति का कोई उपयोग नहीं। 2. अनुमानित ऊंचाई का उपयोग न करें 3. अनावश्यक अद्यतन के लिए कोई जरूरत नहीं है। 4. स्वत: पसंदीदा मैक्स लेआउट चौड़ाई का उपयोग नहीं करते। 5. SystemLayoutSizeFittingSize का कोई उपयोग नहीं होना चाहिए (लेकिन मेरे लिए काम नहीं करना चाहिए, लेकिन मुझे नहीं पता कि यह आंतरिक रूप से क्या कर रहा है), लेकिन इसके बजाय मेरी विधि - (फ्लोट) getViewHeight काम कर रही है और मुझे पता है कि यह औपचारिक रूप से क्या कर रहा है।

जब मैं सेल प्रदर्शित करने के कई अलग-अलग तरीकों का उपयोग करता हूं, तो क्या यूआईटेबल व्यू सेल में विभिन्न ऊंचाइयों का होना संभव है?


1

मेरे मामले में, पैडिंग हेडहेडर और सेक्शनफुटर हाइट्स की वजह से थी, जहां स्टोरीबोर्ड ने मुझे इसे न्यूनतम 1 में बदलने की अनुमति दी थी। इसलिए व्यूडालड विधि में:

tableView.sectionHeaderHeight = 0
tableView.sectionFooterHeight = 0

1

मैं बस के 2 मूल्यों के साथ कुछ गूंगा कोशिश और त्रुटि किया था rowHeightऔर estimatedRowHeightऔर बस कुछ डिबगिंग अंतर्दृष्टि प्रदान हो सकता है सोचा:

यदि आप उन दोनों को सेट करते हैं या केवल सेट करते हैं estimatedRowHeightतो आपको वांछित व्यवहार मिलेगा:

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

यह सुझाव दिया गया है कि आप सही अनुमान लगाने के लिए अपना सर्वश्रेष्ठ प्रयास करेंगे, लेकिन अंतिम परिणाम अलग नहीं है। यह सिर्फ आपके प्रदर्शन को प्रभावित करेगा।

यहां छवि विवरण दर्ज करें


यदि आप केवल पंक्ति सेट करते हैं, तो केवल यह करें:

tableView.rowHeight = UITableViewAutomaticDimension

आपका अंतिम परिणाम वांछित नहीं होगा:

यहां छवि विवरण दर्ज करें


यदि आप estimatedRowHeight1 या उससे छोटे सेट करते हैं तो आप की परवाह किए बिना दुर्घटनाग्रस्त हो जाएंगे rowHeight

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1 

मैं निम्नलिखित त्रुटि संदेश के साथ दुर्घटनाग्रस्त हो गया:

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

1

@Smileyborg द्वारा स्वीकृत उत्तर के संबंध में, मैंने पाया है

[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]

ऐसे कुछ मामलों में अविश्वसनीय होना जहां अड़चनें अस्पष्ट हैं। लेआउट इंजन को एक दिशा में ऊंचाई की गणना करने के लिए मजबूर करने के लिए बेहतर है, नीचे UIView पर सहायक श्रेणी का उपयोग करके:

-(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;
}

जहां w: टेबलव्यू की चौड़ाई है


0

बस अपने दृष्टिकोण में इन दो कार्यों को जोड़ें यह आपकी समस्या को हल करेगा। यहां, सूची एक स्ट्रिंग सरणी है जिसमें आपकी हर पंक्ति के स्ट्रिंग हैं।

 func tableView(_ tableView: UITableView, 
   estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        tableView.rowHeight = self.calculateHeight(inString: list[indexPath.row])

    return (tableView.rowHeight) 
}

func calculateHeight(inString:String) -> CGFloat
{
    let messageString = input.text
    let attributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue) : UIFont.systemFont(ofSize: 15.0)]

    let attributedString : NSAttributedString = NSAttributedString(string: messageString!, attributes: attributes)

    let rect : CGRect = attributedString.boundingRect(with: CGSize(width: 222.0, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)

    let requredSize:CGRect = rect
    return requredSize.height
}

-1
swift 4

    @IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var tableView: UITableView!
    private var context = 1
 override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.addObserver(self, forKeyPath: "contentSize", options: [.new,.prior], context: &context)
    }
  // Added observer to adjust tableview height based on the content

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &self.context{
            if let size = change?[NSKeyValueChangeKey.newKey] as? CGSize{
                print("-----")
                print(size.height)
                tableViewHeightConstraint.constant = size.height + 50
            }
        }
    }

//Remove observer
 deinit {

        NotificationCenter.default.removeObserver(self)

    }

-1

यदि सेल ऊंचाई सामग्री द्वारा गतिशील है, तो आपको इसे ठीक से गिनना चाहिए और फिर सेल के प्रस्तुत होने से पहले ऊंचाई मान वापस कर देना चाहिए। एक आसान तरीका यह है कि टेबल सेल ऊंचाई प्रतिनिधि पद्धति पर कॉल करने के लिए नियंत्रक के लिए टेबल व्यू सेल कोड में गिनती पद्धति को परिभाषित करें। यदि तालिका या स्क्रीन की चौड़ाई पर भरोसा है, तो वास्तविक सेल फ़्रेम चौड़ाई (डिफ़ॉल्ट 320) की गणना करना न भूलें । यही है, तालिका सेल ऊंचाई प्रतिनिधि विधि में, सेल चौड़ाई को सही करने के लिए पहले सेल.फ्रेम का उपयोग करें, फिर उपयुक्त मान प्राप्त करने और उसे वापस करने के लिए सेल में परिभाषित गणना ऊंचाई विधि को कॉल करें

पुनश्च। सेल ऑब्जेक्ट जनरेट करने के लिए कोड को कॉल करने के लिए अलग-अलग टेबल व्यू सेल डेलीगेट विधि के लिए किसी अन्य विधि में परिभाषित किया जा सकता है।


-3

UITableView.automaticDimension इंटरफ़ेस बिल्डर के माध्यम से सेट किया जा सकता है:

Xcode> स्टोरीबोर्ड> आकार निरीक्षक

तालिका देखें सेल> रो ऊंचाई> स्वचालित

आकार निरीक्षक


-4

स्विफ्ट में अभी तक एक और iOs7 + iOs8 समाधान

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

नोट: systemLayoutSizeFittingSize मेरे मामले में काम नहीं करता है
djdance

सेल की ऊँचाई सही नहीं है cellForRowAtIndexPath, सेल अभी इस बिंदु पर नहीं है।
एंड्री चेर्नेंको

iOs7 में यह निश्चित मूल्य है, अर्थात यह काम करता है। आप बाहर cellForRowAtIndexPath में सेट कर सकते हैं अगर आप चाहते हैं
djdance
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.