0

这就是我想要做的事情:我想要一个显示全屏图像的应用程序,并通过滑动 UISlider 使该图像动画化。不断变化的滑块值将通过创建动画的数组的图像进行排序。该动画是转台上的单个角色。当滑块更改值时,角色看起来好像正在围绕转盘旋转。

该项目是 3D 艺术家的作品集。艺术家给了我一系列 180 张以全屏视网膜分辨率渲染的角色图像。他还额外提供了 180 张以非视网膜全屏分辨率渲染的图像。这个想法是,当有人在视网膜 iPad 上从任何角度观看他的角色时,他们可以使用 UISwitch 在视网膜和非视网膜显示之间切换。下面发布的代码在模拟器上运行良好。

但是,在 iPad 4 上运行此代码时,它会运行一段时间,然后我会收到内存警告。然后它崩溃了。我假设有那么多这样大小的图像显示得和有人想要移动滑块一样快,这对于 iPad 4 来说太多了。

我很好奇在使用这样的图像时应该考虑哪些限制。180张图片太多了吗?什么是合理的金额?有没有更有效的方法来产生我想要的结果?我没有采用猜测和检查多少图像不会导致崩溃的方法,而是认为有人可能对我的问题有一些有用的见解。

@implementation RBViewController

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

_imageDisplay.image = [UIImage imageNamed:@"HyperReal_0.png"];

_arrayRetina = [[NSMutableArray alloc] initWithCapacity:180];
_arrayNormal = [[NSMutableArray alloc] initWithCapacity:180];

for(int i = 0; i < 179; i++)
{
    NSString *myImageString = [[NSString alloc] initWithFormat:@"HyperReal_%i.png", i];
    UIImage *myImageGraphic = [UIImage imageNamed:myImageString];
    [_arrayRetina addObject:myImageGraphic];
}

for(int i = 0; i < 179; i++)
{
 NSString *myImageString = [[NSString alloc] initWithFormat:@"lameHyperReal_%i.png", i];
    UIImage *myImageGraphic = [UIImage imageNamed:myImageString];
    [_arrayNormal addObject:myImageGraphic];
}
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (IBAction)switchButton:(id)sender {

if (_switchOnForRetina.isOn == YES)
{
    _imageDisplay.image = [_arrayRetina objectAtIndex:_sliderBar.value];
}
else
    _imageDisplay.image = [_arrayNormal objectAtIndex:_sliderBar.value];

}

- (IBAction)changeSlider:(id)sender {

if (_switchOnForRetina.isOn == YES)
{

_imageDisplay.image = [_arrayRetina objectAtIndex:_sliderBar.value];
}
else

_imageDisplay.image = [_arrayNormal objectAtIndex:_sliderBar.value];
}



@end
4

3 回答 3

1

预缓冲内存开销大致为:

((2048*1536*4 + 1024*768*4)*180)/1024/1024/1024 = 2.63671875 千兆字节

即使正在进行一些地下优化,您的 iPad 也没有足够的内存来安全地运行该程序。

作为解决崩溃的方法,不要预加载所有图像,而只需加载所需的图像

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [ self changeSlider: nil ];
}


- (IBAction)changeSlider:(id)sender {

    if (_switchOnForRetina.isOn == YES)
    {
        _imageDisplay.image =   nil;
        NSString*   pResourcePath   =   [ [ NSBundle mainBundle ] pathForResource: [ NSString stringWithFormat: @"HyperReal_%i", ( int )_sliderBar.value ] ofType: @"png" ];
        _imageDisplay.image =   [ UIImage imageWithContentsOfFile: pResourcePath  ];
    }
    else
    {
        _imageDisplay.image =   nil;
        NSString*   pResourcePath   =   [ [ NSBundle mainBundle ] pathForResource: [ NSString stringWithFormat: @"lameHyperReal_%i", ( int )_sliderBar.value ] ofType: @"png" ];
        _imageDisplay.image =   [ UIImage imageWithContentsOfFile: pResourcePath ];
    }
}

这里更好的解决方案是从您的图像序列中创建 2 个 h264 电影

一个在前向动画顺序,第二个在后向动画顺序

然后根据滑块动画播放正确的电影

当然,这还有很多工作。

干杯

于 2013-03-12T18:03:06.280 回答
1

我不太确定为什么您需要以编程方式在 Retina 和非 Retina 之间切换,因为 @2x 处理得非常好,但我假设有一个我不熟悉的用例,并将其保留在那里。

关于内存使用,我们正在开发具有类似功能的应用程序,我们的解决方案是使用引用所有单个帧的图像名称(或文件路径)的字符串填充数组。在您的情况下,您将存储引用myImageString而不是myImageGraphic.

然后,当您拖动滑块时,您将使用如下内容:

- (IBAction)changeSlider:(id)sender
{
    if (_switchOnForRetina.isOn == YES)
        _imageDisplay.image = [UIImage imageNamed:[_arrayRetina objectAtIndex:_sliderBar.value]];
    else
        _imageDisplay.image = [UIImage imageNamed:[_arrayNormal objectAtIndex:_sliderBar.value]];
}

另外一点,我们使用这个帖子imageWithContentsOfFile是因为内存使用量较低,尽管在这种情况下缓存可能会有所帮助。imageNamed

编辑:

正如麦克拉格所指出的,上述方法仅在修饰时发生变化。这是我正在使用的示例,尽管我UIGestureRecognizer在 UIView 上使用以向方法发送百分比而不是依赖滑块。

- (void)dragPosition:(float)myPercent
{
    NSInteger index = (self.images.count - 1) * myPercent;
    NSString *imageName = [self.images objectAtIndex:index];
    NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
    self.image = [UIImage imageWithContentsOfFile:imagePath];
}

希望这可以帮助!

于 2013-03-12T17:52:19.000 回答
1

不是一次将所有 180 个图像加载到一个数组中,您不能实例化在滑块的连续 UIContolEventValueChanged 事件期间要显示的当前图像吗?

创建滑块

UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 155, 20)];
slider.continuous = YES;
slider.value = 0.0f;
slider.minimumValue = 0.0f;
slider.minimumValue = 179.0f;
[slider addTarget:self action:@selector(handleContinuousSlider:) forControlEvents:UIControlEventValueChanged];

处理它的持续变化事件

- (void)handleContinuousSlider:(UISlider *)slider {
    //create a UIImage with file name that makes the current integer value of the slider
    NSString *myImageString = [[NSString alloc] initWithFormat:@"HyperReal_%i.png", slider.value];
    UIImage *myImageGraphic = [UIImage imageNamed:myImageString];

   //I assume this is a UIImageView
   [_imageDisplay setImage:myImageGraphic];

   [myImageGraphic release];
}
于 2013-03-12T17:48:40.483 回答