iOS:在自定义属性动画期间以正确的分辨率将 CGImage 绘制到 CALayer 的子类中

发布于 2024-12-12 06:36:50 字数 3128 浏览 0 评论 0原文

我已经无能为力了...

我有一个视图应该显示一个动画圆形部分,该部分是预渲染的并可作为 png 文件使用(视网膜和非视网膜版本,正确命名;pie_p_000.png 与 pie_p_000.png 对比) [电子邮件受保护])。此部分应根据应用程序内某个百分比值的变化进行动画处理。所以我创建了一个 UIView 的子类,它的层层次结构中有一个自定义的 CALayer。计划是为 CALayer 子类实现自定义动画属性,并在重写的方法 drawInContext: 中更改图片。到目前为止一切顺利,该计划有效,当我使用视图的函数 setPercentageWithFloat: 更改百分比值时会显示动画(完整源代码如下)。 问题是:我真的不知道为什么我的iPhone4总是显示低分辨率图像。我已经尝试过使用比例因子,但这没有帮助。图像要么以正确的尺寸和低分辨率呈现,要么以双倍尺寸呈现。 覆盖display:直接设置contents属性会导致动画不出现(动画期间不显示图像);动画时间结束后,呈现最终图像。在这种情况下,将呈现正确分辨率的图像。

顺便说一句:下面的代码在错误处理、灵活性和优雅方面并不是很复杂,但因为它只是尝试让该东西运行;) - 所以图像仍然翻转呈现等等......

我希望有人有给我一个提示。 谢谢

视图:

#import "ScrollBarView.h"
#import "ScrollBarLayer.h"

@implementation ScrollBarView

@synthesize sbl;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code 
    }
    return self;
}

- (void)setImagesWithName:(NSString*)imageName {
    ScrollBarLayer *ssbl = [[ScrollBarLayer alloc] init];
    ssbl.frame = CGRectMake(0, 0, 30, 30);
    [ssbl setImagesWithName:imageName];
    [self.layer addSublayer:ssbl];

    self.sbl = ssbl;

    [ssbl release];
}

- (void) dealloc {
    self.sbl = nil;
    [super dealloc];
}

- (void)setPercentageWithFloat:(CGFloat)perc {
    if(perc > 1.0){
        perc = 1.0;
    } else if(perc < 0) {
        perc = 0;
    }

    [self.sbl setPercentage:perc];

    CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"percentage"];
    ba.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    ba.duration = 0.8;
    ba.toValue = [NSNumber numberWithFloat:perc];
    [self.sbl addAnimation:ba forKey:nil];
}
@end

视图可以配置为使用不同的名称(theName)处理不同的图像(使用函数setImagesWithName:)。在此方法中,View 将 ScrollBarLayer 添加到其图层属性中。

该层:

#import "ScrollBarLayer.h"

@implementation ScrollBarLayer

@synthesize filename;
@dynamic percentage;

- (id)init {
    self = [super init];
    if (self) {

    }
    return self;
}

- (void)setImagesWithName:(NSString*)imageName {
    self.frame = CGRectMake(0, 0, 30, 30);
    self.percentage = 0;
    self.filename = imageName;
}

+ (BOOL) needsDisplayForKey:(NSString*)key {
    if([key isEqualToString:@"percentage"]) {
        return YES;
    }
    return [super needsDisplayForKey:key];
}

- (void) drawInContext:(CGContextRef)ctx {

    int imageIdx = (int)roundf((float)199 * self.percentage);
    NSString *thisfilename = [self.filename stringByAppendingString:[NSString stringWithFormat:@"%03d.png", i+1]];
    UIImage* c = [UIImage imageNamed:thisfilename];
    CGImageRef img = [c CGImage];
    CGSize sz = CGSizeMake(CGImageGetWidth(img), CGImageGetHeight(img));

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(sz.width, sz.height), NO, 0.0);
    CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width, sz.height), img);
    UIGraphicsEndImageContext();
}

- (void) dealloc {
    self.pictures = nil;
    self.filename = nil;
    [super dealloc];
}

@end

I'm at the end of my wisdom...

I have a view which should display an animated circular section which is pre-rendered and available as png files (both retina and non-retina versions, correctly named; pie_p_000.png vs. [email protected]). This section should be animated according the change to some percentage value within the app. So I made a subclass of UIView which has a custom CALayer within its layer hierarchy. The plan is to implement a custom animatable property for the CALayer subclass and change pictures in the overridden method drawInContext:. So far so good, the plan worked and the animation is shown when I change the percentage value using the function setPercentageWithFloat: of the view (full source below).
The thing is: I really don't know why my iPhone4 always presents the low-res image. I tried already playing around with scale factors, but that didn't help. Either the images are presented in the right size and low-res or the images are presented double size.
Overriding display: and setting the contents property directly has the effect that the animation doesn't appear (during the animation no image is presented); after the animation time the final image is presented. In this case the correct resolution image is presented.

Btw: the following code is not very sophisticated in error-handling, flexibility and elegance yet as it is an attempt just to get that thing running ;) - so the image is still presented flipped and so on...

I hope that somebody has a hint for me.
Thanks

The view:

#import "ScrollBarView.h"
#import "ScrollBarLayer.h"

@implementation ScrollBarView

@synthesize sbl;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code 
    }
    return self;
}

- (void)setImagesWithName:(NSString*)imageName {
    ScrollBarLayer *ssbl = [[ScrollBarLayer alloc] init];
    ssbl.frame = CGRectMake(0, 0, 30, 30);
    [ssbl setImagesWithName:imageName];
    [self.layer addSublayer:ssbl];

    self.sbl = ssbl;

    [ssbl release];
}

- (void) dealloc {
    self.sbl = nil;
    [super dealloc];
}

- (void)setPercentageWithFloat:(CGFloat)perc {
    if(perc > 1.0){
        perc = 1.0;
    } else if(perc < 0) {
        perc = 0;
    }

    [self.sbl setPercentage:perc];

    CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"percentage"];
    ba.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    ba.duration = 0.8;
    ba.toValue = [NSNumber numberWithFloat:perc];
    [self.sbl addAnimation:ba forKey:nil];
}
@end

The View can be configured to work with different images (using the function setImagesWithName:) using different names (theName). Within this method the View adds the ScrollBarLayer to its layer property.

The Layer:

#import "ScrollBarLayer.h"

@implementation ScrollBarLayer

@synthesize filename;
@dynamic percentage;

- (id)init {
    self = [super init];
    if (self) {

    }
    return self;
}

- (void)setImagesWithName:(NSString*)imageName {
    self.frame = CGRectMake(0, 0, 30, 30);
    self.percentage = 0;
    self.filename = imageName;
}

+ (BOOL) needsDisplayForKey:(NSString*)key {
    if([key isEqualToString:@"percentage"]) {
        return YES;
    }
    return [super needsDisplayForKey:key];
}

- (void) drawInContext:(CGContextRef)ctx {

    int imageIdx = (int)roundf((float)199 * self.percentage);
    NSString *thisfilename = [self.filename stringByAppendingString:[NSString stringWithFormat:@"%03d.png", i+1]];
    UIImage* c = [UIImage imageNamed:thisfilename];
    CGImageRef img = [c CGImage];
    CGSize sz = CGSizeMake(CGImageGetWidth(img), CGImageGetHeight(img));

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(sz.width, sz.height), NO, 0.0);
    CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width, sz.height), img);
    UIGraphicsEndImageContext();
}

- (void) dealloc {
    self.pictures = nil;
    self.filename = nil;
    [super dealloc];
}

@end

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

娇纵 2024-12-19 06:36:50

我也需要对此的了解。

我认为问题出在点和像素之间...我认为您应该检查设备是否有视网膜显示屏,如果是这样,您应该将 CGContextDrawImage(...) CGRect (在第二个参数中)替换为宽度和高度的一半。

#define IS_RETINA_DISPLAY() ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00)

...
UIGraphicsBeginImageContextWithOptions(CGSizeMake(sz.width, sz.height), NO, 0.0);

if (IS_RETINA_DISPLAY())
{
    CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width/2.0f, sz.height/2.0f), img);
} else
{
    CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width, sz.height), img);
}
UIGraphicsEndImageContext();
...

但我不确定这个鳃会给你一个“顺利”的结果......

I also would need light on this.

I think that the issue is between points and pixels... I think that you should check if the device has a retina display, and if so, you should replace the CGContextDrawImage(...) CGRect (in second parameter) for one with half width and height.

#define IS_RETINA_DISPLAY() ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00)

...
UIGraphicsBeginImageContextWithOptions(CGSizeMake(sz.width, sz.height), NO, 0.0);

if (IS_RETINA_DISPLAY())
{
    CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width/2.0f, sz.height/2.0f), img);
} else
{
    CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width, sz.height), img);
}
UIGraphicsEndImageContext();
...

But I'm not sure this gill give you a "smooth" result...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文