2013-05-02 40 views
5

Tôi biết điều này đã được hỏi nhiều lần nhưng xin vui lòng chịu với tôi như tôi đã cố gắng cho một ngày có thể để tìm một giải pháp.UITableView dequeued tế bào tải lại hình ảnh cũ

Tôi có trình phân tích cú pháp rss, chế độ xem bảng với: văn bản, chi tiết và hình ảnh được đặt thành AccessoryDisclosureIndicator.view.

Hình ảnh tải tuyệt vời với tính năng GCD đơn giản: di chuyển nhanh cho hàng trăm kết quả. KHÔNG lag, KHÔNG có lỗi nếu tôi có kết nối tốt.

Vấn đề là, trong giây lát - chúng nhấp nháy khi tải vì ô đang được sử dụng lại. Ngoài ra nếu kết nối kém, đôi khi nó sẽ rời khỏi hình ảnh lộn xộn NHƯNG văn bản/chi tiết là chính xác, chỉ có hình ảnh cũ ... Vì vậy, hãy để tôi lặp lại, các văn bản/chi tiết cập nhật tốt và KHÔNG BAO GIỜ được xếp hàng lại sai, chỉ cần hình ảnh đôi khi hiếm khi được xếp hàng sai trên các kết nối xấu/thu giữ-cuộn qua lại nhanh chóng.

Câu hỏi của tôi, ai đó có thể giúp tôi cache/gắn thẻ cell.accs.views không? Tôi đã thử đặt cellID nhưng gặp sự cố khi triển khai. Mã dưới đây của tôi hoạt động tốt nếu kết nối không bao giờ chậm lại, chỉ là một nhấp nháy nhẹ khi xếp hàng lại một ô mà tôi không quan tâm.

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

if (cell == nil) { 
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; 

    [cell.textLabel setNumberOfLines:3]; 
    cell.textLabel.font = [UIFont boldSystemFontOfSize:14.0]; 

    [cell.detailTextLabel setNumberOfLines:3]; 
    cell.detailTextLabel.font = [UIFont systemFontOfSize:12.0]; 
    cell.detailTextLabel.textColor = [UIColor blackColor]; 
} 

RSSItem * rssItem = (RSSItem *)(_rssParser.rssItems)[indexPath.row]; 

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 
dispatch_async(queue, ^{ 
    //This is what you will load lazily 
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:rssItem.imageURL]]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 

     UIImageView *accImageView = [[UIImageView alloc] initWithImage:[UIImage imageWithData:data ]]; 

     [accImageView setFrame:CGRectMake(0, 0, accImageView.image.size.width, 92)]; 
     cell.accessoryView = accImageView; 

     //[cell setNeedsLayout];//not needed 
    }); 
}); 

[cell.textLabel setText:rssItem.title]; 
[cell.detailTextLabel setText:rssItem.summary]; 

return cell;} 

Trả lời

0

Dưới đây là những gì tôi đã làm bằng cách sử dụng SDWebImage, tôi đã buộc phải sử dụng một trình giữ chỗ cho tất cả các ô, tôi chỉ cần đặt những người không có hình ảnh vào một biểu tượng chữ V nhỏ. Hoạt động như mong đợi. Cảm ơn tất cả mọi người.

RSSItem * rssItem = (RSSItem *)(_rssParser.rssItems)[indexPath.row]; 

if (rssItem.imageURL == nil){ 
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)]; 
    [imageView setImageWithURL:[NSURL URLWithString:rssItem.imageURL] placeholderImage:[UIImage imageNamed:@"chevron.png"]]; 
    cell.accessoryView = imageView; 
}else{ 
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 110, 96)]; 
    imageView.contentMode = UIViewContentModeScaleAspectFit; 
    [imageView setImageWithURL:[NSURL URLWithString:rssItem.imageURL] placeholderImage:[UIImage imageNamed:@"loading.png"]]; 
    cell.accessoryView = imageView; 
} 

[cell.textLabel setText:rssItem.title]; 
[cell.detailTextLabel setText:rssItem.summary]; 

return cell; 
1

Bạn chỉ cần xóa hình ảnh cũ khi sử dụng lại ô.

Cuối cùng, bạn chỉ có thể thêm:

cell.accessoryView = nil; 
+0

vấn đề của tôi là rõ ràng sâu hơn, cảm ơn bạn tho – JoshP

1

Vấn đề là, trong một khoảnh khắc thứ hai - họ nhấp nháy trên tải, bởi vì các tế bào được tái sử dụng

Nhưng đó câu trả lời của bạn ngay tại đó. Các tế bào đang được tái sử dụng, và tôi không thấy bất cứ điều gì trong mã của bạn mà loại bỏ hình ảnh. Nếu hình ảnh không phải là hình ảnh phù hợp cho hàng này (vì nó đang được sử dụng lại trong một hàng khác), bạn cần đặt cell.accessoryView thành không (hoặc đến một hình ảnh có hình giữ chỗ) bây giờ. Hình ảnh bên phải sẽ không xuất hiện cho đến sau (dispatch_async).

Tuy nhiên, mã bạn đang sử dụng rất thiếu sót, vì nó đang tìm nạp hình ảnh từ Internet mỗi lần, ngay cả khi chúng tôi đã thấy hàng này và do đó đã tải xuống hình ảnh. Cuốn sách của tôi có nhiều mã tốt hơn để tải hình ảnh trong chế độ xem bảng bằng cách tải xuống chúng nếu cần và lưu trữ chúng sau đó. Nó cũng cho bạn thấy làm thế nào để ngăn chặn việc tải xuống nếu hàng được cuộn ra khỏi bàn trước khi hình ảnh đến, do đó tiết kiệm băng thông:

http://www.apeth.com/iOSBook/ch37.html#_http_requests

Cuộn xuống nơi nó nói "Giả sử, ví dụ, bạn cần phải tải xuống hình ảnh để làm hình thu nhỏ trong các ô của UITableView. Hãy xem xét cách những hình ảnh này có thể được cung cấp một cách uể oải theo yêu cầu. "

+0

Yea ... I figured tôi cần phải delagate một bộ nhớ cache để ngăn chặn redownloading, nhưng có ... tôi cũng cần phải tìm ra cách để gắn thẻ các tế bào ... Tôi là một kẻ điên cuồng đọc những mẩu tin của bạn, nhưng yea im a noob – JoshP

+0

Dự án ví dụ làm việc có thể tải xuống thực tế tại đây: https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch37p920downloader/p754p772downloader – matt

+0

Ví dụ của bạn trông giống như một cái gì đó id muốn thử, cảm ơn. Muốn cho tôi một gợi ý về cách chạy rss.items.title/text/imgurl/mảng của tôi thông qua addobject: d? – JoshP

10

Vấn đề chính là trước khi bạn dispatch_async tải hình ảnh mới (mà bạn biết có thể mất vài phút, ngay cả giây khi kết nối chậm), đặt lại hình ảnh cũ hoặc tải hình ảnh đó.

Hai vấn đề thứ yếu:

  1. Hãy nhận biết rằng nó có thể, nếu sử dụng một mạng rất chậm, mà do thời gian hình ảnh được trở lại, mà bạn có thể cuộn các tế bào ra khỏi màn hình (ví dụ như tưởng tượng mà bạn đã lật màn hình cuộn lên và sau đó, trong khi hình ảnh vẫn đang được tải, bạn sẽ lật nó xuống). Bạn thực sự cần đảm bảo rằng ô vẫn hiển thị trước khi cố gắng đặt hình ảnh.

    Bạn có thể làm điều này bằng cách kiểm tra (trên hàng đợi chính) dù các tế bào thậm chí còn có thể nhìn thấy:

    dispatch_async(dispatch_get_main_queue(), ^{ 
        UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath]; 
        if (updateCell != nil) { 
         // update image in `updateCell` 
        } 
    }); 
    

    Xin lưu ý, phương pháp UITableView này, cellForRowAtIndexPath, không nên nhầm lẫn với các phương pháp UITableViewDataSource, tableView:cellForRowAtIndexPath:. Trước đây, bạn chỉ cần kiểm tra xem ô có hiển thị hay không và nếu có, hãy trả về một con trỏ tới ô đó. Thứ hai, rõ ràng, là phương pháp mà bạn tham chiếu trong câu hỏi của bạn, để tạo và cấu hình các ô xem bảng. Cũng lưu ý rằng ý tưởng này, rằng bạn sử dụng các NSIndexPath để kiểm tra xem liệu các tế bào vẫn còn nhìn thấy, giả định rằng không có hàng bổ sung có thể có thể có thể được chèn vào trong xem bảng trong thời gian can thiệp. Điều này thường không phải là một giả định hợp lệ, vì vậy bạn thậm chí có thể quay trở lại mô hình cơ bản và tính lại indexPath cho ô này trước khi tiếp tục với logic trên để xem liệu ô đó có còn nhìn thấy được hay không.

  2. Bạn có thể không muốn sử dụng hàng đợi toàn cầu vì trên mạng thực sự chậm mà các yêu cầu của bạn có thể bắt đầu bị tải xuống và bạn có thể gặp phải rất nhiều yêu cầu mạng đồng thời. Đây là một vấn đề bởi vì (a) hệ thống không thể thực hiện nhiều hơn 4 hoặc 5 yêu cầu đồng thời cùng một lúc; và (b) bạn sẽ sử dụng số lượng hạn chế các chuỗi công nhân do iOS cung cấp. Bạn thực sự không nên có nhiều hơn 4 hoặc 5 yêu cầu đồng thời (nếu bạn có nhiều hơn thế, không chỉ bạn sẽ thấy không đạt được hiệu suất, nhưng họ thực sự sẽ chặn và cuối cùng thời gian chờ). Tôi khuyên bạn nên xem xét sử dụng một số NSOperationQueue mà bạn đặt maxConcurrentOperationCount thành bốn hoặc năm.

    Ngoài ra, điều này dành cho một tối ưu hóa cuối cùng, đặc biệt làm cho các yêu cầu này có thể hủy (và, rõ ràng là làm cho logic hủy thực sự hủy yêu cầu mạng chứa trong đó). Do đó, nếu bạn cuộn nhanh đến hàng thứ 100 trong bảng, bạn thực sự không muốn có các ô hiển thị đang chờ tải xuống hình ảnh cho 99 hàng trước đó để hoàn thành trước. Nếu bạn hủy yêu cầu hình ảnh cho các ô đã cuộn ra khỏi màn hình, điều đó sẽ giải quyết được sự cố này.

Nếu bạn muốn chẩn đoán hành vi của ứng dụng trên kết nối mạng chậm, tôi có thể yêu cầu Mac mô phỏng mọi thứ từ tốc độ cao đến chất lượng thực sự kém mạng trên trình mô phỏng. Trong menu Xcode trong Xcode, chọn "Công cụ nhà phát triển mở ..." và sau đó chọn "Công cụ dành cho nhà phát triển khác". Sau khi bạn đăng nhập, bạn sẽ thấy một tùy chọn cho "Hardware IO Tools cho Xcode". Công cụ Network Conditioner có ở đó. Bạn có thể thử nghiệm ứng dụng của mình trong trường hợp xấu nhất, điều này có thể khiến bạn suy nghĩ không chỉ về hai vấn đề mà tôi phác thảo ở trên, mà còn có nhiều tính năng nâng cao hơn như lưu vào bộ nhớ hình ảnh và tương tự.

Bạn thực sự muốn lưu vào bộ nhớ cache hình ảnh, ít nhất là lưu trữ liên tục, lý tưởng cho cả RAM và lưu trữ liên tục. Có các loại UIImageView giải quyết cả hai vấn đề không đồng bộ ở trên, cũng như cung cấp một số bộ nhớ đệm: Hai điều bạn có thể xem xét là SDWebImageAFNetworking.

+0

Ohhh gawd, với dòng điều hòa ON Tôi thấy rằng ô sẽ chạy qua 4 hoặc 5 hình ảnh cho đến khi nó được cái đúng ... Tại sao cái này khó thế? Tôi là tất cả cho bộ nhớ đệm, nhưng im gặp rắc rối khi thêm các thẻ và kiểm tra đối với họ hoặc tôi chỉ không thể tìm thấy một hướng dẫn tốt ... – JoshP

+1

@JoshP Hoặc [SDWebImage] (https://github.com/rs/SDWebImage) hoặc Danh mục 'UIImageView' của [AFNetworking] (https://github.com/AFNetworking/AFNetworking) cả hai sẽ làm cho cuộc sống của bạn * nhiều * dễ dàng hơn. Nếu bạn thực sự muốn viết của riêng bạn, hãy cho tôi biết và tôi có thể giúp bạn tìm câu trả lời hay. Khi tôi tự viết, cá nhân tôi sử dụng kết hợp 'NSCache' cho bộ đệm RAM và thư mục' Documents' để lưu trữ bộ nhớ lưu trữ liên tục (mặc dù tôi nghĩ quy ước bây giờ là sử dụng thư mục 'Cache' riêng biệt). Tôi thực sự khuyên bạn nên gắn bó với AFNetworking hoặc SDWebImage. – Rob

+0

Tôi sẽ thử những người đó, mọi người làm điều này hàng ngày và tôi không thể tìm thấy một hướng dẫn tốt, nó chỉ boggles tâm trí rằng bảng quên rằng di chuyển của nó, thực sự? Làm thế nào nó có thể nhớ văn bản nhưng quên hình ảnh? Điều này có vẻ giống như một cái gì đó mà táo nên giải quyết automagically :) Tôi về để không dequeue các tế bào nhưng đó là vô lý. Cảm ơn, trong tâm trí của tôi tôi biết nó có thể được thực hiện bằng cách sửa đổi mã của tôi ở trên mà không có lớp bên ngoài và có thể chỉ là một bộ nhớ cache appdel, nhưng yea như tôi đã nói tôi cần phải nhìn khó hơn cho ví dụ tốt hơn. – JoshP

Các vấn đề liên quan