2012-03-22 15 views
5

के लिए पंक्ति हाइट्स की पूर्व-गणना मैं अपनी परियोजना में महीनों के लिए इस के साथ संघर्ष कर रहा हूं।आईओएस: UITableView

यहां सौदा है: मेरे पास केवल 1 सेक्शन के साथ UITableView है, लेकिन UITableViewCells की ऊंचाई अलग-अलग है। प्रत्येक UITableViewCell किसी ऑब्जेक्ट का प्रतिनिधित्व करता है, और नई ऑब्जेक्ट लगातार उपलब्ध हो रही हैं, और UITableView में डाली गई हैं। तालिका में प्रदर्शित होने वाली एक नई ऑब्जेक्ट की प्रभावी ढंग से खोज करने का एकमात्र तरीका यह है कि इसे एक बार खीचें (उस कोड को चलाएं जो UITableViewCell खींचता है और परिणामस्वरूप ऊंचाई क्या है) देखें।

आह, लेकिन समस्या है! UITableView प्रतिनिधि विधि तालिका दृश्य: heightForRowAtIndexPath: सेल को पहली बार खींचा जाने से पहले कहा जाता है! यह समझ में आता है: तालिका उन्हें खींचने से पहले कोशिकाओं को स्थानांतरित करना चाहता है।

मैं बहुत प्रयोग किया गया है और केवल 2 विकल्प मिल सकता है, जो दोनों के प्रमुख कमियां हैं:

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

मेरा समाधान अब तक # 2 का उपयोग करने के लिए किया गया है, लेकिन इसे बनाने से समस्या को कम करने की कोशिश करने के लिए ताकि ऊंचाई परीक्षण केवल एक दूसरे के 1/4 होते हैं, जो "अंतरिक्ष से बाहर" परीक्षण, ताकि मुख्य धागा बहुत अधिक लॉक न हो। लेकिन, समाधान सुरुचिपूर्ण है और अभी भी अंतराल की समस्याओं का कारण बनता है, खासकर पुराने उपकरणों पर। अंतराल इतना खराब है कि मुझे वास्तव में एक बेहतर समाधान की आवश्यकता है।

+2

शायद यह बताएगा कि क्या नई वस्तु है, अगर ऊंचाई की गणना की जा सकती है, तो वास्तव में इसे चित्रित किए बिना? – MadhavanRP

+0

नहीं। यह नहीं कर सकता है। आने वाली वस्तु एक बहुत ही जटिल वस्तु है, एक कस्टम वस्तु है, जिसमें कई अलग-अलग चलने वाले भाग शामिल हैं। मेरा विश्वास करो, यह मेरा पहला विचार भी था और अगर मैं कर सकता तो मैं निश्चित रूप से ऐसा करता। असल में, मैंने सप्ताहों में एक अच्छा "अनुमान लगाने वाला एल्गोरिदम" बनाने की कोशिश की (प्रति पोस्ट 1, मेरी पोस्ट में)। –

उत्तर

2

ऊंचाई की गणना के लिए, this और this answer सुझाव है कि आप CGLayer का उपयोग करके पृष्ठभूमि थ्रेड में ऑफ-स्क्रीन खींच सकते हैं। Link to iOS documentation to CGLayer

और कुछ डेटा आईडी कि एक सेल में प्रदर्शित किया जा रहा है आप इस तरह कोड इस्तेमाल कर सकते हैं पर निर्भर करता है ऊंचाई कैश करने के लिए:

-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath 
{ 
    NSString* objectId = [self getObjectIdForPath:indexPath]; 
    if (![self->idToCellHeight objectForKey: objectId]) 
    { 
     CGFloat height = [object calculateHeightForId:objectId]; 
     [self->idToCellHeight setObject:[NSNumber numberWithFloat:height] forKey: objectId]; 
    } 

    return [[self->idToCellHeight objectForKey:objectId] floatValue]; 
} 
बेशक

पहले ड्रॉ अभी भी सटीक जानने के बिना किया जा करना होगा ऊंचाई, लेकिन यदि आप प्रदर्शित डेटा के आधार पर ऊंचाई की गणना नहीं कर सकते हैं तो कोई कामकाज नहीं है।

+0

बेशक, मैं इस तरह से वस्तुओं की ऊंचाइयों को पहले से ही सहेज रहा/कैशिंग कर रहा हूं। पृष्ठभूमि थ्रेड पर पोस्ट ऑफ़-स्क्रीन खींचना एक आदर्श समाधान होगा, सिवाय इसके कि मुझे UIKit के साथ काम करने के तरीके के बारे में कोई संसाधन नहीं मिल रहा है। ऑब्जेक्ट की ऊंचाई का परीक्षण करने के लिए, मैं एक कस्टम UITableViewCell सबक्लास का एक उदाहरण बना रहा हूं, जिसमें कई UIKit घटक (UILabel, UIImageView, आदि) हैं जब भी मैं पृष्ठभूमि थ्रेड पर इस ड्राइंग ऑपरेशन को करने का प्रयास करता हूं, मुझे एक क्रैश मिलता है ... –

+0

सामान्य रूप से UIKit धागा सुरक्षित नहीं है, उदाहरण के लिए गैर-मुख्य थ्रेड पर UIKit विधियों को कॉल करने से प्रतिपादन इंजन को भ्रमित कर दिया जाएगा जो यूआई को मुख्य थ्रेड से एक ही समय में अपडेट कर रहा है। हालांकि, आईओएस 4.0 पर कुछ यूआईकिट विधियां थ्रेड-सुरक्षित हो गई हैं। यह पोस्ट देखें: http://www.cocoabuilder.com/archive/cocoa/296299- ड्रॉइंग- थ्रेड- सुरक्षा-in-ios.html – Amiramix

+0

यह लिंक भी: http://stackoverflow.com/questions/6087068/thread- इस विषय पर सुरक्षित-यूकिट-विधियों और ऐप्पल दस्तावेज: http://developer.apple.com/library/ios/#releasenotes/General/WhatsNewIniPhoneOS/Articles/iPhoneOS4.html तो मुझे लगता है कि आपके मामले में यह लगने का मामला है पृष्ठभूमि थ्रेड में आकर्षित करने के लिए आप किस UIKit विधियों का उपयोग कर सकते हैं। अनुमोदित, यह संभव नहीं हो सकता है यदि आपका कोड उनमें से कई का उपयोग कर रहा है और उन्हें आसानी से थ्रेड-सुरक्षित समकक्षों के लिए प्रतिस्थापित नहीं किया जा सकता है। – Amiramix

-1

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

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

+0

समस्या * नहीं * * आरई * के बारे में है- ऊंचाई बढ़ाना ... यह पहली गणना के बारे में है। जैसा कि आपने यहां वर्णित किया है, पहले गणना के बाद, मैं पहले ही गणना की गई ऊंचाई को बचा रहा हूं। आपने कहा "जब आप एक नया सेल प्राप्त करते हैं जिसे तालिका में जोड़ा जाना आवश्यक है, तो आइटम की ऊंचाई की गणना करें।" * वह * वह हिस्सा है जो परेशानी पैदा कर रहा है, यद्यपि! –

+0

आह, मुझे लगता है कि मैंने याद किया जहां आपने कहा था "इसे बाद में सहेजना" और सोचा था कि आप ऊंचाई में बहुत अधिक काम कर रहे थे ForRowAtIndexPath, मूर्ख मुझे। –

1

सोचा कि मैं इस पर पिच करूंगा।UITableView के लिए पंक्ति ऊंचाइयों की पूर्व-गणना पूरी तरह से संभव है और अनुकूलन के लिए बहुत अच्छी तरह से काम करती है।

ऑफ स्क्रीन ड्राइंग के विकल्प के रूप में, ऊंचाई निर्धारित करने के लिए कक्षाओं के तरीकों जैसे UILabel का उपयोग करना बिल्कुल ठीक है।

चेतावनी यह है कि लगभग सभी UIView संबंधित विधियां मुख्य धागे पर निष्पादित की जानी चाहिए।

मैं अपने दृष्टिकोण को समझाऊंगा, जो आकार का दृश्य बनाने के लिए है जो मैं ऊंचाई गणना के लिए उपयोग करता हूं।

आकार दृश्य बंद बनाया जा सकता है मुख्य थ्रेड:

UILabel *mySizingLabel = [[UILabel alloc] init];

हालांकि, जब आकार का निर्धारण करने, कि मुख्य थ्रेड पर किया जाना चाहिए। dispatch_async या [[NSOperationQueue] mainQueue] का उपयोग न करने के लिए सावधान रहें। उदाहरण के लिए यह काम नहीं करेगा:

__block CGSize size; 
dispatch_async(dispatch_get_main_queue(), ^{ 
    size = [mySizingLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; 
}]; 

CGFloat myCellHeight = size.height; 

क्योंकि लाइन प्रदान करती है कि myCellHeightsizeThatFits से पहले निष्पादित करेंगे, जिससे एक बकवास मूल्य दे रही है यही कारण है कि।

इसके बजाय, sizeThatFits रिटर्न तक मुख्य धागे पर प्रतीक्षा करना आवश्यक है। यह performSelectorOnMainThread का उपयोग waitYES पर किया जा सकता है। पैरामीटर को NSMutableArray का उपयोग करके उस विधि में और बाहर पारित किया जा सकता है।

इस दृष्टिकोण का एक बड़ा लाभ है कि आप कस्टम ग्राफिक्स संदर्भ का उपयोग करके ऑफस्क्रीन खींचने की आवश्यकता के बिना UIView कक्षाओं द्वारा प्रदान किए गए अंतर्निहित आकार देने के तरीकों का लाभ उठा सकते हैं।

एक और बात, UIKit अतिरिक्त sizeWithFont का उपयोग करने के लिए प्रलोभन न करें। इसके अलावा मुद्दों "You're Doing It Wrong #2: Sizing labels with -[NSString sizeWithFont:...]" में माइक वेलर से प्रकाश डाला से, एक ही चेतावनी लागू होता है कि इस विधि भी पर मुख्य निष्पादित किया जाना चाहिए:

NSString UIKit परिवर्धन संदर्भ

इस वर्ग के विस्तार में वर्णित विधियों चाहिए अपने ऐप के मुख्य थ्रेड से उपयोग किया जाए।

+0

क्यों dispatch_async के बजाय dispatch_sync का उपयोग न करें? – Lukas

0

मैं संक्षेप में व्याख्या करने की कोशिश करूंगा।

मैक्स मेक्लेओड तरह इंगित करता है, जब आप अपने डाटासेट तैयार calculate पंक्ति की ऊँचाई एसिंक्रोनस रूप

-(void)calculateAsyncRowHeights { 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     UITableView *tableView=[self tableView]; 

     for(ManagedObject *o in _tableData) { 
      CGFloat height=[self calculateRowHeight:tableView withObject:o]; //this is the method that calculates row heights. This is a seperate method because when necessary I call it in - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
      [o setRowHeight:height]; //I set rowHeight property of my model object 
     } 

     dispatch_async(dispatch_get_main_queue(), ^{ 

     }); 
    }); 
} 

में उपयोग कर रहा है - (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) मॉडल अगर जाँच indexPath वस्तु की पंक्ति ऊंचाई पहले ही गणना की गई है। यदि हां, तो इसे वापस करें, यदि नहीं, तो इसकी गणना करें [स्वयं गणना करें RowHeight: tableView withObject: o]; विधि और इसके वापसी मूल्य वापस।

यह एक आकर्षण की तरह काम करता है और मेरी सभी प्रदर्शन समस्याओं को हल करता है।