简体   繁体   English

包含UIView的UIImage / UIImageView重绘是否已缩放

[英]UIImage/UIImageView redraw when containing UIView is scaled

My iPad app has a navigation where I show screenshots of the different pages and because I want to show more than one screenshot at once I scale the container to around 24% of the original screenshots (1024x768). 我的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];
}

but when I scale images with "small" text it looks sth like this: 但是当我使用“小”文本缩放图像时,它看起来像这样:

I have to use the big screenshots because if a user taps the image it should scale to 100% and be crispy clear. 我必须使用大截图,因为如果用户点击图像,它应该缩放到100%并且清脆。

is there a way how I can scale the images "smoothly" (on the fly) without ruining performance? 有没有办法如何“平滑”(动态)缩放图像而不破坏性能? it would be enough to have two versions: the full-px one and another for the 24% version. 它有两个版本就足够了:全px和24%版本的另一个版本。

The reason the scaled-down image looks crappy is it's being scaled in OpenGL, which is using fast-but-low-quality linear interpolation. 缩小图像看起来很糟糕的原因是它在OpenGL中进行缩放,OpenGL使用快速但低质量的线性插值。 As you probably know, UIView is built on top of CALayer, which is in turn a sort of wrapper for OpenGL textures. 您可能知道,UIView构建在CALayer之上,而CALayer又是OpenGL纹理的一种包装器。 Because the contents of the layer reside in the video card, CALayer can do all of its magic on the GPU, independent of whether the CPU is busy loading a web site, blocked on disk access, or whatever. 由于该层的内容位于视频卡中,因此CALayer可以在GPU上完成所有的魔术,无论CPU是忙于加载网站,还是阻止磁盘访问,或者其他什么。 I mention this only because it's useful to pay attention to what's actually in the textures inside your layers. 我之所以提到这一点,只是因为注意层内纹理中的实际内容是有用的。 In your case, the UIImageView's layer has the full 1024x768 bitmap image on its texture, and that isn't affected by the container's transform: The CALayer inside the UIImageView doesn't see that it's going to be (let's see..) 246x185 on-screen and re-scale its bitmap, it just lets OpenGL do its thing and scale down the bitmap every time it updates the display. 在你的情况下,UIImageView的图层在其纹理上有完整的1024x768位图图像,并且不受容器变换的影响:UIImageView中的CALayer看不到它(让我们看到..)246x185 on - 屏幕并重新缩放其位图,它只是让OpenGL做它的事情并在每次更新显示时缩小位图。

To get better scaling, we'll need to do it in CoreGraphics instead of OpenGL. 为了获得更好的扩展,我们需要在CoreGraphics而不是OpenGL中进行扩展。 Here's one way to do it: 这是一种方法:

- (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];
        }
    }];
}

And here's TapImageView, a UIImageView subclass that tells us when it's been tapped by sending an action up the responder chain: 这里是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

Instead of scaling the container and all of its subviews. 而不是缩放容器及其所有子视图。 Create a UIImageView from the contents of the container and adjust its frame size to 24% of the original. 从容器的内容创建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];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM