2009-09-03 6 views
8

셀을 채우는 큰 이미지가있는 테이블 뷰가 있고 행 크기는 이미지 크기를 기반으로 설정됩니다. 불행히도, 다음 셀로 스크롤 할 때 테이블이 잘못 움직입니다.테이블 뷰에 대해 어떻게 캐시합니까?

테이블에로드되기 전에 행 높이와 이미지를 캐시하면 테이블 뷰가 더 부드럽게 스크롤된다는 말을 들었습니다. 내 모든 데이터는 plist에 저장됩니다.

어떻게 캐싱해야합니까? 코드는 어떻게 생겼으며 어디로 이동합니까?

감사합니다. 나는 또한 행 높이를 계산하는 다음 사용하고

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

    static NSString *detailTableViewCellIdentifier = @"Cell"; 

    DetailTableViewCell *cell = (DetailTableViewCell *) 
     [tableView dequeueReusableCellWithIdentifier:detailTableViewCellIdentifier]; 

    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"DetailTableViewCell" owner:self options:nil]; 
    for(id currentObject in nib) 
    { 
     cell = (DetailTableViewCell *)currentObject; 
    } 
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSString *Path = [[NSBundle mainBundle] bundlePath]; 
    NSString *MainImagePath = [Path stringByAppendingPathComponent:([[appDelegate.sectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:@"MainImage"])]; 

    cell.mainImage.image = [UIImage imageWithContentsOfFile:MainImagePath]; 

    return cell; 
} 

:

여기에 이미지를로드하는 내 코드의

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 
    AppDelegate *appDelegate = (DrillDownAppAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSString *Path = [[NSBundle mainBundle] bundlePath]; 
    NSString *MainImagePath = [Path stringByAppendingPathComponent:([[appDelegate.sectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:@"MainImage"])]; 
    UIImage *imageForHeight = [UIImage imageWithContentsOfFile:MainImagePath]; 
    imageHeight = CGImageGetHeight(imageForHeight.CGImage); 
    return imageHeight; 
} 

편집 : 여기에 아래의 최종 코드입니다.

#define PHOTO_TAG 1 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
static NSString *CellIdentifier = @"Photo"; 

UIImageView *photo; 
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
UIImage *theImage = [UIImage imageNamed:[[appDelegate.sectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:@"MainImage"]]; 

imageHeight = CGImageGetHeight(theImage.CGImage); 
imageWidth = CGImageGetWidth(theImage.CGImage); 

if (cell == nil) { 
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
    photo = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, imageWidth, imageHeight)] autorelease]; 
    photo.tag = PHOTO_TAG; 
    [cell addSubview:photo]; 
    } else { 
    photo = (UIImageView *) [cell viewWithTag:PHOTO_TAG]; 
    [photo setFrame:CGRectMake(0, 0, imageWidth, imageHeight)]; 
    } 

photo.image = theImage; 
return cell; 
} 

답변

16

캐싱은 테이블 뷰 성능을위한 만병 통치약이 아닙니다. 캐싱은 계산에 비용이 많이 드는 경우에만 유용하며 계산을 피할 수 있습니다. 반면에 UITableViewCell에서 뷰가 너무 많으면 캐싱이 아무 것도하지 않습니다. 행 높이가 모두 동일하면 캐시 할 것이 없습니다. +[UIImage imageNamed:]을 사용하는 경우 시스템에서 이미 이미지를 캐싱하고 있습니다.

UITableViewCells의 가장 일반적인 일차 문제점은 너무 많은 서브 뷰를 넣는 것입니다. 어떻게 세포를 만들었습니까? Table View 프로그래밍 가이드를 공부하는 데 시간을 할애 했습니까? 특히 A Closer Look at Table-View Cells? 이 문서를 이해하면 나중에 많은 슬픔을 줄일 수 있습니다.

편집 : (당신이 재사용 가능한 셀을 가져 오는 한 다음 즉시 멀리 던지기, 펜촉을 읽고 셀을 찾는 모든 최상위 레벨 오브젝트 반복하고

먼저 (위의 코드를 기반으로) 하나 당신이 방금 버린 것과 거의 비슷합니다). 그런 다음 문자열을 만들어 파일을 열고 내용을 읽는 데 사용합니다. UITableView가 새로운 셀을 원할 때마다이 작업을 수행합니다. 그리고 같은 행에 대해 반복해서 반복합니다.

그런 다음 UITableView가 높이를 알고 자 할 때 디스크의 이미지를 다시 읽습니다. 그리고 당신은 UITableView가 요청할 때마다 그렇게합니다 (그리고 이것을 최적화하려고 시도하더라도 동일한 행에 대해 여러 번 요청할 수 있습니다).

위에서 링크 된 UITableView 프로그래밍 가이드를 읽어야 시작합니다. 잘하면 많은 도움이 될 것입니다. 이 작업을 수행 할 때 고려해야 할 사항은 다음과 같습니다.

  • 이 셀에는 이미지보기 하나만 존재한다고 표시하셨습니다. 그걸 위해 새끼가 정말로 필요한가요? NIB를 고수하고 (경우에 따라 사용할 이유가있는 경우) NIB 기반 셀을 구현하는 방법에 대한 프로그래밍 안내서를 읽으십시오. IBOutlet을 사용해야하며 최상위 개체를 반복하지 말아야합니다.

  • +[UIImage imageNamed:]은 번들의 경로를 찾을 필요없이 리소스 디렉토리에서 자동으로 파일을 찾습니다.또한 캐시 이미지가 자동으로 표시됩니다.

  • 포인트가 -dequeueReusableCellWithIdentifier:이면 UITableView가 더 이상 사용하지 않고 새 카드를 만드는 대신 재구성 할 수있는 셀을 가져 오는 것입니다. 당신은 그것을 부르고 있지만, 즉시 버리십시오. 당신은 그것이 nil을 반환했는지 확인해야만한다면 NIB에서로드해야합니다. 그렇지 않으면 이미지를 변경하기 만하면됩니다. 다시 프로그래밍 가이드를 읽으십시오. 이것에 대한 많은 예가 있습니다. 단지 -dequeueReusableCellWithIdentifier:이 무엇을하는지 이해하려고 시도했는지 확인하고 프로그램의이 시점에서 입력 한 것으로 취급하지 마십시오.

+0

롭을, 응답 해 주셔서 감사합니다. 나는 그 세포에 대한 하나의 견해만을 가지고 있으며 그것은 하나의 이미지이다. 이전 질문에서 이미지 캐싱에 대한 아이디어가 있습니다. http://stackoverflow.com/questions/1352479/tricks-for-improving-iphone-uitableview-scrolling-performance – Jonah

+0

나는 위의 코드를 추가했습니다. 도움. 감사! – Jonah

+0

rpetrich의 답변이 좋습니다. rowHeight 캐싱과 관련하여 가장 쉬운 해결책은 각 행에 대해 계산 된 높이를 저장하는 NSNumbers 배열을 유지하는 것입니다. 모두 0으로 시작하여 시작할 수 있으며, 0이면 계산하여 결과를 배열에 저장할 수 있습니다. 0이 아니면 반환합니다. Instrument에서 몇 가지 프로파일 링을 수행하고 무작위로 변경하기 전에 실제 시간을 보내고 있는지 살펴보십시오. 행 높이 계산은 문제가 될 수 있습니다. 어쩌면 이미지를 너무 자주 스케일링하고있는 것 같습니다 (가능성이 있음). 악기가 도움이 될 것입니다. –

4

당신이 높이를 캐시해야하는 경우 , 나는 이런 식으로 뭔가 (에 "기사"개체를 표시하는 셀에 대한 캐싱 높이 - 기사 하위 클래스의 어쩌면)했다 :

+ (CGFloat) heightForArticle: (Article*) article atWidth: (CGFloat) width { 

    static NSCache* heightCache = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     heightCache = [NSCache new]; 
    }); 

    NSAssert(heightCache, @"Height cache must exist"); 

    NSString* key = @"unique"; //Create a unique key here 
    NSNumber* cachedValue = [heightCache objectForKey: key]; 
    if(cachedValue) 
     return [cachedValue floatValue]; 
    else { 
     CGFloat height = 40;//Perform presumably large height calculation here 

     [heightCache setObject: [NSNumber numberWithFloat: height] forKey: key]; 

     return height; 
    } 
} 
+0

정말 도움이되었습니다. 높이를 캐시하고 테이블보기 셀 이미지를 캐시하는 데 사용되었습니다. 감사합니다 :) – Supertecnoboff

+0

사실 존경과 함께,하지만 내 의견을 다시 받아 들인다. 이것은 작동하지 않는다, 나는 때때로, 그것은 잘못된 높이 값을 반환 나타났습니다. 그래서 세포는 너무 크거나 너무 작아지게됩니다. – Supertecnoboff

관련 문제