UITableView 셀 내부의 URL에서 비동기 이미지로드-스크롤하는 동안 이미지가 잘못된 이미지로 변경됨


158

UITableView 셀 내부에서 그림을 비동기식으로로드하는 두 가지 방법을 작성했습니다. 두 경우 모두 이미지가 정상적으로로드되지만 테이블을 스크롤하면 스크롤이 종료되고 이미지가 올바른 이미지로 돌아갈 때까지 이미지가 몇 번 변경됩니다. 왜 이런 일이 일어나고 있는지 전혀 모른다.

#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

- (void)viewDidLoad
{
    [super viewDidLoad];
    dispatch_async(kBgQueue, ^{
        NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:
                                                       @"http://myurl.com/getMovies.php"]];
        [self performSelectorOnMainThread:@selector(fetchedData:)
                               withObject:data waitUntilDone:YES];
    });
}

-(void)fetchedData:(NSData *)data
{
    NSError* error;
    myJson = [NSJSONSerialization
              JSONObjectWithData:data
              options:kNilOptions
              error:&error];
    [_myTableView reloadData];
}    

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    // Return the number of rows in the section.
    // Usually the number of items in your array (the one that holds your list)
    NSLog(@"myJson count: %d",[myJson count]);
    return [myJson count];
}
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

        myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        if (cell == nil) {
            cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
        }

        dispatch_async(kBgQueue, ^{
        NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];

            dispatch_async(dispatch_get_main_queue(), ^{
        cell.poster.image = [UIImage imageWithData:imgData];
            });
        });
         return cell;
}

... ...

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

            myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
            if (cell == nil) {
                cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
            }
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]];
    NSURLRequest* request = [NSURLRequest requestWithURL:url];


    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse * response,
                                               NSData * data,
                                               NSError * error) {
                               if (!error){
                                   cell.poster.image = [UIImage imageWithData:data];
                                   // do whatever you want with image
                               }

                           }];
     return cell;
}

5
실제 셀에 정보를 저장하려고합니다. 이것은 나쁘다, 매우 나쁘다. n 배열 (또는 비슷한 것)에 정보를 저장 한 다음 셀에 표시해야합니다. 이 경우의 정보는 실제 UIImage입니다. 예, 비동기 적으로로드하지만 배열에로드하십시오.
Fogmeister

1
@Fogmeister 님을 참조하고 poster있습니까? 아마도 그의 커스텀 셀의 이미지 뷰 일 것이므로 EXEC_BAD_ACCESS 가하는 일은 완벽하게 맞습니다. 셀을 모델 데이터의 리포지토리로 사용해서는 안된다는 것이 맞지만, 그가 그렇게하고 있다고 생각하지 않습니다. 그는 자신에게 제시해야 할 것을 커스텀 셀에 제공하고 있습니다. 또한 이것은 더 미묘한 문제입니다. 테이블 뷰를 지원하는 모델 배열에 이미지 자체를 저장하는 것에 대해주의를 기울일 것입니다. 이미지 캐싱 메커니즘을 사용하는 것이 좋으며 모델 객체는 해당 캐시에서 검색해야합니다.
Rob

1
예, 정확히 내 요점입니다. 요청 (전체로 표시)을 보면 이미지를 비동기 적으로 다운로드하여 셀의 imageView에 직접 넣습니다. 따라서 셀을 사용하여 데이터, 즉 이미지를 저장합니다. 그가해야 할 일은 객체를 참조하고 해당 객체 (배열 또는 어딘가에 포함되어 있음)에서 이미지를 요청하는 것입니다. 객체에 아직 이미지가 없으면 자리 표시자를 반환하고 이미지를 다운로드해야합니다. 그런 다음 이미지를 다운로드하여 표시 할 준비가되면 표를 통해 셀을 업데이트 할 수 있도록 표시합니다 (표시되는 경우).
Fogmeister

1
그가하고있는 일은 테이블의 해당 셀로 스크롤 할 때마다 다운로드를 강제로 수행합니다. 이미지가 영구적으로 저장되는지 여부는 그에게 달려 있지만 적어도 테이블 뷰의 수명 동안 저장하십시오.
Fogmeister

1
정확히 : D 그렇게하면 URL에서 이미지를 한 번만 가져와야합니다. Facebook Friend Picker와 같은 것들에서 이것을 볼 수 있습니다. 시작하면 모든 아바타가 회색 자리 표시 자입니다. 그런 다음 스크롤하면 이동하면서 모두 채워집니다. 그러나 이전에 표시된 셀로 다시 스크롤하면 이미 다운로드 한 이미지가 즉시 표시됩니다.
Fogmeister

답변:


230

빠른 전술 수정을 찾고 있다고 가정하면 셀 이미지가 초기화되고 셀 행이 여전히 표시되는지 확인해야합니다.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

    cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];

    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg", self.myJson[indexPath.row][@"movieId"]]];

    NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (data) {
            UIImage *image = [UIImage imageWithData:data];
            if (image) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                    if (updateCell)
                        updateCell.poster.image = image;
                });
            }
        }
    }];
    [task resume];

    return cell;
}

위 코드는 셀이 재사용된다는 사실에서 비롯된 몇 가지 문제를 해결합니다.

  1. 백그라운드 요청을 시작하기 전에 셀 이미지를 초기화하지 않습니다 (새 이미지를 다운로드하는 동안 대기열에서 제외 된 셀의 마지막 이미지가 계속 표시됨). 이미지보기 nilimage속성을 확인하십시오. 그렇지 않으면 이미지가 깜박입니다.

  2. 더 미묘한 문제는 실제로 느린 네트워크에서는 셀이 화면을 스크롤하기 전에 비동기 요청이 완료되지 않을 수 있다는 것입니다. 당신은 사용할 수 있습니다UITableView메소드cellForRowAtIndexPath: (유사하게 이름이 지정된 UITableViewDataSourcemethod 와 혼동하지 말 것 tableView:cellForRowAtIndexPath:) 해당 행의 셀이 여전히 표시되는지 확인할 수 있습니다. nil셀이 보이지 않으면 이 메소드가 반환 됩니다.

    문제는 비동기 메서드가 완료 될 때까지 셀이 스크롤되어 테이블의 다른 행에 다시 사용되었다는 것입니다. 행이 여전히 보이는지 확인하여 화면에서 스크롤 된 행의 이미지로 실수로 이미지를 업데이트하지 않도록합니다.

  3. 당면한 질문과 관련이 없지만, 나는 여전히 현대의 규칙과 API를 활용하기 위해 이것을 업데이트해야한다고 느꼈습니다.

    • 사용하다 NSURLSession-[NSData contentsOfURL:]백그라운드 큐로 디스패치하지 않고 .

    • dequeueReusableCellWithIdentifier:forIndexPath:오히려 사용dequeueReusableCellWithIdentifier: (그러나 해당 프로토 타입에 셀 프로토 타입 또는 레지스터 클래스 또는 NIB를 사용해야합니다). 과

    • Cocoa 명명 규칙 을 준수하는 클래스 이름을 사용했습니다 (예 : 대문자로 시작).

이러한 수정에도 문제가 있습니다.

  1. 위의 코드는 다운로드 한 이미지를 캐싱하지 않습니다. 즉, 이미지를 화면 밖으로 스크롤하고 다시 화면에서 다시 스크롤하면 앱이 이미지를 다시 검색하려고 할 수 있습니다. 아마도 서버 응답 헤더가 NSURLSessionNSURLCache에서 제공하는 상당히 투명한 캐싱을 허용 할 정도로 운이 좋을 수도 있지만 그렇지 않은 경우 불필요한 서버 요청을하고 훨씬 느린 UX를 제공 할 것입니다.

  2. 화면을 스크롤하는 셀에 대한 요청을 취소하지 않습니다. 따라서 100 번째 행으로 빠르게 스크롤하면 해당 행의 이미지가 더 이상 표시되지 않는 이전 99 개의 행에 대한 요청 뒤에 백 로그 될 수 있습니다. 항상 최상의 UX에 대한 가시적 셀에 대한 요청의 우선 순위를 지정하려고합니다.

이러한 문제를 해결하는 가장 간단한 해결 방법 UIImageViewSDWebImage 또는 AFNetworking 과 같은 범주 를 사용하는 입니다. 원하는 경우 위의 문제를 처리하기 위해 자체 코드를 작성할 수 있지만 많은 작업이 있으며 위의 UIImageView범주는 이미이 작업을 수행했습니다.


1
감사. 답을 수정해야한다고 생각합니다. updateCell.poster.image = nilcell.poster.image = nil;가 선언되기 전에 updateCell가 호출됩니다.
Segev

1
내 응용 프로그램은 많은 JSON을 사용하므로 AFNetworking확실히 갈 길입니다. 나는 그것에 대해 알고 있었지만 그것을 사용하기 위해 게으른했다. 캐시가 간단한 코드 라인으로 작동하는 방식에 감탄하고 있습니다. [imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
Segev

2
위의 모든 내용과 SDWebImage를 시도했지만 실제로는 AFNetworking을 시도 할 필요가 없었습니다. 감사합니다 @Rob.
mondousage

1
이미지로드가 완료되고 이미지보기가 업데이트되면 활동 표시기를 제거하십시오. 유일한 트릭은 이미지를 검색하는 동안 셀이 스크롤되지 않고 다른 행에 재사용되는 경우 발생하는 상황을 예상해야한다는 것입니다. 기존 활동 표시기의 존재를 감지하고 제거 / 업데이트해야합니다. 셀에 기존 표시기가 없다고 가정하는 것이 아닙니다.
Rob

1
원래 질문은 " cellForRowAtIndexPath빠르게 스크롤 할 때 왜 이로 인해 이미지가 깜빡 거리는가"였으며, 그 원인과 그 해결 방법을 설명했습니다. 그러나 나는 왜 그것이 충분하지 않은지 설명하고 몇 가지 더 깊은 문제를 설명 했으며이 라이브러리 중 하나를 사용하여이를보다 우아하게 처리하는 데 더 나은 이유를 설명했습니다 (가시적 셀에 대한 요청 우선 순위 지정, 중복 네트워크를 피하기 위해 캐싱) 요청 등). "테이블보기에서 깜박 거리는 이미지를 중지하는 방법"에 대한 질문에 대한 다른 예상치가 확실하지 않습니다.
Rob

15

/ * 나는이 방법으로 그것을하고 또한 그것을 시험했다 * /

1 단계 = viewDidLoad 메소드에서 다음과 같이 테이블에 대한 사용자 정의 셀 클래스 (표의 프로토 타입 셀의 경우) 또는 nib (사용자 정의 셀의 경우 사용자 정의 nib의 경우)를 등록하십시오.

[self.yourTableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:@"CustomCell"];

또는

[self.yourTableView registerNib:[UINib nibWithNibName:@"CustomTableViewCell" bundle:nil] forCellReuseIdentifier:@"CustomCell"];

2 단계 = UITableView의 "dequeueReusableCellWithIdentifier : forIndexPath :"메소드를 다음과 같이 사용하십시오 (이를 위해서는 class 또는 nib를 등록해야합니다).

   - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
            CustomTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell" forIndexPath:indexPath];

            cell.imageViewCustom.image = nil; // [UIImage imageNamed:@"default.png"];
            cell.textLabelCustom.text = @"Hello";

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                // retrive image on global queue
                UIImage * img = [UIImage imageWithData:[NSData dataWithContentsOfURL:     [NSURL URLWithString:kImgLink]]];

                dispatch_async(dispatch_get_main_queue(), ^{

                    CustomTableViewCell * cell = (CustomTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
                  // assign cell image on main thread
                    cell.imageViewCustom.image = img;
                });
            });

            return cell;
        }

1
최종 블록 내에서 cellForRowAtIndexPath를 호출하지 않으면 모든 것이 두 번째로 시작됩니까?
Mark Bridges

@MarkBridges, 아니요. 실제로 tableView의 cellForRowAtIndexPath 메소드를 호출합니다. 같은 이름을 가진 tableView의 데이터 소스 메소드와 혼동하지 마십시오. [self tableView : tableView cellForRowAtIndexPath : indexPath]와 같이 호출 할 수 있습니다. 이것이 혼란을 없애기를 바랍니다.
Nitesh Borad

14

이 문제를 해결하는 여러 가지 프레임 워크가 있습니다. 몇가지 말하자면:

빠른:

목표 -C :


고려해야 할 다른 프레임 워크가 있으면 제안을 추가하십시오.
kean

3
실제로이 SDWebImage문제는 해결되지 않습니다. 이미지 다운로드시기를 제어 할 수 있지만 이 작업에 대한 권한을 묻지 않고 SDWebImage이미지를 할당 UIImageView할 수 있습니다. 기본적으로 질문의 문제는 여전히이 라이브러리로 해결되지 않습니다.
Bartłomiej Semańczyk

이 질문의 문제는 저자가 세포가 재사용되었는지 여부를 확인하지 않았다는 것입니다. 이는 SDWebImage를 포함한 해당 프레임 워크에서 해결되는 매우 기본적인 문제입니다.
kean dec

SDWebImage는 iOS 8부터 매우 게으 르며, 가장 좋아하는 프레임 워크 중 하나 였지만 이제는 PinRemoteImage를 사용하기 시작했습니다.
Joan Cardona

@ BartłomiejSemańczyk 당신은 맞습니다,이 문제는 SDWebimage에 의해 해결되지 않았습니다
Jan

9

스위프트 3

NSCache를 사용하여 이미지 로더에 대한 자체 구현을 작성합니다. 셀 이미지가 깜빡이지 않습니다!

ImageCacheLoader.swift

typealias ImageCacheLoaderCompletionHandler = ((UIImage) -> ())

class ImageCacheLoader {
    
    var task: URLSessionDownloadTask!
    var session: URLSession!
    var cache: NSCache<NSString, UIImage>!
    
    init() {
        session = URLSession.shared
        task = URLSessionDownloadTask()
        self.cache = NSCache()
    }
    
    func obtainImageWithPath(imagePath: String, completionHandler: @escaping ImageCacheLoaderCompletionHandler) {
        if let image = self.cache.object(forKey: imagePath as NSString) {
            DispatchQueue.main.async {
                completionHandler(image)
            }
        } else {
            /* You need placeholder image in your assets, 
               if you want to display a placeholder to user */
            let placeholder = #imageLiteral(resourceName: "placeholder")
            DispatchQueue.main.async {
                completionHandler(placeholder)
            }
            let url: URL! = URL(string: imagePath)
            task = session.downloadTask(with: url, completionHandler: { (location, response, error) in
                if let data = try? Data(contentsOf: url) {
                    let img: UIImage! = UIImage(data: data)
                    self.cache.setObject(img, forKey: imagePath as NSString)
                    DispatchQueue.main.async {
                        completionHandler(img)
                    }
                }
            })
            task.resume()
        }
    }
}

사용 예

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "Identifier")
    
    cell.title = "Cool title"

    imageLoader.obtainImageWithPath(imagePath: viewModel.image) { (image) in
        // Before assigning the image, check whether the current cell is visible
        if let updateCell = tableView.cellForRow(at: indexPath) {
            updateCell.imageView.image = image
        }
    }    
    return cell
}

3
고마워요 그러나 코드에는 약간의 문제가 있습니다. 데이터 = 시도하면? Data (contentsOf : url) {// URL을 위치로 바꾸십시오. 많은 사람들을 도울 것입니다.
Carl Hung

2
코드는있는 그대로 네트워크를 통해 파일을 두 번 다운로드합니다. downloadTaks에서 한 번, Data (cntentsOf :)로 한 번입니다. 다운로드 작업은 실제로 네트워크를 통해 다운로드하고 임시 파일에 데이터를 쓰고 localUrl (귀하의 경우 위치)을 전달하기 때문에 url 대신 사용자 위치를 사용해야합니다. 따라서 데이터는 파일에서만 읽을 수 있도록 로컬 Url을 가리켜 야합니다.
Stéphane de Luca

사용 예에서 "ImageCacheLoader.obtainImageWithPath (imagePath : viewModel.image) ......."이어야합니까?
Tim Kruger

매우 빠르게 스크롤하면 작동하지 않습니다. 셀 재사용으로 인해 이미지가 여러 번 스왑됩니다.
Juan Boero

5

@Nitesh Borad objective C 코드를 사용하여 빠른 버전은 다음과 같습니다.

   if let img: UIImage = UIImage(data: previewImg[indexPath.row]) {
                cell.cardPreview.image = img
            } else {
                // The image isn't cached, download the img data
                // We should perform this in a background thread
                let imgURL = NSURL(string: "webLink URL")
                let request: NSURLRequest = NSURLRequest(URL: imgURL!)
                let session = NSURLSession.sharedSession()
                let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
                    let error = error
                    let data = data
                    if error == nil {
                        // Convert the downloaded data in to a UIImage object
                        let image = UIImage(data: data!)
                        // Store the image in to our cache
                        self.previewImg[indexPath.row] = data!
                        // Update the cell
                        dispatch_async(dispatch_get_main_queue(), {
                            if let cell: YourTableViewCell = tableView.cellForRowAtIndexPath(indexPath) as? YourTableViewCell {
                                cell.cardPreview.image = image
                            }
                        })
                    } else {
                        cell.cardPreview.image = UIImage(named: "defaultImage")
                    }
                })
                task.resume()
            }

3

가장 좋은 대답은이 작업을 수행하는 올바른 방법이 아닙니다. (실제로 indexPath를 모델과 바인딩했지만 항상 좋은 것은 아닙니다. 이미지를로드하는 동안 일부 행이 추가되었다고 상상해보십시오. 이제 주어진 indexPath의 셀이 화면에 있지만 이미지는 상황이 좀처럼 재현하기 어려울 수 있지만 가능합니다.

MVVM 접근 방식을 사용하고 컨트롤러에서 viewModel로 셀을 바인딩하고 viewModel에서 이미지를로드 한 다음 (reactiveCocoa 신호를 switchToLatest 메소드로 할당)이 신호를 구독하고 이미지를 셀에 할당하는 것이 좋습니다. ;)

MVVM을 남용하지 않아야합니다. 보기는 단순해야합니다! ViewModel은 재사용이 가능해야합니다! 컨트롤러에서 View (UITableViewCell)와 ViewModel을 바인딩하는 것이 매우 중요한 이유입니다.


1
예, 인덱스 경로 "전술적 수정"(권장하지 않았지만 OP의 문제를 해결하기 위해 가장 겸손한 편집이었습니다) 은이 문제로 인해 어려움을 겪습니다 (단, 테이블보기에 행을 추가하거나 삭제 한 경우에만). 그리고이 현상이 나타나면 다른 인덱스를 사용하여 동일한 인덱스 경로를 사용하지 않고 적절한 행에 대한 쿼리 모델을 수정할 수 있습니다. 그러나 그 전술적 수정은 여기서 제기 한 것보다 훨씬 더 심각한 문제 (위에서 설명)를 가지고 있습니다. UIImageView내가 추천 하는 범주 솔루션 을 사용하면 인덱스 경로와 관련된 문제가 없습니다.
Rob

2
약간의 소리가 들릴지 모르지만 VIEW에서 모든 종류의 논리를 호출하면이 아키텍처가 남용됩니다.
badeleux

3

필자의 경우 이미지 캐싱 (Used SDWebImage) 때문이 아닙니다. 사용자 정의 셀의 태그가 indexPath.row와 일치하지 않기 때문입니다.

cellForRowAtIndexPath에서 :

1) 사용자 정의 셀에 색인 값을 지정하십시오. 예를 들어

cell.tag = indexPath.row

2) 메인 스레드에서 이미지를 할당하기 전에 이미지가 태그와 일치하여 해당 셀에 속하는지 확인하십시오.

dispatch_async(dispatch_get_main_queue(), ^{
   if(cell.tag == indexPath.row) {
     UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
     thumbnailImageView.image = tmpImage;
   }});
});

2

"Rob"감사합니다 .... UICollectionView와 동일한 문제가 있으며 귀하의 답변으로 문제를 해결할 수 있습니다. 내 코드는 다음과 같습니다.

 if ([Dict valueForKey:@"ImageURL"] != [NSNull null])
    {
        cell.coverImageView.image = nil;
        cell.coverImageView.imageURL=nil;

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            if ([Dict valueForKey:@"ImageURL"] != [NSNull null] )
            {
                dispatch_async(dispatch_get_main_queue(), ^{

                    myCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];

                    if (updateCell)
                    {
                        cell.coverImageView.image = nil;
                        cell.coverImageView.imageURL=nil;

                        cell.coverImageView.imageURL=[NSURL URLWithString:[Dict valueForKey:@"ImageURL"]];

                    }
                    else
                    {
                        cell.coverImageView.image = nil;
                        cell.coverImageView.imageURL=nil;
                    }


                });
            }
        });

    }
    else
    {
        cell.coverImageView.image=[UIImage imageNamed:@"default_cover.png"];
    }

나를 위해, mycell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];절대로 없습니다, 그래서 이것은 효과가 없습니다.
carbocation

1
셀이 표시되는지 여부를 확인할 수 있습니다. for (mycell * updateCell in collectionView.visibleCells) {cellVisible = YES; } if (cellVisible) {cell.coverImageView.imageURL = [NSURL URLWithString : [Dict valueForKey : @ "ImageURL"]]; } 그것은 저에게도 효과가 있습니다
sneha

@ sneha Yep, 그런 식으로 반복하여 표시 되는지 확인할 수는 visibleCells있지만 사용하는 [collectionView cellForItemAtIndexPath:indexPath]것이 더 효율적 이라고 생각 합니다 (처음에 전화를하는 이유).
Rob

@sneha 또한, 위 의이 답변의 코드 샘플에서는 updateCellis가 아닌지 확인 nil하지만 사용하지는 않습니다. 콜렉션 뷰 셀이 여전히 표시되는지 여부를 판별하는 데 사용할뿐만 updateCell아니라이 블록 내부에서 cell더 이상 유효하지 않을 수도 있습니다. 그리고 분명히 그렇다면 nil(이 셀이 보이지 않기 때문에) 아무것도 할 필요가 없습니다.
Rob

2
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
        MyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

        cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];

        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg", self.myJson[indexPath.row][@"movieId"]]];

        NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (data) {
                UIImage *image = [UIImage imageWithData:data];
                if (image) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                        if (updateCell)
                            updateCell.poster.image = image;
                    });
                }
            }
        }];
        [task resume];

        return cell;
    }

0

백그라운드에서 셀에 이미지를로드 할 때 셀로드 속도를 높이고 싶다고 생각합니다. 이를 위해 다음 단계를 수행했습니다.

  1. 파일 확인이 문서 디렉토리에 있는지 여부

  2. 그렇지 않은 경우 처음으로 이미지를로드하여 전화 문서 디렉토리에 저장하십시오. 휴대폰에 이미지를 저장하지 않으려는 경우 셀 이미지를 배경에 직접로드 할 수 있습니다.

  3. 이제 로딩 과정 :

다음을 포함하십시오 : #import "ManabImageOperations.h"

코드는 다음과 같습니다.

NSString *imagestr=[NSString stringWithFormat:@"http://www.yourlink.com/%@",[dictn objectForKey:@"member_image"]];

        NSString *docDir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
        NSLog(@"Doc Dir: %@",docDir);

        NSString  *pngFilePath = [NSString stringWithFormat:@"%@/%@",docDir,[dictn objectForKey:@"member_image"]];

        BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:pngFilePath];
        if (fileExists)
        {
            [cell1.memberimage setImage:[UIImage imageWithContentsOfFile:pngFilePath] forState:UIControlStateNormal];
        }
        else
        {
            [ManabImageOperations processImageDataWithURLString:imagestr andBlock:^(NSData *imageData)
             {
                 [cell1.memberimage setImage:[[UIImage alloc]initWithData: imageData] forState:UIControlStateNormal];
                [imageData writeToFile:pngFilePath atomically:YES];
             }];
}

ManabImageOperations.h :

#import <Foundation/Foundation.h>

    @interface ManabImageOperations : NSObject
    {
    }
    + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage;
    @end

ManabImageOperations.m :

#import "ManabImageOperations.h"
#import <QuartzCore/QuartzCore.h>
@implementation ManabImageOperations

+ (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage
{
    NSURL *url = [NSURL URLWithString:urlString];

    dispatch_queue_t callerQueue = dispatch_get_main_queue();
    dispatch_queue_t downloadQueue = dispatch_queue_create("com.myapp.processsmagequeue", NULL);
    dispatch_async(downloadQueue, ^{
        NSData * imageData = [NSData dataWithContentsOfURL:url];

        dispatch_async(callerQueue, ^{
            processImage(imageData);
        });
    });
  //  downloadQueue=nil;
    dispatch_release(downloadQueue);

}
@end

문제가 발생하면 답변을 확인하고 의견을 말하십시오 ....


0

간단히 변경하면

dispatch_async(kBgQueue, ^{
     NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:   [NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
     dispatch_async(dispatch_get_main_queue(), ^{
        cell.poster.image = [UIImage imageWithData:imgData];
     });
 });

으로

    dispatch_async(kBgQueue, ^{
         NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:   [NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
         cell.poster.image = [UIImage imageWithData:imgData];
         dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
         });
     });

0

URL을 전달하면됩니다.

NSURL *url = [NSURL URLWithString:@"http://www.myurl.com/1.png"];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data,    NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (data) {
        UIImage *image = [UIImage imageWithData:data];
        if (image) {
            dispatch_async(dispatch_get_main_queue(), ^{
                    yourimageview.image = image;
            });
        }
    }
}];
[task resume];

이유를 알 수 있습니까?
558

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

    If (cell == nil)
    {

        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"QTStaffViewCell" owner:self options:nil];
        cell = [nib objectAtIndex: 0];

    }

    StaffData = [self.staffArray objectAtIndex:indexPath.row];
    NSString *title = StaffData.title;
    NSString *fName = StaffData.firstname;
    NSString *lName = StaffData.lastname;

    UIFont *FedSanDemi = [UIFont fontWithName:@"Aller" size:18];
    cell.drName.text = [NSString stringWithFormat:@"%@ %@ %@", title,fName,lName];
    [cell.drName setFont:FedSanDemi];

    UIFont *aller = [UIFont fontWithName:@"Aller" size:14];
    cell.drJob.text = StaffData.job;
    [cell.drJob setFont:aller];

    if ([StaffData.title isEqualToString:@"Dr"])
    {
        cell.drJob.frame = CGRectMake(83, 26, 227, 40);
    }
    else
    {
        cell.drJob.frame = CGRectMake(90, 26, 227, 40);

    }

    if ([StaffData.staffPhoto isKindOfClass:[NSString class]])
    {
        NSURL *url = [NSURL URLWithString:StaffData.staffPhoto];
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url
                completionHandler:^(NSURL *location,NSURLResponse *response, NSError *error) {

      NSData *imageData = [NSData dataWithContentsOfURL:location];
      UIImage *image = [UIImage imageWithData:imageData];

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

2
설명이없는 코드 덤프는 거의 도움이되지 않습니다. 컨텍스트를 제공하려면이 답변을 편집하십시오.
Chris
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.