ラベル Objective-C の投稿を表示しています。 すべての投稿を表示
ラベル Objective-C の投稿を表示しています。 すべての投稿を表示

2014年6月12日木曜日

iOSアプリでSQLiteを簡単に使えるFMDB

iOSアプリでデータベースを使いたくなることがあります。
でも、Core Dataはなんだか取っ付きにくいですよね。
そこで、SQLiteを使ってみてはいかがでしょうか?
FMDBというライブラリを使えばiOSアプリでも簡単に使えます。


準備



1.ライブラリをダウンロード


https://github.com/ccgus/fmdb



2.ライブラリを追加


ライブラリの『fmdb』以下にあるファイルを全部Xcodeに追加する。



3.フレームワークの追加


『libsqlite3.0.dylib』を追加する。



使い方



初期処理


#import "FMDatabase.h"


//FMDB
FMDatabase *db;

//DBファイル名
NSString *DB_FILE = @"test_db.sqlite3";

//DBファイルのパスを取得
NSString *dbPath = nil;
NSArray *documentsPath = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);

//取得データ数を確認
if ([documentsPath count] >= 1) {
    
    //固定で0番目を取得
    dbPath = [documentsPath objectAtIndex:0];
    
    //パスの最後にファイル名を追加
    dbPath = [dbPath stringByAppendingPathComponent:DB_FILE];
} else {
    
    //エラー
    NSLog(@"database file not found.");
}

//DBファイルがDocument配下に存在するか判定
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dbPath]) {
    
    //存在しない場合、DBファイルをコピー(初回起動時のみ)
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *orgPath = [bundle bundlePath];
    orgPath = [orgPath stringByAppendingPathComponent:DB_FILE];
    
    //DBファイルをDocument配下へコピー
    NSError *error = nil;
    if (![fileManager copyItemAtPath:orgPath toPath:dbPath error:&error]) {
        
        //エラー
        NSLog(@"db file copy error. : %@ to %@.", orgPath, dbPath);
    }
}

//パスを設定して、データベースオープン
db = [FMDatabase databaseWithPath:dbPath];
[db open];



//処理が終わったら、データベースクローズ
[db close];




SELECT


//クエリ実行
NSString *query = @"SELECT item_id, word, date FROM items WHERE word = ? ";
FMResultSet *rs = [db executeQuery:query, @"test1"];

if ([db hadError]) {
    //エラー
    NSLog(@"db execute error");
}

//結果の取得(カラム名指定)
while ([rs next]) {
    
    //数値の取得
    int itemID = [rs intForColumn:@"item_id"];
    
    //文字列の取得
    NSString *word = [rs stringForColumn:@"word"];
    
    //日付の取得
    NSDate *date = [rs dateForColumn:@"date"];
}

//ResultSetのクローズ
[rs close];




INSERT


NSString *word = @"test1";
NSString *mean = @"テスト1";

//INSERT実行
NSString *sql = @"INSERT INTO items (word, mean) VALUES (?, ?) ";
[db executeUpdate:sql, word, mean];

if ([db hadError]) {
    //エラー
    NSLog(@"db insert error");
}



UPDATE


NSNumber *itemID = [NSNumber numberWithInt:12345];
NSDate *timeStamp = [NSDate date];

//UPDATE実行
NSString *sql = @"UPDATE items SET date = ? WHERE item_id = ? ";
[db executeUpdate:sql, timeStamp, itemID];

if ([db hadError]) {
    //エラー
    NSLog(@"db update error");
}



DELETE


NSNumber *itemID = [NSNumber numberWithInt:12345];

//DELETE実行
NSString *sql = @"DELETE FROM items WHERE item_id = ? ";
[db executeUpdate:sql, itemID];

if ([db hadError]) {
    //エラー
    NSLog(@"db delete error");
}



まとめ


RDBを使ったことがある人は簡単ですよね。
Core DataでDBを使うことを断念した人も、FMDBでSQLiteを検討してみてはいかがでしょうか。


せり上がってくる数字と演算子で計算式を作り消して行く
5つ正解でクリアー、全30ステージの知育系パズルゲーム


2014年6月5日木曜日

Objective-Cで0.1+0.2が0.3にならない?

新作アプリ『パズオペ』は計算式を作るパズルゲームです。


数字と演算子を組み合わせて、正しい計算式を作って行きます。

ところが、小数も計算できるようにしていたら不具合が発生しました。
具体的には『0.1+0.2=0.3』とすると、なぜか不正解と判定されてしまうからです。


=================

double num1 = 0.1;
double num2 = 0.2;
double num3 = 0.3;
double ans = num1 + num2;

//比較
if (ans == num3) {
    NSLog(@"YES");
} else {
    //こちらが表示される
    NSLog(@"NO");
}

=================


これは私もすっかり頭から抜け落ちていたのですが、小数の値が二進数では表現できないことによる誤差が原因でした。

そもそも、プログラム内部では値を二進数で計算しています。
十進数の『0.1』は二進数だと『0.0001100110011…』となり循環小数となってしまいます。
演算するときはどこかで丸められてしまうので、十進数の値とは誤差が生じてしまうというわけです。

Objective-Cではそういう場合は『NSDecimalNumber』を使って対処します。

=================

NSDecimalNumber *decimal1 = [NSDecimalNumber decimalNumberWithString:@"0.1"];
NSDecimalNumber *decimal2 = [NSDecimalNumber decimalNumberWithString:@"0.2"];
NSDecimalNumber *decimal3 = [NSDecimalNumber decimalNumberWithString:@"0.3"];
NSDecimalNumber *decimalAns = [decimal1 decimalNumberByAdding:decimal2];

//比較
if ([decimalAns isEqualToNumber:decimal3]) {
    //ちゃんとこちらが表示される
    NSLog(@"YES!!");
} else {
    NSLog(@"NO!!");
}

=================

プログラムを書いていると、内部は二進数で…ということは忘れがちです。
これはどの言語でもある現象なので、覚えておくと良いでしょう。


せり上がってくる数字と演算子で計算式を作り消して行く
5つ正解でクリアー、全30ステージの知育系パズルゲーム


2014年5月14日水曜日

Xcodeで便利になるコメントの書き方

プログラムを書くときに、ちゃんとコメントを入れていますか?
一人で書く場合は、案外適当だったりしますよね。
そのコメントの入れ方で実はXcode上で便利になることを知りました。


普通はこんな感じでコメントを書くと思います。

スラッシュ2つ『//』のコメント

---------------------------------
//コメントテスト用メソッド
- (void) commentTest1 {
    
    //変数コメント
    int commentVal = 0;
}
---------------------------------


それをこのように変えてみてください。

『/**』と『*/』の間に書くコメント

---------------------------------
/** コメントテスト用メソッド */
- (void) commentTest1 {
    
    /** 変数コメント */
    int commentVal = 0;
}
---------------------------------


そして、その関数を補完候補で見てみると…

このように、コメントがメソッドの説明として表示されます。


同じように、変数でもこの通り。


このコメントの入れ方はドキュメント出力するとき用だと思っていました。
実はXcodeの補完候補でも使われているんですねえ。
今まで知りませんでした。

簡単で地味に便利なので、コメントを入れる時はこのやり方にしてはいかがですか?


アルファベットをタップして英単語を作る知育系アプリはいかがですか?
「しりとり」や「文字数制限」などのステージが48もありますよ。



2014年5月8日木曜日

レビューのお願いをするダイアログを出す方法

レビューを促すAppiraterの導入手順


App Storeでアプリをダウンロードするときに意外とレビューを気にする人は多いです。
でも、あんまりレビューって書いてくれなかったりしますよね。
そこで、iOSアプリのレビューをお願いするダイアログを出すライブラリ『Appirater』をご紹介します。



こういうモノはユーザーには結構ウザがられてしまいがちです。
ですので、ただ闇雲に出すのではなく、ある程度アプリを使ってくれた人にだけ依頼をした方が良いでしょう。
この『Appirater』はアプリの起動日数・回数を指定したり、ゲームクリアー時などの任意のタイミングで表示することができるので、その辺りを考慮した実装ができます。


1.ダウンロード

 githubから、Appiraterをダウンロードする。
https://github.com/arashpayan/appirater

2.プロジェクトに追加

 ダウンロードしたライブラリから以下のファイルをプロジェクトに追加。

  • Appirater.h
  • Appirater.m
  • AppiraterDelegate.h

ローカライズしたい言語のフォルダも追加。
※例えば英語、日本語の場合は以下のフォルダを追加。
  • en.lproj
  • ja.lproj

3.フレームワークの追加

 プロジェクトに以下のフレームワークを追加する。

  • CFNetwork.framework
  • SystemConfiguration.framework
  • StoreKit.framework ※
※StoreKitはRequired⇒Optionalに変更


4.文言のローカライズ

 ダイアログの文言を修正する場合は、ローカライズファイルを修正する。
(日本語の場合は、AppiraterLocalizable.strings(Japanese))

5.ARC設定

 プロジェクトがARCを利用しない場合、以下の設定をする。
「TARGETS」⇒「Build Phases」⇒「Compile Sources」
 ⇒Appirater.mに 「-fobjc-arc」を設定

6.設定値の修正

 AppDelegate.mのapplication:didFinishLaunchingWithOptionsメソッドにAppirater起動の設定を記述する。


[Appirater setAppId:@"000000000"]; // アプリのID
[Appirater setDaysUntilPrompt: 30]; // ダイアログを表示するまでの日数
[Appirater setUsesUntilPrompt: 20]; // ダイアログを表示するまでの起動回数

[Appirater setSignificantEventsUntilPrompt: -1]; 
// ある特定のタイミングでダイアログを表示したいときに使用
// 値 : -1=無効, 1=有効
// 表示する場合は、任意の箇所に
//[Appirater userDidSignificantEvent:YES] を記述

[Appirater setTimeBeforeReminding: 1];
//「後で見る」を選択したときに再びダイアログを表示するまでの日数

[Appirater setDebug: NO]; // デバッグモード(YESでアプリを起動するたびに表示)



7. 起動時のカウント

AppDelegate.mのapplicationWillEnterForegroundメソッドに、カウントする処理を記述する。

[Appirater appEnteredForeground:YES];


8.アプリ名

 実行時の画面に表示するアプリ名を変更する場合は、Appirater.hを修正する。

#define APPIRATER_APP_NAME を修正



9.Appiraterの起動

ダイアログを表示したい場面で、以下の処理を記述する。

[Appirater appLaunched:YES]; // Appiraterの起動



タップで計算式を作って暗算する知育系アプリです。
あなたの脳偏差値はいくつかな?




2014年4月2日水曜日

iOSアプリで簡単にインジケーターを表示する方法

iOSアプリでデータの少し時間のかかる処理(データの読み込みなど)をするときにインジケーターを表示したくなります。
ちなみにインジケーターとは以下のようなグルグルするヤツです。


標準でインジケーターを表示するやり方は以下のようになります。


UIActivityIndicatorViewを使用


==============================================

UIActivityIndicatorView *ai = [[UIActivityIndicatorView alloc] init];
ai.frame = CGRectMake(0, 0, 50, 50);
ai.center = self.view.center;
ai.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[self.view addSubview:ai];

[ai startAnimating];

==============================================

ただ、このやり方はいまいち使い勝手がよくありません。
そこで、標準以外のライブラリを使ってみました。



SVProgressHUDを使用


導入方法はとても簡単です。

ライブラリを以下よりダウンロード

ライブラリから以下のファイルをプロジェクトに追加
  • SVProgressHUD.h
  • SVProgressHUD.m

プロジェクトに以下のフレームワークを追加

  • QuartzCore.framework


これだけでOK、あとは以下のように書くだけ

==============================================

#import "SVProgressHUD.h"

//インジケーターを表示
[SVProgressHUD show];

//インジケーターを消す
[SVProgressHUD dismiss];

==============================================




ただ、このライブラリはiOS6だとなぜか落ちてしまったり、ARCではない環境では上手く動かなかったりして、私の環境では使えませんでした。


MBProgressHUDを使用


こちらも導入は簡単です。

ライブラリを以下よりダウンロード

ライブラリから以下のファイルをプロジェクトに追加
  • MBProgressHUD.h
  • MBProgressHUD.m

あとは以下のように書くだけ

==============================================

#import "MBProgressHUD.h"

//インジケーターを表示
[MBProgressHUD showHUDAddedTo:self.view animated:YES];

//インジケーターを消す
[MBProgressHUD hideHUDForView:self.view animated:YES];

==============================================


こちらのライブラリはiOS6でも非ARC環境でもOKでした。
また、以下のように背景の色を変えたり、テキストを表示できたり使い勝手もよさそうです。


==============================================
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];

//背景
hud.dimBackground = YES;

//テキスト
[hud setLabelText:@"Loading..."];
==============================================




まとめ


インジケーターを表示するなら『MBProgressHUD』をオススメします。







2014年3月28日金曜日

Objective-Cで簡単に遅延実行する方法

アプリを作っていると、処理を少し遅らせて実行したい場合があります。
Objective-Cでのやり方を備忘録として記載しておきます。


performSelectorを使用


『performSelector:withObject:afterDelay:』メソッドを使用すると簡単にできます。


=======================================

[self performSelector:@selector(hoge) withObject:nil afterDelay:1.0];

=======================================




ただ、このやり方は以下のような問題点があります

  • 並列処理では使えない
  • セレクターで呼ぶメソッドの引数を一つしか渡せない


これを解決するには、並列処理のときと同じようにGCDを使います。



GCDを使用


=======================================

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
    //遅延実行する処理
});

=======================================









2014年3月25日火曜日

Objective-Cで簡単に並列処理をする方法

プログラムで重い処理を実行すると、フリーズしたような状態になることがあります。
そういう場合、並列(非同期)処理をして、別スレッドに重い処理を持って行きます。
Objective-Cでのやり方を調べたので備忘録として記載しておきます。


NSOperationQueueを使用


NSOperationQueueを使用すると簡単にできます。


==========================

NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
    //重い処理
}];

==========================

ただ、このやり方だと次のような問題点があり、単純な処理しか行えません。

  • スレッドセーフでないオブジェクトを扱えない
  • スレッドの処理を終えた後の次の処理を行うのが簡単にできない



GCD(Grand Central Dispatch)の利用


GCDとはAPIを通して処理のブロックをキューに入れると、ランタイムの方で上手く並列実行してくれるという仕組みのことです。
このやり方だと、前の方法での問題点は解決できます。


==========================

dispatch_queue_t q_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t q_main   = dispatch_get_main_queue();
dispatch_async(q_global, ^{
    
    //重い処理
    
    dispatch_async(q_main, ^{
        
        //重い処理が終わった後の処理
        
    });
});

==========================



並列処理というと難しい印象がありますが、GCDを使えば簡単ですね。






2014年3月24日月曜日

Objective-Cで英字のみのデータを判別する方法

Objective-Cで英字のみのデータを判別する方法


調べても見つけづらかったので、備忘録として記載しておきます。


Objective-Cで正規表現


正規表現を使って以下のように記述する。

============================

NSError* err = nil;
NSRegularExpression* regex = nil;

// 検索する文字列
NSString* string = @"abc123";

// 正規表現オブジェクト作成(英字のみ)
regex = [NSRegularExpression
           regularExpressionWithPattern:@"([a-zA-Z]*)"
           options:NSRegularExpressionCaseInsensitive
           error:&err];

// 比較
NSTextCheckingResult *match = [regex firstMatchInString:string
                        options:0
                        range:NSMakeRange(0, string.length)];

if (match) {
    // マッチした時の処理
    NSRange matchRange = [match range]; // マッチした個数
    
    //マッチした個数と文字列の長さが同じ場合(全て英字)
    if (matchRange.length == string.length) {
        //英字のみの場合の処理
    }
}

============================





2014年3月15日土曜日

AdMobメディエーションで広告(Nend,iAd,AdMob)を切り替える方法

iPhone(iOS)アプリにAdMobメディエーションを実装し広告(Nend,iAd,Admob)を切り替える方法


iPhone(iOS)アプリには広告を表示することができます。
広告には様々な種類があり、国内だけのモノや海外でも使えるモノもあります。
これらの広告を切り替えて表示したいと思ったことありませんか?

AdMobメディエーションを使うと簡単にいくつかの広告を切り替えて表示することができます。
例としてAdMob,iAd,Nendを切り替えて表示する方法をご紹介します。


前提


この実装は2014/3/15時点の最新(ver6.8.0)、Xcode5.1でのものです。

事前準備


まず、Admob、Nendに申し込み、iTunes ConnectでiAdを有効にしておきます。
(iAdの有効化は実装後でもOKです)

AdMob


Nend


※API KeyとSpot IDを控えておく


メディエーションの設定


AdMobのサイトで設定

アプリを新規追加


新しいアプリを収益化


広告ユニットを追加


新しい広告ユニットを追加

広告ユニットIDを控えておく(実装時に必要)


広告ネットワークの設定


メディエーションを編集

新しい広告ネットワーク

iAdを追加

Nendを追加(API KeyとSpot IDを入力)

CPMの値で広告の優先順位を設定

優先順は国別の設定も可能
例えばNendは国内向けなので、日本以外は無効の設定にする


ダウンロード


AdmobメディエーションSDKのダウンロード



iAdアダプターのダウンロード



Nend SDKのダウンロード


Nendのサイトでダウンロード


実装


サンプルとして「Single View Application」へ実装します。
基本的には以下のGoogleのドキュメントのやり方で良いのですが、抜けがあったりするので注意してください。


ファイル追加


ダウンロードしたファイルをプロジェクトに追加
サンプルとしてこのようなディレクトリ階層にしてますが、以下のファイルが追加されていればどのような形でも問題ありません。

AdMob
  • GADAdMobExtras.h
  • GADAdNetworkExtras.h
  • GADAdSize.h
  • GADBannerView.h
  • GADBannerViewDelegate.h
  • GADInterstitial.h
  • GADInterstitialDelegate.h
  • GADRequest.h
  • GADRequestError.h
  • libGoogleAdMobAds.a

AdMob/Mediation
  • GADCustomEventBanner.h
  • GADCustomEventBannerDelegate.h
  • GADCustomEventExtras.h
  • GADCustomEventInterstitial.h
  • GADCustomEventInterstitialDelegate.h
  • GADCustomEventRequest.h

AdMob_iAdAdapter
  • libAdapterIAd.a

Nend
  • NADView.h
  • libAdapterNend.a
  • libNendAd.a

ライブラリ追加


TARGETS→General→Linked Frameworks and Libraries→+


以下のライブラリを追加
(ファイルを追加した時点で既に追加されているモノもあり)
  • AdSupport.framework ※
  • AudioToolbox.framework
  • AVFoundation.framework
  • CoreGraphics.framework
  • MessageUI.framework
  • StoreKit.framework
  • SystemConfiguration.framework
  • CoreTelephony.framework (サイトに書かれていない)
  • iAd.framework (iAdで必要)
  • Security.framework(Nendで必要)←抜けてました(4/21追記)

(以下は既に追加されているはず)
  • libGoogleAdMobAds.a
  • libAdapterIAd.a
  • libAdapterNend.a
  • libNendAd.a
※Deployment TargetがiOS6未満の場合はAdSupport.frameworkの設定をRequiredからOptionalに切り替える必要がある

Other Linker Flagsの追加


PROJECT→BuildSettings→Linking→Other Linker Flagsに「-ObjC」を追加


コーディング


ViewController.h
================================================

// SDK から GADBannerView の定義をインポートする
#import "GADBannerView.h"


@interface ViewController : UIViewController {
    // インスタンス変数として 1 つ宣言する
    GADBannerView *bannerView_;
}

================================================


ViewController.m
================================================

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //バナーユニットID
    NSString *MY_BANNER_UNIT_ID = @"ca-app-pub-1234567890123456/1234567890";
    
    // 画面上部に標準サイズのビューを作成する
    // 利用可能な広告サイズの定数値は GADAdSize.h で説明されている
    bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner];
    
    // 広告ユニット ID を指定する
    bannerView_.adUnitID = MY_BANNER_UNIT_ID;
    
    // ユーザーに広告を表示した場所に後で復元する UIViewController を
    // ランタイムに知らせてビュー階層に追加する
    bannerView_.rootViewController = self;
    [self.view addSubview:bannerView_];
    
    // 一般的なリクエストを行って広告を読み込む
    [bannerView_ loadRequest:[GADRequest request]];
}


================================================


実行


画面上部に広告が表示される

おわりに


事前準備の申し込みが面倒だったり、設定や実装が大変そうに見えますが、一度やってしまえば後はコピペでいけるので楽になります。

ある広告会社がトラブルで広告が配信されなくなっても、メディエーションで切り替えるようにしておけば他の会社の広告が表示されるので保険としても役立ちます。

特にこの広告でなければダメというこだわりが無いのなら、とりあえずAdMobメディエーションを組み込んでおくのが無難なのではないでしょうか。




『パズ銭』はハマり過ぎ注意な中毒者続出のパズルゲーム

パズ銭 ~スワイプで両替パズル