2種類の画像アニメーションを実行する「DCImageAnimation」クラス

画像のタイマーアニメーションとコマ送りアニメーションを実行する「DCImageAnimation」クラスを作成しました。

FrameByFrameAnimation」クラスを改良し、タイマーでも動作するようにしたものです。これによりメモリ使用量が30%軽減されています。

アニメーションは主に以下のパラメータを渡して使用します。

  1. アニメーションの種類(終了時のメソッド処理分岐用)
  2. アニメーションのフレーム数
  3. アニメーション画像ファイル名の接頭詞
  4. アニメーションのリピート回数/ループの有無

デリケードメソッドを指定する事もでき、アニメーション終了後に任意のメソッドを呼ぶことができます。

下記よりサンプルのダウンロードが行えます。

サンプルダウンロード

使用方法

タイマーアニメーション

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//タイマーアニメーション開始
- (void)startTimerAnimation
{
    //クラス初期化
    DCImageAnimation *timerAnimation = [[DCImageAnimation alloc] init];

    //アニメーション開始
    [timerAnimation startTimerAnimating:animationType //アニメーション名 (終了時の処理分岐用)
                                       :animationFrames //アニメーションの総フレーム数
                                       :animationPrefix //アニメーション画像の接頭詞
                                       :isLoopAnimation]; //アニメーションをループさせるか

    //ステージに追加
    [self.view addSubview:[timerAnimation timerAnimationImageView]];
}

コマ送りアニメーション

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//コマ送りアニメーション開始
- (void)startFrameAnimation
{
    //クラス初期化
    DCImageAnimation *frameAnimation = [[DCImageAnimation alloc] init];

    //アニメーション開始
    [frameAnimation startFrameAnimating:animationType //アニメーション名 (終了時の処理分岐用)
                                       :animationFrames //アニメーションの総フレーム数
                                       :animationPrefix //アニメーション画像の接頭詞
                                       :animationRepeatNum]; //アニメーションのリピート回数

    //ステージに追加
    [self.view addSubview:[frameAnimation frameAnimationImageView]];
}

サンプルソースコード

ViewController.h

1
2
3
4
5
6
7
8
9
10
#import <UIKit/UIKit.h>
#import "DCImageAnimation.h"

#define IMAGE_WIDTH  85
#define IMAGE_HEIGHT 120
#define ANIM_TYPE    2

@interface ViewController : UIViewController

@end

ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

//アニメーションのタイプ
enum {
    TIMER_ANIMATION = 1, //タイマーアニメーション
    FRAME_ANIMATION = 2 //コマ送りアニメーション
};

- (void)viewDidLoad
{
    [super viewDidLoad];

    //アニメーション開始
    if (ANIM_TYPE == TIMER_ANIMATION) {
        //タイマーアニメーション
        [self startTimerAnimation];
    } else if (ANIM_TYPE == FRAME_ANIMATION) {
        //コマ送りアニメーション
        [self startFrameAnimation];
    }
}

//タイマーアニメーション開始
- (void)startTimerAnimation
{
    //クラス初期化
    DCImageAnimation *timerAnimation = [[DCImageAnimation alloc] init];

    //fps変更 (指定がない場合は 24fpsになります)
    [timerAnimation setFps:16.0f];

    //アニメーション画像のレクタングル指定 (指定がない場合は W320*H480pxになります)
    [timerAnimation setRectangle:CGRectMake(self.view.center.x - (IMAGE_WIDTH / 2),
                                            self.view.center.y - (IMAGE_HEIGHT / 2),
                                            IMAGE_WIDTH,
                                            IMAGE_HEIGHT)];

    //クラス側のデリゲートに渡す
    timerAnimation.dc_delegate = (id)self;

    //アニメーションのタイプ (アニメーション終了時の処理を分岐させる時に指定します)
    NSString *animationType = @"timer";

    //アニメーションの総フレーム数
    NSInteger animationFrames = 14;

    //アニメーションファイルの接頭詞
    NSString *animationPrefix = @"frame";

    //アニメーションをループさせるか
    BOOL isLoopAnimation = YES;

    //アニメーション開始
    [timerAnimation startTimerAnimating:animationType
                                       :animationFrames
                                       :animationPrefix
                                       :isLoopAnimation];

    //タグを指定 (省いても問題ありません)
    [timerAnimation timerAnimationImageView].tag = 0;

    //ステージに追加
    [self.view addSubview:[timerAnimation timerAnimationImageView]];
}

//コマ送りアニメーション開始
- (void)startFrameAnimation
{
    //クラス初期化
    DCImageAnimation *frameAnimation = [[DCImageAnimation alloc] init];

    //fps変更 (指定がない場合は 24fpsになります)
    [frameAnimation setFps:16.0f];

    //アニメーション画像のレクタングル指定 (指定がない場合は W320*H480pxになります)
    [frameAnimation setRectangle:CGRectMake(self.view.center.x - (IMAGE_WIDTH / 2),
                                            self.view.center.y - (IMAGE_HEIGHT / 2),
                                            IMAGE_WIDTH,
                                            IMAGE_HEIGHT)];

    //クラス側のデリゲートに渡す
    frameAnimation.dc_delegate = (id)self;

    //アニメーションのタイプ (アニメーション終了時の処理を分岐させる時に指定します)
    NSString *animationType = @"frame";

    //アニメーションの総フレーム数
    NSInteger animationFrames = 14;

    //アニメーションファイルの接頭詞
    NSString *animationPrefix = @"frame";

    //アニメーションのリピート回数 (0の場合は無限ループします)
    NSInteger animationRepeatNum = 0;

    //アニメーション開始
    [frameAnimation startFrameAnimating:animationType
                                       :animationFrames
                                       :animationPrefix
                                       :animationRepeatNum];

    //タグを指定 (省いても問題ありません)
    [frameAnimation frameAnimationImageView].tag = 0;

    //ステージに追加
    [self.view addSubview:[frameAnimation frameAnimationImageView]];
}

@end

ソースコード

DCImageAnimation.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#import <UIKit/UIKit.h>

#define SECOND                  60.0
#define ANIM_FPS                24.0
#define ANIM_IMAGE_X            0
#define ANIM_IMAGE_Y            0
#define ANIM_IMAGE_WIDTH        320
#define ANIM_IMAGE_HEIGHT       480
#define ANIM_IMAGE_HEIGHT_4INCH 568
#define ANIM_IMAGE_EXT          @"png"

@protocol DCImageAnimationDelegate;

@interface DCImageAnimation : UIImageView {
    id<DCImageAnimationDelegate> _dc_delegate;
}

#pragma mark - property
@property (nonatomic, assign) id<DCImageAnimationDelegate> dc_delegate;
@property (nonatomic, retain) UIImageView *frameAnimationImageView;
@property (nonatomic, retain) UIImageView *timerAnimationImageView;

#pragma mark - public method
- (id)init;
- (void)startFrameAnimating:(NSString *)animationType :(NSInteger)animationImageNum :(NSString *)animationImagePrefix :(NSInteger)animationRepeatNum;
- (void)startTimerAnimating:(NSString *)animationType :(NSInteger)animationImageNum :(NSString *)animationImagePrefix :(BOOL)isInfiniteLoopAnimation;
- (void)setFps:(CGFloat)fps;
- (void)resetFps;
- (void)setRectangle:(CGRect)rect;
- (void)resetRectangle;

@end

#pragma mark - delegate method
@protocol DCImageAnimationDelegate<NSObject>
@optional
@end

DCImageAnimation.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#import "DCImageAnimation.h"

@implementation DCImageAnimation

@synthesize dc_delegate;
@synthesize frameAnimationImageView = _frameAnimationImageView;
@synthesize timerAnimationImageView = _timerAnimationImageView;

NSInteger _animationImageX          = ANIM_IMAGE_X;
NSInteger _animationImageY          = ANIM_IMAGE_Y;
NSInteger _animationImageWidth      = ANIM_IMAGE_WIDTH;
NSInteger _animationImageHeight     = ANIM_IMAGE_HEIGHT;
CGFloat   _animationFps             = ANIM_FPS;
NSString *_animationImageExt        = ANIM_IMAGE_EXT;
NSString *_animationType;
NSTimer  *_animationTimer;
NSInteger _animationTimerCount      = 0;
BOOL      _isLoopAnimation          = NO;

#pragma mark - Init

// 初期化
- (id)init
{
    self = [super init];
    [self resetFps];
    [self resetRectangle];
    return self;
}

#pragma mark - Frame Animation

// アニメーション設定
- (void)startFrameAnimating:(NSString *)animationType
                           :(NSInteger)animationImageNum
                           :(NSString *)animationImagePrefix
                           :(NSInteger)animationRepeatNum
{
    // アニメーションの種類を保持
    _animationType = animationType;

    // イメージビュー初期化
    _frameAnimationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(_animationImageX,
                                                                             _animationImageY,
                                                                             _animationImageWidth,
                                                                             _animationImageHeight)];

    // 最後の画像が消えないようにする
    _frameAnimationImageView.image = [self getUIImageFromResources:[self animationImageName:animationImagePrefix:animationImageNum]
                                                               ext:_animationImageExt];

    // 画像をタップ可能にする
    _frameAnimationImageView.userInteractionEnabled = YES;

    // アニメーションフレームを配列に入れる
    NSMutableArray *animationImageArray = [NSMutableArray array];
    for (int i = 1; i <= animationImageNum; i++) {
        [animationImageArray addObject:[self animationImageName:animationImagePrefix:i]];
    }
    _frameAnimationImageView.animationImages = [self animationImages:animationImageArray];

    // アニメーションの秒数を設定
    _frameAnimationImageView.animationDuration = [self animationSeconds:animationImageNum];

    // アニメーションのリピート回数を設定
    _frameAnimationImageView.animationRepeatCount = animationRepeatNum;

    // アニメーション開始
    [_frameAnimationImageView startAnimating];

    // アニメーション終了時のメソッド定義
    [self performSelector:@selector(animationDidFinish:)
               withObject:nil
               afterDelay:_frameAnimationImageView.animationDuration];
}

#pragma mark - Timer Animation

// アニメーション設定
- (void)startTimerAnimating:(NSString *)animationType
                           :(NSInteger)animationImageNum
                           :(NSString *)animationImagePrefix
                           :(BOOL)isLoopAnimation
{
    // アニメーションの種類を保持
    _animationType = animationType;

    // イメージビュー初期化
    _timerAnimationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(_animationImageX,
                                                                             _animationImageY,
                                                                             _animationImageWidth,
                                                                             _animationImageHeight)];

    // 画像をタップ可能にする
    _timerAnimationImageView.userInteractionEnabled = YES;

    // アニメーションフレームを配列に入れる
    NSMutableArray *animationImageArray = [NSMutableArray array];
    for (int i = 1; i <= animationImageNum; i++) {
        [animationImageArray addObject:[self animationImageName:animationImagePrefix:i]];
    }

    // リピートの有無を格納
    _isLoopAnimation = isLoopAnimation;

    // アニメーションタイマー開始
    [self setAnimationTimerEvent:[self animationImages:animationImageArray]];
}

// アニメーションタイマーイベント発行
- (void)setAnimationTimerEvent:(NSArray *)animationImageList
{
    // タイマーに渡すパラメータ格納
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:animationImageList forKey:@"animationImageList"];

    // タイマー実行
    _animationTimer = [NSTimer scheduledTimerWithTimeInterval:[self animationTimerInterval]
                                                       target:self
                                                     selector:@selector(startAnimation:)
                                                     userInfo:userInfo
                                                      repeats:YES];
}

// タイマーアニメーション開始
- (void)startAnimation:(NSTimer *)timer
{
    NSArray *animationImageList = [(NSDictionary *)timer.userInfo objectForKey:@"animationImageList"];
    _timerAnimationImageView.image = [animationImageList objectAtIndex:_animationTimerCount];
    _animationTimerCount++;
    if ([animationImageList count] == _animationTimerCount) {
        // タイマー削除
        [self clearAnimationTimer];

        // 最終フレームの処理
        if (_isLoopAnimation) {
            // 再起処理
            [self setAnimationTimerEvent:animationImageList];
        } else {
            // アニメーション終了時のメソッド呼び出し
            [self animationDidStop];
        }
    }
}

// アニメーションタイマー削除
- (void)clearAnimationTimer
{
    if (_animationTimer != nil) {
        [_animationTimer invalidate];
    }
    _animationTimerCount = 0;
}

#pragma mark - delegate method

// アニメーション終了時のメソッド
- (void)animationDidFinish:(id)selector
{
    [self onEndAnimation];
}

// アニメーション終了時のメソッド
- (void)animationDidStop
{
    [self onEndAnimation];
}

// アニメーション終了時の処理
- (void)onEndAnimation
{
    // ここでデリゲートメソッドを呼ぶ事ができます

}

#pragma mark - setter method

// fps指定
- (void)setFps:(CGFloat)fps
{
    _animationFps = fps;
}

// fpsリセット
- (void)resetFps
{
    _animationFps = ANIM_FPS;
}

// レクタングル指定
- (void)setRectangle:(CGRect)rect
{
    _animationImageX      = rect.origin.x;
    _animationImageY      = rect.origin.y;
    _animationImageWidth  = rect.size.width;
    _animationImageHeight = rect.size.height;
}

// レクタングルリセット
- (void)resetRectangle
{
    [self setRectangle:CGRectMake(ANIM_IMAGE_X,
                                  ANIM_IMAGE_Y,
                                  ANIM_IMAGE_WIDTH,
                                  ANIM_IMAGE_HEIGHT)];
}

#pragma mark - getter method

// アニメーション秒数を取得
- (CGFloat)animationSeconds:(NSInteger)animationImageNum
{
    return (animationImageNum / _animationFps);
}

// アニメーションタイマーのインターバル取得
- (CGFloat)animationTimerInterval
{
    return (SECOND / _animationFps) / SECOND;
}

// アニメーション画像を取得
- (NSString *)animationImageName:(NSString *)animationImageSuffix
                                :(int)animationFrame
{
    return ([NSString stringWithFormat:@"%@%@", animationImageSuffix, [NSString stringWithFormat:@"%d", animationFrame]]);
}

// 画像ファイル名を配列で取得する
- (NSArray *)animationImages:(NSMutableArray *)animationImageNameList
{
    // 画像の配列を作成
    NSMutableArray *imageArray = [NSMutableArray array];
    for (int i = 0; i < animationImageNameList.count; i++) {
        NSString *imageTitle = [animationImageNameList objectAtIndex:i];

        // 画像の配列に画像ファイルを追加
        [imageArray addObject:[self getUIImageFromResources:imageTitle
                                                        ext:_animationImageExt]];
    }
    return (imageArray);
}

// 画像リソースの取得
- (UIImage *)getUIImageFromResources:(NSString*)fileName ext:(NSString*)ext
{
  NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:ext];
  UIImage *img = [[UIImage alloc] initWithContentsOfFile:path];
  return (img);
}

@end

関連記事

パラパラアニメを実行する「FrameByFrameAnimation」クラス改訂版

お薦めの参考書

Cocos2d-xスマートフォン2Dゲーム開発講座 Cocos2d-x 3対応
Cocos2d-xを利用したスマートフォン向け3Dゲーム開発の手法を、サンプルを基に作りながら学ぶ事ができます。実際に遊べるゲームサンプルが4種収録されており、iOS / Android両対応のゲームを開発したい方には必見の一冊になっています。

   このエントリーをはてなブックマークに追加

About

Masaki Hirokawa (@dolice_apps)

デザインエンジニア 廣川政樹の開発ブログ。Objective-Cや Javaなど iPhone/Androidアプリ開発に関する技術情報を掲載しています。

iPhone apps

  • リラックス・ヒーリング(無料)
  • 望みが叶う!引き寄せの法則アプリ(無料)
  • ミステリー - 怖い話や不思議な体験、都市伝説まとめ
  • 浮世絵壁紙 - 美しい日本画ギャラリー(無料)
  • 綺麗な壁紙HD iPhone 7/7 Plus/SE & iPod対応(無料)
  • クールな壁紙HD iPhone 7/7 Plus/SE & iPod対応(無料)

Android apps

  • 浮世絵壁紙 - 美しい日本画ギャラリー
  • 綺麗な高画質壁紙

Objective-C Classes

Tag Cloud

ActionScript(9) ActionScript3(7) Ad(4) Adfurikun(2) AdMob(9) Android(10) Animation(17) AppDelegate(3) ARC(1) ArrayList(1) AVAudioPlayer(4) AVAudioSession(1) AVFoundation(3) Banner(2) Bitmap(1) Camera(4) CGAffineTransform(4) CGBlendMode(2) CGContextRef(1) CGImageRef(1) Classes(44) CLLocationManager(1) ConnectivityManager(1) ContentResolver(1) CoreLocation(2) Delegate(3) Device(7) Display(1) DisplayMetrics(1) Download(101) Facebook(6) Foundation(72) Framework(2) Google Analytics(1) Handler(1) iAd(6) ImageView(1) In-AppPurchase(1) iOS(12) iOS 7(15) iOS 7.1(3) iOS 8(1) iPad(7) iPhone(21) iPhone 6(4) Java(7) JavaScript(2) LINE(4) Localize(1) Magazine(1) MediaStore(1) MFComposeViewController(1) Nend(1) NetworkInfo(1) NSArray(23) NSCalendar(3) NSData(2) NSDate(7) NSDateComponents(2) NSDateFormatter(2) NSDictionary(12) NSEnumerator(1) NSIndexSet(1) NSInteger(6) NSMutableArray(17) NSMutableDictionary(5) NSMutableOrderedSet(7) NSMutableString(4) NSMutableURLRequest(1) NSNotificationCenter(1) NSNumber(1) NSObject(1) NSOrderedSet(7) NSRange(2) NSSelectorFromString(1) NSSet(6) NSString(19) NSTimer(4) NSTimeZone(1) NSURL(7) NSURLConnection(1) NSURLRequest(2) NSUserDefaults(7) NSXMLParser(2) Objective-C(222) PHP(1) Products(14) QuartzCore(3) RSS(2) Runnable(1) Sample(51) Screensaver(9) SDK(2) ShareCompat(1) Social(10) StoreKit(1) Twitter(6) UIAccelerometer(3) UIActionSheet(1) UIActivityIndicator(1) UIActivityIndicatorView(3) UIActivityViewController(1) UIAlertView(5) UIApplication(7) UIButton(4) UIColor(3) UIDatePicker(5) UIDevice(6) UIDeviceOrientation(1) UIEvent(6) UIImage(15) UIImagePickerController(4) UIImageView(12) UIKit(58) UILabel(7) UINavigationBar(4) UINavigationItem(1) UIPasteboard(4) UIScreen(1) UIScrollBar(1) UIScrollView(1) UISlider(3) UIStoryboardSegue(3) UISwitch(1) UITabBar(3) UITableView(4) UITextField(2) UIView(11) UIViewAnimationTransition(1) UIViewController(4) UIWebView(5) WindowManager(1) Xcode(48) Xcode 5(8) Xcode 5.1(2)