Сбой прокрутки изображения

Я реализовал прокрутку с пейджингом для прокрутки некоторых изображений (графиков) на полной странице (например, приложение Photo, установленное на iPhone).

Я нашел приведенный ниже код, в котором используется классическое трехстраничное решение (я сделал небольшую модификацию для своего приложения), но, даже если он «работает», прокрутка кажется медленной, и часто после прокрутки некоторых изображений происходит сбой приложения.

Я использую Xcode 4.2 с включенной опцией ARC и тестирую оба на устройстве iPad. Изображения (10 jpg) размером 2048x1539 со средним размером 200/250 КБ каждое.

Есть ли кто-нибудь, кто может помочь мне в поиске причины проблемы?

Спасибо, Коррадо.

const int numImages = 10;
const float kPageWidth = 1024.0f;
const float kPageHeight = 768.0f;


- (void)viewDidLoad {
[super viewDidLoad];

    scroll.contentSize = CGSizeMake(kPageWidth * numImages, kPageHeight);
imageview1 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kPageWidth, kPageHeight)];

imageview2 = [[UIImageView alloc] initWithFrame:CGRectMake(kPageWidth, 0, kPageWidth, kPageHeight)];    

imageview3 = [[UIImageView alloc] initWithFrame:CGRectMake(kPageWidth * 2, 0, kPageWidth, kPageHeight)];

scroll.contentOffset = CGPointMake(0, 0);

[imageview1 setImage:[UIImage imageNamed:@"grafico_0.jpg"]];
imageview1.contentMode = UIViewContentModeScaleAspectFit;
[imageview1 setTag:1];

imageview2.contentMode = UIViewContentModeScaleAspectFit;
[imageview2 setTag:2];

imageview3.contentMode = UIViewContentModeScaleAspectFit;
[imageview3 setTag:3];

[scroll addSubview:imageview1];
[scroll addSubview:imageview2];
[scroll addSubview:imageview3];        
}


- (void)scrollViewDidScroll:(UIScrollView*)scrollView {

const CGFloat currPos = scrollView.contentOffset.x;
const NSInteger selectedPage = lroundf(currPos * (1.0f / kPageWidth)); 
const NSInteger zone = 1 + (selectedPage % 3); 
const NSInteger nextPage = selectedPage + 1;
const NSInteger prevPage = selectedPage - 1;

/// Next page
if (nextPage < numImages)
{
    NSInteger nextViewTag = zone + 1;
    if (nextViewTag == 4)
        nextViewTag = 1;

    UIImageView* nextView = (UIImageView*)[scrollView viewWithTag:nextViewTag];      
    nextView.frame = (CGRect){.origin.x = nextPage * kPageHeight, .origin.y = 0.0f, kPageHeight, kPageWidth};

    NSString *str = [NSString stringWithFormat:@"grafico_%d.jpg", nextPage];
    UIImage* img = [UIImage imageNamed:str];
    nextView.image = img;
}


/// Prev page
if (prevPage >= 0)
{
    NSInteger prevViewTag = zone - 1;
    if (!prevViewTag)
        prevViewTag = 3;

    UIImageView* prevView = (UIImageView*)[scrollView viewWithTag:prevViewTag];      
    prevView.frame = (CGRect){.origin.x = prevPage * kPageHeight, .origin.y = 0.0f, kPageHeight, kPageWidth};

    NSString *str = [NSString stringWithFormat:@"grafico_%d.jpg", prevPage];
    UIImage* img = [UIImage imageNamed:str];
    prevView.image = img;
}

}

person Corrado    schedule 11.05.2012    source источник


Ответы (1)


Вы не должны использовать imageNamed: для загрузки больших изображений, потому что этот метод кэширует изображения и должен использоваться только для небольших изображений, которые вы используете несколько раз в своем приложении (например, изображения для кнопок и т. д.). Этот метод печально известен тем, что вызывает проблемы с памятью при использовании со многими большими изображениями.

Вместо этого переключитесь на imageWithContentsOfFile:. Загрузка ваших изображений с помощью этих методов гарантирует, что изображения не кэшируются, а память освобождается после того, как вы больше не используете эти изображения.

Если прокрутка кажется вялой, вы можете переместить загрузку изображения в фоновый поток, используя performSelectorInBackground:

[self performSelectorInBackground:@selector(retrieveImageData:) withObject:imagePath];

загрузка UIImage происходит в этом методе:

- (void)retrieveImageData:(NSString *)imagePath {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
  [self performSelectorOnMainThread:@selector(imageDataRetrieved:) withObject:image waitUntilDone:NO];
  [pool release];
}

и прикрепление изображения к UIImageView в основном потоке (манипуляции с пользовательским интерфейсом не должны происходить в фоновом потоке):

- (void)imageDataRetrieved:(UIImage)*image {
  yourImageView.image = image;
}
person joern    schedule 11.05.2012
comment
Спасибо, я заменил ImageNamed на imageWithContentsOfFile, и приложение не падает, даже если прокрутка все еще медленная. - person Corrado; 12.05.2012
comment
Вы загружаете изображения, как я описал в своем ответе? Что вы подразумеваете под медленной прокруткой? Что прокрутка останавливается на миллисекунду при загрузке нового изображения? Или что вы можете прокручивать только одно изображение за раз? - person joern; 12.05.2012
comment
Я имею в виду, что переход между изображениями кажется вялым. Я уменьшил размер изображений до 1024x768, и это немного лучше (2048x1539 (250Kb) изображения слишком велики?). Я также пытаюсь понять ваше второе решение, но оно мне не очень понятно (это мое первое приложение!). - person Corrado; 12.05.2012
comment
Такой размер изображения не должен быть проблемой. Я делаю то же самое в своем приложении, используя метод, который я упомянул выше. В вашем коде вы загружаете новое изображение в основной поток. Это означает, что все приложение (включая анимацию прокрутки) останавливается до полной загрузки изображения. Именно вялость. Мой метод загружает изображение в фоновом режиме. Это означает, что загрузка не блокирует все приложение. Это похоже на реальную жизнь на фабрике, где вы вызываете второго работника, чтобы загрузить изображение и вернуть его вам, когда оно загрузится, вместо того, чтобы делать все самостоятельно. - person joern; 12.05.2012
comment
Кстати, я отредактировал пример кода в своем ответе. Названия методов не совпадают. Может быть, это добавило вам путаницы ;-) - person joern; 12.05.2012
comment
Я думал, что метод предварительной загрузки 3 изображений решит проблему. В любом случае, я попытаюсь реализовать ваше альтернативное решение (вы правы, название метода меня смутило). Большое тебе спасибо ! P.S. Есть еще много секретов, которые мне нужно узнать... - person Corrado; 12.05.2012
comment
Я рад, что смог помочь. Дайте мне знать, когда вам понадобится дополнительная помощь, это действительно широкое поле. Пожалуйста, примите мой ответ, нажав на белую галочку. - person joern; 12.05.2012