iOS:在自定义属性动画期间以正确的分辨率将 CGImage 绘制到 CALayer 的子类中
我已经无能为力了...
我有一个视图应该显示一个动画圆形部分,该部分是预渲染的并可作为 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我也需要对此的了解。
我认为问题出在点和像素之间...我认为您应该检查设备是否有视网膜显示屏,如果是这样,您应该将 CGContextDrawImage(...) CGRect (在第二个参数中)替换为宽度和高度的一半。
但我不确定这个鳃会给你一个“顺利”的结果......
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.
But I'm not sure this gill give you a "smooth" result...