UIImage / UIImageView перерисовывается, когда содержащий UIView масштабируется - PullRequest
1 голос
/ 12 января 2012

В моем приложении для iPad есть навигация, в которой я показываю скриншоты разных страниц, и, поскольку я хочу показать более одного скриншота одновременно, я масштабирую контейнер примерно до 24% от оригинальных скриншотов (1024x768).

- (void) loadView
{
    // get landscape screen frame
    CGRect screenFrame = [UIScreen mainScreen].bounds;
    CGRect landscapeFrame = CGRectMake(0, 0, screenFrame.size.height, screenFrame.size.width);

    UIView *view = [[UIView alloc] initWithFrame:landscapeFrame];
    view.backgroundColor = [UIColor grayColor];

    self.view = view;

    // add container view for 2 images
    CGRect startFrame = CGRectMake(-landscapeFrame.size.width/2, 0, landscapeFrame.size.width*2, landscapeFrame.size.height);
    container = [[UIView alloc] initWithFrame:startFrame];
    container.backgroundColor = [UIColor whiteColor];

    // add image 1 (1024x768)
    UIImage *img1 = [UIImage imageNamed:@"01.jpeg"];
    UIImageView *img1View = [[UIImageView alloc] initWithImage:img1];
    [container addSubview:img1View];

    // add image 2 (1024x768)
    UIImage *img2 = [UIImage imageNamed:@"02.jpeg"];
    UIImageView *img2View = [[UIImageView alloc] initWithImage:img2];

    // move img2 to the right of img1
    CGRect newFrame = img2View.frame;
    newFrame.origin.x = 1024.0;
    img2View.frame = newFrame;

    [container addSubview:img2View];

    // scale to 24%
    container.transform = CGAffineTransformMakeScale(0.24, 0.24);

    [self.view addSubview:container];
}

, но когда я масштабирую изображения с «маленьким» текстом, это выглядит примерно так:

Я должен использовать большие скриншоты, потому что еслипользователь нажимает на изображение, оно должно масштабироваться до 100% и быть четким.

Есть ли способ, как я могу масштабировать изображения "плавно" (на лету) без ущерба для производительности?было бы достаточно иметь две версии: full-px одну и другую для 24% версии.

Ответы [ 2 ]

3 голосов
/ 18 апреля 2012

Причина, по которой уменьшенное изображение выглядит дрянно, заключается в том, что оно масштабируется в OpenGL, который использует быструю, но некачественную линейную интерполяцию. Как вы, наверное, знаете, UIView построен на основе CALayer, который, в свою очередь, является своего рода оболочкой для текстур OpenGL. Поскольку содержимое слоя находится в видеокарте, CALayer может выполнять всю свою магию на графическом процессоре, независимо от того, занят ли процессор загрузкой веб-сайта, заблокирован ли доступ к диску или что-то еще. Я упоминаю об этом только потому, что полезно обращать внимание на то, что на самом деле находится в текстурах внутри ваших слоев. В вашем случае слой UIImageView имеет полное растровое изображение 1024x768 на своей текстуре, и это не зависит от преобразования контейнера: CALayer внутри UIImageView не видит, что это будет (давайте посмотрим ..) 246x185 в -экран и масштабировать его растровое изображение, он просто позволяет OpenGL делать свое дело и уменьшать растровое изображение каждый раз, когда он обновляет дисплей.

Чтобы получить лучшее масштабирование, нам нужно сделать это в CoreGraphics вместо OpenGL. Вот один из способов сделать это:

- (UIImage*)scaleImage:(UIImage*)image by:(float)scale
{
    CGSize size = CGSizeMake(image.size.width * scale, image.size.height * scale);
    UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageCopy;
}

- (void)loadView
{
    // get landscape screen frame
    CGRect screenFrame = [UIScreen mainScreen].bounds;
    CGRect landscapeFrame = CGRectMake(0, 0, screenFrame.size.height, screenFrame.size.width);

    UIView *view = [[UIView alloc] initWithFrame:landscapeFrame];
    view.backgroundColor = [UIColor grayColor];

    self.view = view;

    // add container view for 2 images
    CGRect startFrame = CGRectMake(-landscapeFrame.size.width/2, 0, landscapeFrame.size.width*2, landscapeFrame.size.height);
    container = [[UIView alloc] initWithFrame:startFrame];
    container.backgroundColor = [UIColor whiteColor];

    // add image 1 (1024x768)
    UIImage *img1 = [UIImage imageNamed:@"01.png"];
    img1View = [[TapImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
    img1View.userInteractionEnabled = YES; // important!
    img1View.image = [self scaleImage:img1 by:0.24];
    [container addSubview:img1View];

    // add image 2 (1024x768)
    UIImage *img2 = [UIImage imageNamed:@"02.png"];
    img2View = [[TapImageView alloc] initWithFrame:CGRectMake(1024, 0, 1024, 768)];
    img2View.userInteractionEnabled = YES;
    img2View.image = [self scaleImage:img2 by:0.24];
    [container addSubview:img2View];

    // scale to 24% and layout subviews
    zoomed = YES;
    container.transform = CGAffineTransformMakeScale(0.24, 0.24);

    [self.view addSubview:container];
}

- (void)viewTapped:(id)sender
{
    zoomed = !zoomed;

    [UIView animateWithDuration:0.5 animations:^
    {
        if ( zoomed )
        {
            container.transform = CGAffineTransformMakeScale(0.24, 0.24);
        }
        else
        {
            img1View.image = [UIImage imageNamed:@"01.png"];
            img2View.image = [UIImage imageNamed:@"02.png"];
            container.transform = CGAffineTransformMakeScale(1.0, 1.0);
        }
    }
    completion:^(BOOL finished)
    {
        if ( zoomed )
        {
            UIImage *img1 = [UIImage imageNamed:@"01.png"];
            img1View.image = [self scaleImage:img1 by:0.24];
            UIImage *img2 = [UIImage imageNamed:@"02.png"];
            img2View.image = [self scaleImage:img2 by:0.24];
        }
    }];
}

А вот TapImageView, подкласс UIImageView, который сообщает нам, когда его коснулись, отправив действие вверх по цепочке респондента:

@interface TapImageView : UIImageView
@end

@implementation TapImageView

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
    [[UIApplication sharedApplication] sendAction:@selector(viewTapped:) to:nil from:self forEvent:event];
}

@end
1 голос
/ 12 января 2012

Вместо масштабирования контейнера и всех его подпредставлений. Создайте UIImageView из содержимого контейнера и настройте его размер кадра на 24% от исходного.

UIGraphicsBeginImageContext(container.bounds.size);
[container renderInContext:UIGraphicsGetCurrentContext()];
UIImage *containerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

UIImageView *containerImageView = [[UIImageView alloc] initWithImage:containerImage];
CGRectFrame containerFrame = startFrame;
containerFrame.size.with *= 0.24;
containerFrame.size.height *= 0.24;
containerImageView.frame = containerFrame;

[self.view addSubView:containerImageView];
...