月別アーカイブ: 2015年7月

【NSUserDefaults】データの保存

小さなゲームの得点などを保存しておくにはNSUserDefaultsが良さそうです。

参考にさせて頂いたサイト。
NSUserDefaults – iPhoneアプリ開発の虎の巻
逆引きObjective-C for iPhoneアプリ – ユーザーデフォルトの基本的な使い方

// NSUserDefaultsの取得
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];

とかくと、NSUserDefaultsがあれば出してくれるし、なければ作ってくれるという有難い。

で、セーブとロードを作ってみた。

#pragma mark - セーブとロード

- (void)save{
    // NSUserDefaultsの取得
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    // データの保存処理
    [defaults setInteger:recordCount forKey:@"record"];//レコードを保存
    [defaults setInteger:totalCount forKey:@"total"];//トータルを保存
    [defaults synchronize];
}

- (void)load{
    // NSUserDefaultsの取得
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
    // データの取得
    recordCount = (int)[defaults integerForKey:@"record"];//レコードを取り出す
    totalCount = (int)[defaults integerForKey:@"total"];//トータルを取り出す。
    
    _recordHyouji.text = [NSString stringWithFormat:@"%04d",recordCount];//それぞれ表示
    _totalHyouji.text = [NSString stringWithFormat:@"%05d",totalCount];

}

起動した時などviewDidLoadでloadメソッドを
保存したいタイミングでsaveメソッドを呼び出すと良い感じです。

小数点以下の桁数を揃える。

float型をそのまま表示しようとすると小数点以下6桁まで表示されてしまう。

[NSString stringWithFormat:@”%f”,1.1234567];

↑これだと
1.123456

参考にさせて頂いたサイト。
ストップウォッチ【objective-c】

@”%f”のところを
@”%05.2f”とすると

小数点以下2桁までと、小数点も一つと数えて5桁の表示になる。

つまり、
[NSString stringWithFormat:@”%05.2f”,1.1234567];

とすると

01.12

と表示される。

合ってるかな?

数字の桁数を揃える。

得点表示などで0010点など10の位、100の位などまだ数字が入ってないけど表示をしておきたい場合のやり方。

参考にさせて頂いたサイト。
Storyboardから始めるiPhoneアプリ開発 [Xcode][objective-c]整数値の先頭に0を付けて桁数を揃える方法

[NSString stringWithFormat:@"%04d", 10];

これで0010と表示されます。

インスタンス変数とプロパティの違い。

直前の2つの投稿でNSTimerについて書いた。

NSTimerの概念。 | iPhoneアプリ備忘録
NSTimerでカウントダウンタイマー。 | iPhoneアプリ備忘録

ちょっと変数の名前が違うのでわかりにくいと思うが、
NSTimerの概念。 | iPhoneアプリ備忘録の方はcountとtimerにプロパティを使い、
NSTimerでカウントダウンタイマー。 | iPhoneアプリ備忘録の方はインスタンス変数を使っている。

まだ初心者なので、インスタンス変数とプロパティの違いがよくわかっていない。

色々と検索するとここが参考になった。
Objective-Cのインスタンス変数とプロパティの違い・関係について。プロパティに… – Yahoo!知恵袋

メソッドの変数はメソッドの中だけ、
インスタンス変数はクラスの中だけ、
プロパティはクラスの外からでも使える、

ってことでよいのかな?

詳しい人に聞いたら怒られそうな解釈だけど、まだ小さなアプリしか作ってないのでまだ分からなくてもいいかな。

NSTimerでカウントダウンタイマー。

前回の記事でNSTimerは一定時間ごとにアクションを起こすものだと書いた。
viewDidLoadメソッドに入れておけば画面が表示されて一定時間後にアラートを出したり広告を変えたりするには便利だ。

しかし、カウントダウンタイマーとして使うにはviewDidLoadメソッドに入れると使いにくい。
何か方法があるのかもしれないがよく判らん。

簡単にできる方法としてはメソッドの中で動作させるのが良いだろう。

参考にさせて頂いたサイトはコチラ。
Objective-C – NSTimerの基本的な使い方 – Qiita

#import "ViewController.h"

@interface ViewController ()
{
    int count;
    NSTimer *timer_;
}
@property (weak, nonatomic) IBOutlet UILabel *timeHyouji2;
@end

@implementation ViewController

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

- (IBAction)timeStart2:(UIButton *)sender {
    //タイマーが動いていないときにタイマー開始
    if (![timer_ isValid]) {
        //[timer_ fire];
        count = 30;    
        self.timeHyouji2.text = [NSString stringWithFormat:@"%d",count];
        timer_ = [NSTimer scheduledTimerWithTimeInterval:1
                                                  target:self
                                                selector:@selector(time:)
                                                userInfo:nil
                                                 repeats:YES];
    }
}

    
-(void)time:(NSTimer*)timer{
    NSLog(@"%d", count);
    count -= 1;
    self.timeHyouji2.text = [NSString stringWithFormat:@"%d",count];
    //タイマーが有効かどうか
    NSString *str = [timer_ isValid] ? @"yes" : @"no";
    NSLog(@"isValid:%@", str);

    if (count <= 0.0) {
        [timer_ invalidate];
    }

}

@end

NSTimerの概念。

カウントダウンタイマーを付けたい。
タイマーを使うにはNSTimerを使うらしい。

参考にしたサイト
【iOS】タイマー(NSTimer)を実装:iPhoneアプリ内にカウントダウンタイマーを実装する | 日本VTR実験室
iOS 7 でカウントダウンタイマーを実装する – Qiita

はじめ、NSTimer自体が一定時間ごとにカウントアップしたり、カウントダウンしたりするものだと思っていた。
しかしながら、色々とサイトを見ていると、NSTimer自体は一定時間ごとにアクションを起こす為の物とわかった。

概念を理解するのに思い込みは大敵だ。

サンプルコード

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *timeHyouji;//画面に表示するためラベルとアウトレット接続
@property (nonatomic)         int time;//カウントダウンの時間(回数)の設定を入れる
@property (nonatomic, strong) NSTimer* countdown_timer;//
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
        self.countdown_timer = [NSTimer scheduledTimerWithTimeInterval:1 //1秒ごとに
                                                            target:self 
                                                          selector:@selector(update) //update関数を呼び出す
                                                          userInfo:nil
                                                           repeats:YES];//yesでくりかえします。
    self.time = 30;//1秒を30回なので30秒になる。

}

- (void)update //呼ばれる関数
{
    self.timeHyouji.text = [NSString stringWithFormat:@"%d",self.time];//数字を表示
    
    if (self.time <= 0.0) {//もしtimeが0以下ならば
        [self.countdown_timer invalidate];//タイマーを止める。
    }
    else {//そうでなければ
        self.time--;//timeから1を引く
    }
}

重要なのは

        self.countdown_timer = [NSTimer scheduledTimerWithTimeInterval:1 //1秒ごとに
                                                            target:self 
                                                          selector:@selector(update) //update関数を呼び出す
                                                          userInfo:nil
                                                           repeats:YES];//yesでくりかえします。

ここの部分。
これが呼び出されるとタイマーがスタートする。
viewDidLoadメソッドに入れておけば表示とともにカウントがスタートする。

こちら
【iOS】時間を指定して処理をする場合はNSTimerを使う | AdMax Tech Blog
に書いてあるように

ある画面に何分以上いたら「シェアしてよ」のアラートを出す
ある一定時間同じ画面にいたら画面描画を変える

などの場合には良さげです。

このサンプルコードの場合は起動したらすぐにNSTimerが作動し、1秒毎にupdateメソッドが呼び出されラベルの数値が書き換えられて0になると止まる。

NSTimerは設定時間ごとにメソッドを呼び出すだけなのでカウントダウンや、タイマーなどの仕組みは呼び出されるメソッド側で考えなければならない。

Objective-cでランダム。

ランダムで条件変更したいと思い調べてみました。

参考にさせて頂いたサイト。
だいたい47度 Objective-Cの乱数作成はarc4random_uniform
Objective-Cと戦うブログ: 乱数生成はarc4randomよりarc4random_uniformを使うといいらしい

で、arc4random_uniform()というのを使うと良いようです。
例えばカッコの中に10と入れると0〜9の数値がランダムで出るらしい。

- (IBAction)Tap:(UIButton *)sender {
    int randInt = arc4random_uniform(10);// 0から9の乱数を生成
    if (randInt == 0) //もし乱数が0だったら
    {
        [self hoge1];//hoge1を実行
    } else {
        [self hoge2];//そうでなければhoge2を実行
    }
}

使い方としてはこんな感じかな?

mp3などからcafへ変換する方法

iPhoneアプリを作るときに音声データを.caf形式に変換する必要がある。

ターミナルを使うと簡単に出来ます。

afconvert -f caff -d ima4 

ターミナルを起動したあと、下記をコピペ、
afconvert -f caff -d ima4

その後ろに変換したいファイルをドラッグアンドドロップ

afconvert -f caff -d ima4 hoge.mp3

エンターを押すと変換したいファイルのディレクトリに変換された.cafファイルが作られる。

効果音などがBGMとかぶっても消えない。連打しても消えない方法?

ミュージックなどで音楽をかけながら使いたいアプリだったり、
同じ音を何度も鳴らしてかぶる場合に、重なって聞こえるようにする方法。
普通の方法だと音をかぶせると先に鳴らした音が消えるらしい。
appDelegateを使う。

MP3とかの音源だと出来なくて.cafという形式を使う。
ターミナルやitunesで変換できるらしいので変換して入れておく。
ストーリーボードでボタンを作成して接続しておく。

AVFoundationフレームワークを使うので入れる。

コードは

ViewController.h

#import <AVFoundation/AVFoundation.h>
@interface ViewController : UIViewController

<UIAlertViewDelegate, AVAudioPlayerDelegate>

@property (strong) NSMutableArray *souldplayers;//音を鳴らすためのプロパティ

@end

ViewController.m

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    
    //音を鳴らすための配列を作成
    self.souldplayers = [NSMutableArray arrayWithCapacity:0];
    
}


#pragma mark - ringing
//音を鳴らす
- (IBAction)ringing:(UIButton *)sender
{
    [self playHitSound];
}

- (void)playHitSound
{
    //アプリに組み込んだサウンドファイルのファイルパスを取得する
    NSString *soundPath;
    soundPath = [[NSBundle mainBundle] pathForResource:@"rin" ofType:@"caf"];
    //URLにする
    NSURL *fileURL = [NSURL fileURLWithPath:soundPath];
    //サウンドファイルを読み込む
    AVAudioPlayer *player;
    player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:NULL];
    
    //デリゲートを指定する
    [player setDelegate:self];
    //配列に入れる
    [self.souldplayers addObject:player];
    
    NSLog(@"souldplayers.count = %lu", (unsigned long)[self.souldplayers count]); //=> 0
    [player play];
}


//効果音の再生が完了した時の処理
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
    //インスタンスを削除する
    [self.souldplayers removeObject:player];
}

appDlegate.mにAVfoundationをインポート
applicationDidBecomeActiveにコード追記

#import "AppDelegate.h"
#import <AVFoundation/AVFoundation.h>

@implementation AppDelegate

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryAmbient error:NULL];
    [session setActive:YES error:NULL];

    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

@end

こんな感じで。
鳴るかな?

2016/04/20追記
参考サイト
【iOS】音楽を止めずに効果音を同時に再生するには|てくめも@ecoop.net
[iPhone] AVAudioplayer で音楽の再生 (Objective-C)
AVAudioPlayerを使用した曲の再生/停止/ボリューム調節サンプル – Dolice Lab
Objective-C:音楽(BGM)や効果音(SE)等のサウンド再生方法 | siro:chro

Objective-CでGameCenter

久々の更新。

1年ぶりにiPhoneアプリをもっかいやってみようと思い立った。
いつの間にやらxcodeにSWIFTなる言語が追加されていた。

おぶしーもまだサッパリ分からないのでSWIFTに乗り換えても良いかなと思ったが、
新しく本を買うのがもったいなかったのでしばらくはおぶしーで頑張ろう。

で、以前作ったアプリにGame Center機能を付けようと奮戦した。

まず、GameCenterを使うにはitunes connectでLeaderboardを設定しておかなければならない。
その辺は色々なサイトで紹介されているので割愛。

ただしここで重要なのは「参照名」ではなく「Leaderboard ID」ってとこだけ宜しく。

ランキング表示はこちらを参照させて頂いた。
【ios7版】GameCenterのランキング表示、スコア送信を実装する – Qiita

ログイン認証とランキン表示は上記サイトのコードそのままで問題なかった。
ログイン認証

/**
* 画面を読み込む際の処理
*/
- (void)viewDidLoad
{
[super viewDidLoad];
[self authenticateLocalPlayer];
}

/**
 * GameCenterにログインしているか確認処理
 * ログインしていなければログイン画面を表示
 */
- (void)authenticateLocalPlayer
{
    GKLocalPlayer* player = [GKLocalPlayer localPlayer];
    player.authenticateHandler = ^(UIViewController* ui, NSError* error )
    {
        if( nil != ui )
        {
            [self presentViewController:ui animated:YES completion:nil];
        }
    };
}

ついで、ランキング表示

/**
 * ランキングボタンタップ時の処理
 * リーダーボードを表示
 */
- (IBAction)showRanking:(id)sender {
    GKGameCenterViewController *gcView = [GKGameCenterViewController new];
    if (gcView != nil)
    {
        gcView.gameCenterDelegate = self;
        gcView.viewState = GKGameCenterViewControllerStateLeaderboards;
        [self presentViewController:gcView animated:YES completion:nil];
    }
}

/**
 * リーダーボードで完了タップ時の処理
 * 前の画面に戻る
 */
- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

ここまでは良かった。

しかしスコア送信だけうまく行かなった。
何度試しても「NO SCORE」と出るのです。

apple公式のGamecenterプログラミングガイドを読んでもよく判らん。
identifierってなんやねん。

そもそも、ランキングを呼び出すときはLeaderboard IDは要らないのもよくわからんかった。
あとで呼び出しは全体呼び出しかデフォルト設定ってことがわかった。

で、送信するときのidentifierが参照名かLeaderboard IDなのかわかりにくい、表記の仕方もわかりにくい。

そうして色々と調べていくとこちらのサイトでは
ios7からのGameCenter – Qiita

GKScore *scoreReporter=[[GKScore alloc] initWithLeaderboardIdentifier:@"カテゴリのID"];

となっている。
最後のLeaderboard IDのところが@””で囲まれている。
前後のコードで描き方も変わるんだろうが超が30個位つく初心者にはよくわからない。

しかたがないので色々なパターンを試して

    GKScore *scoreReporter=[[GKScore alloc] initWithLeaderboardIdentifier:@"Leaderboard ID"];
    scoreReporter.value = self.Score;
    NSArray *scorearray=[[NSArray alloc]initWithObjects:scoreReporter, nil];
    NSLog(@"点数送信!!!!!");
    [GKScore reportScores:scorearray withCompletionHandler:^(NSError *error)
     {
         if (error)
         {
             NSLog(@"書き込めませんでした/(^o^)\");
         }
     }];

とゆーコードを点数を確認したすぐ後に入れることで何とかちゃんと送信できるようになった。

あと、sandboxの話とかもあるけど関係あったのか無かったのかよく分からず。
これだけのために数日費やしたので忘れないように残しておく。