3

我有一个基于动画的应用程序。经过数小时的尝试和搜索最有效的动画处理器(具有低延迟和最佳内存管理),我发现了令人惊叹的 PNGAnimator (http://www.modejong.com/iPhone/)。有趣的是,我实际上使用的是 JPEG(由于大小),但这不是问题。

问题是该类本身是 UIViewController 的子类,当我将它添加到我的 rootViewController 时,新的视图控制器会与它重叠 - 所以所有界面元素(按钮......)都隐藏在它下面。

这就是为什么我想以某种方式将代码转换为 UIView 的子类。(所以我可以在上面添加按钮)我尝试自己转换代码,但是应用程序会在动画调用时崩溃。你能告诉我我应该在代码中更改什么以便它将充当 UIView 吗?

例如,我尝试在 .h 文件中将其更改为 UIView,然后在 .m 文件中,将引用 self.view 更改为只是 self。然后在我的根视图控制器中将其添加为子视图,但这也不会显示并崩溃。

这是.h文件:

//
//  ImageAnimatorViewController.h
//  PNGAnimatorDemo
//
//  Created by Moses DeJong on 2/5/09.
//

#import <UIKit/UIKit.h>

#define ImageAnimator15FPS (1.0/15)
#define ImageAnimator12FPS (1.0/12)
#define ImageAnimator25FPS (1.0/24)
#define ImageAnimator18FPS (1.0/18)

#define ImageAnimatorDidStartNotification @"ImageAnimatorDidStartNotification"
#define ImageAnimatorDidStopNotification @"ImageAnimatorDidStopNotification"

@class AVAudioPlayer;

@interface ImageAnimatorViewController : UIViewController {

@public

    NSArray *animationURLs;

    NSTimeInterval animationFrameDuration;

    NSInteger animationNumFrames;

    NSInteger animationRepeatCount;

    UIImageOrientation animationOrientation;

    NSURL *animationAudioURL;

    AVAudioPlayer *avAudioPlayer;

@private

    UIImageView *imageView;

    NSArray *animationData;

    NSTimer *animationTimer;

    NSInteger animationStep;

    NSTimeInterval animationDuration;

    NSTimeInterval lastReportedTime;
}

// public properties

@property (nonatomic, copy) NSArray *animationURLs;
@property (nonatomic, assign) NSTimeInterval animationFrameDuration;
@property (nonatomic, readonly) NSInteger animationNumFrames;
@property (nonatomic, assign) NSInteger animationRepeatCount;
@property (nonatomic, assign) UIImageOrientation animationOrientation;
@property (nonatomic, retain) NSURL *animationAudioURL;
@property (nonatomic, retain) AVAudioPlayer *avAudioPlayer;
@property (nonatomic, assign) CGRect viewCGRect;

// private properties

@property (nonatomic, retain) UIImageView *imageView;
@property (nonatomic, copy) NSArray *animationData;
@property (nonatomic, retain) NSTimer *animationTimer;
@property (nonatomic, assign) NSInteger animationStep;
@property (nonatomic, assign) NSTimeInterval animationDuration;

+ (ImageAnimatorViewController*) imageAnimatorViewController;

- (void) startAnimating;
- (void) stopAnimating;
- (BOOL) isAnimating;

- (void) animationShowFrame: (NSInteger) frame;

+ (NSArray*) arrayWithNumberedNames:(NSString*)filenamePrefix
                         rangeStart:(NSInteger)rangeStart
                           rangeEnd:(NSInteger)rangeEnd
                       suffixFormat:(NSString*)suffixFormat;

+ (NSArray*) arrayWithResourcePrefixedURLs:(NSArray*)inNumberedNames;

@end

这里是 .m 文件:

//
//  ImageAnimatorViewController.m
//  PNGAnimatorDemo
//
//  Created by Moses DeJong on 2/5/09.
//

#import "ImageAnimatorViewController.h"

#import <QuartzCore/QuartzCore.h>

#import <AVFoundation/AVAudioPlayer.h>

@implementation ImageAnimatorViewController

@synthesize animationURLs, animationFrameDuration, animationNumFrames, animationRepeatCount,
imageView, animationData, animationTimer, animationStep, animationDuration, animationOrientation, viewCGRect;
@synthesize animationAudioURL, avAudioPlayer;

- (void)dealloc {
    // This object can't be deallocated while animating, this could
    // only happen if user code incorrectly dropped the last ref.

    NSAssert([self isAnimating] == FALSE, @"dealloc while still animating");

  self.animationURLs = nil;
  self.imageView = nil;
  self.animationData = nil;
  self.animationTimer = nil;

  [super dealloc];
}

+ (ImageAnimatorViewController*) imageAnimatorViewController
{
  return [[[ImageAnimatorViewController alloc] init] autorelease];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  // Return YES for supported orientations
  return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

// Implement loadView to create a view hierarchy programmatically, without using a nib.

- (void)loadView {
    UIView *myView = [[UIView alloc] initWithFrame:viewCGRect];
    [myView autorelease];
    self.view = myView;
    /*UIView *myView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
    [myView autorelease];
    self.view = myView;

    // FIXME: Additional Supported Orientations

    if (animationOrientation == UIImageOrientationUp) {
        // No-op
    } else if (animationOrientation == UIImageOrientationLeft) {
        // 90 deg CCW
        //[self rotateToLandscape];
    } else if (animationOrientation == UIImageOrientationRight) {
        // 90 deg CW
        //[self rotateToLandscapeRight];
    } else {
        NSAssert(FALSE,@"Unsupported animationOrientation");
    }
*/
    // Foreground animation images

    UIImageView *myImageView = [[UIImageView alloc] initWithFrame:self.view.frame];
    [myImageView autorelease];
    self.imageView = myImageView;

    // Animation data should have already been loaded into memory as a result of
    // setting the animationURLs property

    NSAssert(animationURLs, @"animationURLs was not defined");
    NSAssert([animationURLs count] > 1, @"animationURLs must include at least 2 urls");
    NSAssert(animationFrameDuration, @"animationFrameDuration was not defined");

    // Load animationData by reading from animationURLs

    NSMutableDictionary *dataDict = [NSMutableDictionary dictionaryWithCapacity:[animationURLs count]];

    NSMutableArray *muArray = [NSMutableArray arrayWithCapacity:[animationURLs count]];
    for ( NSURL* aURL in animationURLs ) {
        NSString *urlKey = aURL.path;
        NSData *dataForKey = [dataDict objectForKey:urlKey];

        if (dataForKey == nil) {
            dataForKey = [NSData dataWithContentsOfURL:aURL];
            NSAssert(dataForKey, @"dataForKey");

            [dataDict setObject:dataForKey forKey:urlKey];
        }

        [muArray addObject:dataForKey];
    }
    self.animationData = [NSArray arrayWithArray:muArray];

    int numFrames = [animationURLs count];
    float duration = animationFrameDuration * numFrames;

    self->animationNumFrames = numFrames;
    self.animationDuration = duration;

    [self.view addSubview:imageView];

    // Display first frame of image animation

    self.animationStep = 0;

    [self animationShowFrame: animationStep];

    self.animationStep = animationStep + 1;

    if (animationAudioURL != nil) {
        AVAudioPlayer *avPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:animationAudioURL
                                                                         error:nil];
    [avPlayer autorelease];
        NSAssert(avPlayer, @"AVAudioPlayer could not be allocated");
        self.avAudioPlayer = avPlayer;

        [avAudioPlayer prepareToPlay];      
    }
}

// Create an array of file/resource names with the given filename prefix,
// the file names will have an integer appended in the range indicated
// by the rangeStart and rangeEnd arguments. The suffixFormat argument
// is a format string like "%02i.png", it must format an integer value
// into a string that is appended to the file/resource string.
//
// For example: [createNumberedNames:@"Image" rangeStart:1 rangeEnd:3 rangeFormat:@"%02i.png"]
//
// returns: {"Image01.png", "Image02.png", "Image03.png"}

+ (NSArray*) arrayWithNumberedNames:(NSString*)filenamePrefix
                              rangeStart:(NSInteger)rangeStart
                                rangeEnd:(NSInteger)rangeEnd
                             suffixFormat:(NSString*)suffixFormat
{
    NSMutableArray *numberedNames = [[NSMutableArray alloc] initWithCapacity:40];

    for (int i = rangeStart; i <= rangeEnd; i++) {
        NSString *suffix = [NSString stringWithFormat:suffixFormat, i];
        NSString *filename = [NSString stringWithFormat:@"%@%@", filenamePrefix, suffix];

        [numberedNames addObject:filename];
    }

    NSArray *newArray = [NSArray arrayWithArray:numberedNames];
    [numberedNames release];
    return newArray;
}

// Given an array of resource names (as returned by arrayWithNumberedNames)
// create a new array that contains these resource names prefixed as
// resource paths and wrapped in a NSURL object.

+ (NSArray*) arrayWithResourcePrefixedURLs:(NSArray*)inNumberedNames
{
    NSMutableArray *URLs = [[NSMutableArray alloc] initWithCapacity:[inNumberedNames count]];
    NSBundle* appBundle = [NSBundle mainBundle];

    for ( NSString* path in inNumberedNames ) {
        NSString* resPath = [appBundle pathForResource:path ofType:nil];    
        NSURL* aURL = [NSURL fileURLWithPath:resPath];

        [URLs addObject:aURL];
    }

    NSArray *newArray = [NSArray arrayWithArray:URLs];
    [URLs release];
    return newArray;
}


// Invoke this method to start the animation

- (void) startAnimating
{
    self.animationTimer = [NSTimer timerWithTimeInterval: animationFrameDuration
                                             target: self
                                           selector: @selector(animationTimerCallback:)
                                           userInfo: NULL
                                            repeats: TRUE];

    [[NSRunLoop currentRunLoop] addTimer: animationTimer forMode: NSDefaultRunLoopMode];

    animationStep = 0;

    if (avAudioPlayer != nil)
        [avAudioPlayer play];

    // Send notification to object(s) that regestered interest in a start action

    [[NSNotificationCenter defaultCenter]
     postNotificationName:ImageAnimatorDidStartNotification
     object:self];  
}

// Invoke this method to stop the animation, note that this method must not
// invoke other methods and it must cancel any pending callbacks since
// it could be invoked in a low-memory situation or when the object
// is being deallocated. Invoking this method will not generate a
// animation stopped notification, that callback is only invoked when
// the animation reaches the end normally.

- (void) stopAnimating
{
    if (![self isAnimating])
        return;

    [animationTimer invalidate];
    self.animationTimer = nil;

    animationStep = animationNumFrames - 1;
    [self animationShowFrame: animationStep];

    if (avAudioPlayer != nil) {
        [avAudioPlayer stop];
        avAudioPlayer.currentTime = 0.0;
        self->lastReportedTime = 0.0;
    }

    // Send notification to object(s) that regestered interest in a stop action

    [[NSNotificationCenter defaultCenter]
     postNotificationName:ImageAnimatorDidStopNotification
     object:self];  
}

- (BOOL) isAnimating
{
    return (animationTimer != nil);
}

// Invoked at framerate interval to implement the animation

- (void) animationTimerCallback: (NSTimer *)timer {
    if (![self isAnimating])
        return;

    NSTimeInterval currentTime;
    NSUInteger frameNow;

    if (avAudioPlayer == nil) {
        self.animationStep += 1;

//      currentTime = animationStep * animationFrameDuration;
        frameNow = animationStep;
    } else {
        currentTime = avAudioPlayer.currentTime;
        frameNow = (NSInteger) (currentTime / animationFrameDuration);
    }

    // Limit the range of frameNow to [0, SIZE-1]
    if (frameNow == 0) {
        frameNow = 0;
    } else if (frameNow >= animationNumFrames) {
        frameNow = animationNumFrames - 1;
    }

    [self animationShowFrame: frameNow];
//  animationStep = frameNow + 1;

    if (animationStep >= animationNumFrames) {
        [self stopAnimating];

        // Continue to loop animation until loop counter reaches 0

        if (animationRepeatCount > 0) {
            self.animationRepeatCount = animationRepeatCount - 1;
            [self startAnimating];
        }
    }
}

// Display the given animation frame, in the range [1 to N]
// where N is the largest frame number.

- (void) animationShowFrame: (NSInteger) frame {
    if ((frame >= animationNumFrames) || (frame < 0))
        return;

    NSData *data = [animationData objectAtIndex:frame];
    UIImage *img = [UIImage imageWithData:data];
    imageView.image = img;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  self.animationRepeatCount = 0;

    [self stopAnimating];
}

@end

非常感谢您的任何建议!

4

1 回答 1

2

不知道为什么你被否决了。我认为您的问题是UIViewController子类已loadView实现该功能,并依赖它来构建 UI。在视图控制器的情况下,如果视图不是从 .xib 加载的,则会自动调用此函数来构建视图。您可以在创建对象后尝试手动调用此函数,它应该做几乎相同的事情。

请记住,该shouldAutorotateToInterfaceOrientation 函数也是一个UIViewController函数,永远不会在UIView

于 2012-09-29T09:14:08.890 回答