2012-08-13 12 views
5

के साथ UITableViewCell का पुन: उपयोग करना मैं का उपयोग UITableViewCell की छवियों को असीमित रूप से लोड करने के लिए कर रहा हूं। यह कुछ सीमावर्ती मामलों को छोड़कर अच्छी तरह से काम करता है जिसमें सेल का पुन: उपयोग किया जाता है, और पिछला ब्लॉक गलत छवि लोड करता है।जीसीडी

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *CellIdentifier = @"Cell"; 

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (!cell) { 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
    } 

    NSString *imagePath = [self imagePathForIndexPath:indexPath];   
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 

    dispatch_async(queue, ^{ 
     UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; 

     dispatch_sync(dispatch_get_main_queue(), ^{ 
      cell.imageView.image = image; 
      [cell setNeedsLayout]; 
     }); 
    }); 

    return cell; 
} 

जहाँ तक मुझे पता के रूप में GCD कतारों रोका नहीं जा सकता। तब इस सीमा के मामले को कैसे रोका जा सकता है? या मुझे इस समस्या को हल करने के लिए जीसीडी के बजाय कुछ और इस्तेमाल करना चाहिए?

+0

मैं GMGridView उपयोग कर रहा हूँ छवि थंबनेल की बहुत बड़ी राशि को दिखाने के लिए और इसके साथ ठीक उसी समस्या आ। मैं फ्लाई पर अपने वेबसर्वर से थंबनेल लोड करने के लिए जीसीडी का भी उपयोग कर रहा हूं। – Humayun

उत्तर

3

जो मैंने किया है उसे सेल में NSOperation ivar जोड़ा गया है। ऑपरेशन छवि को प्राप्त करने, लोड करने और बनाने के लिए ज़िम्मेदार है। पूरा होने पर, यह सेल को पिंग करता है। जब/सेल को हटा दिया जाता है, तो ऑपरेशन रद्द हो जाता है और नष्ट हो जाता है यदि यह समाप्त नहीं हुआ है। -main में रद्दीकरण के लिए ऑपरेशन टेस्ट। सेल को सेल में पास करने के बाद, सेल ऑपरेशन को छोड़ देता है और छवि का उपयोग करता है।

+1

NSOperation निश्चित रूप से GCD तुलना में बेहतर है, आम तौर पर इसका इस्तेमाल करते हैं, और यह सेल के अंदर डाल निश्चित रूप से सबसे अच्छा (और सबसे सुरुचिपूर्ण) तरीका है ... +1 – meronix

+1

WWDC 2012 सत्र 211 यह चाहिए के रूप में अच्छी तरह से इस का एक अच्छा पूर्वाभ्यास है – jrturton

1

आप अपने एसिंक अनुरोध को शुरू करने से पहले अपनी छवि के रीसेट को मजबूर करने का प्रयास कर सकते हैं।

उपयोगकर्ता तालिका स्क्रॉल करेंगे, वह पुरानी छवि से पहले अपने async विधि अधिकार के साथ यह परिवर्तन दिखाई दे सकते हैं एक

सिर्फ इस पंक्ति जोड़ें:

cell.imageView.image = nil; // or a placeHolder image 
dispatch_async(queue, ^{ 
    UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; 

    dispatch_sync(dispatch_get_main_queue(), ^{ 
     cell.imageView.image = image; 
     [cell setNeedsLayout]; 
    }); 
}); 

संपादित करें:

@ एचजीपीसी:

मुझे नहीं लगता कि यह समस्या हल करता है। एक पिछला ब्लॉक अभी भी को नए ब्लॉक से पहले गलत छवि सेट कर सकता है। - hgpc 9 मिनट पहले

तुम सही, यदि उपयोगकर्ता स्क्रॉल तेजी से यह एक समस्या हो सकती है ...

एक ही रास्ता मैं देख जहां स्टोर करने के लिए एक काउंटर संपत्ति के साथ एक कस्टम सेल बनाने के लिए है (एक सिंक तरीके से) प्रत्येक कोशिका के कतार में operationn की संख्या, और फिर async विधि जाँच लें कि काउंटर छवि बदलने के लिए पहले == 1 है में:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
     static NSString *CellIdentifier = @"Cell"; 

     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
     if (!cell) { 
    // MyCustomUITableViewCell has a property counter 
      cell = [[[MyCustomUITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
      cell.counter = 0; 
     } 

     NSString *imagePath = [self imagePathForIndexPath:indexPath];   
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 

     cell.imageView.image = nil; // or a placeHolder image 
     cell.counter = cell.counter + 1; 
     dispatch_async(queue, ^{ 
      UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; 

      dispatch_sync(dispatch_get_main_queue(), ^{ 
       if (cell.counter == 1){ 
        cell.imageView.image = image; 
        [cell setNeedsLayout]; 
       } 
       cell.counter = cell.counter - 1; 

      }); 
     }); 

    return cell; 
} 
+0

मुझे नहीं लगता कि यह समस्या हल करता है। एक पिछला ब्लॉक अभी भी एक नए ब्लॉक से पहले गलत छवि सेट कर सकते हैं। – hpique

+0

सही है ... se मेरा उत्तर – meronix

+0

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

0

आप से पहले एक सेल पहचानकर्ता सेट कर सकते हैं एसिंक्रोनस कार्य लॉन्च करना, और यूआई को अपडेट करने से पहले यह जांचें।

+0

डाउनवोट क्यों? यह एक अच्छा विचार की तरह लगता है। – hpique

0

आपके पास यहां कुछ संभावनाएं हैं।

ए) प्रत्यक्ष जीडीसी के बजाय NSOperationQueue का उपयोग करें।
एनएसओपरेशन जीडीसी पर आधारित है और थ्रेड को रोकने जैसे बहुत सारे प्रबंधन को सक्षम बनाता है।
सेब से concurrency guide पर एक नज़र डालें।

बी) एक तैयार async छवि लोडर का उपयोग करें।
इंटरनेटज़ पर उपलब्ध पर्याप्त फ्री और ओपन सोर्स प्रोजेक्ट उपलब्ध हैं।
मैं व्यक्तिगत रूप से AFNetworking (विशेष रूप से AFNetworking+UIImageView श्रेणी) की अनुशंसा करता हूं।

यदि आप इसे अपने (अनुसंधान, ज्ञान इत्यादि) पर करना चाहते हैं।) आप के साथ क) रहना चाहिए, लेकिन अधिक सुविधाजनक तरीका ख) है।

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

  • बचें imageWithContentsOfFile: और नहीं बल्कि imageNamed: का उपयोग करें। इसका कारण यह है imageNamed: अपनी छवियों को कैश एक बार उनके लोड कहाँ और imageWithContentsOfFile: नहीं है। यदि आपको imageWithContentsOfFile: का उपयोग एनएससीएसी में सभी छवियों को प्रीलोड करना है और इस कैश से छवियों के साथ तालिका को पॉप्युलेट करना है।

  • tableview छवियों का आकार (आयाम और फ़ाइल आकार) में बड़ा नहीं होना चाहिए। यहां तक ​​कि एक 100x100 पीएक्स रेटिना छवि में कुछ केबी से अधिक नहीं है जो निश्चित रूप से लोड करने में इतनी देर लगती नहीं है कि आपको एसिंक लोड की आवश्यकता होगी।

+0

ध्यान दें कि छवि को एक फ़ाइल से लोड किया जा रहा है, और वह अंतराल है जिसे मैं async बनाना चाहता हूं। कोई डाउनलोड शामिल नहीं है। – hpique

+0

आह मैं देखता हूं, हाँ। लेकिन उस मामले में आपको अन्य समस्याएं हैं। मैं जल्द ही अपना जवाब दूंगा। – yinkou

+0

तो मैंने अपना जवाब अपडेट किया। – yinkou

0

तालिका स्क्रॉल नहीं होने पर केवल छवियों को लोड करने के बारे में क्या? cellForRowWithIndexPath: के साथ समस्या यह है कि आप उन कोशिकाओं के लिए बहुत सारे काम कर रहे हैं जिन्हें पिछले स्क्रॉल किया जा रहा है।

चित्र लोड करने के बजाय

जब भी कोशिकाओं dequeued कर रहे हैं, आप उस पर कर सकता है viewDidLoad (या जब भी आपका डेटा मॉडल आरंभ नहीं हो जाता है), और scrollViewDidEndDecelerating: पर।

-(void)viewDidLoad { 
    [super viewDidLoad]; 
    [self initializeData]; 
    [self loadVisibleImages]; 
} 

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { 
    if (scrollView == self.tableView) { 
     [self loadVisibleImages]; 
    } 
} 

-(void)loadVisibleImages { 
    for (NSIndexPath *indexPath in [self.tableview indexPathsForVisibleRows]) { 
     dispatch_async(queue, ^{ 
      NSString *imagePath = [self imagePathForIndexPath:indexPath]; 
            UIImage *image = [UIImage imageWithContentsOfFile:imagePath];   
            dispatch_sync(dispatch_get_main_queue(), ^{ 
       UITableViewCell *cell = [self tableView:self.tableView cellForRowAtIndexPath:indexPath]; 
                cell.imageView.image = image; 
                [cell setNeedsLayout]; 
            }); 
        }); 
    } 
}