まいまいワークス

主にiPhoneアプリの開発で考えた事、調べた事、感じた事などを記していきます。

タスクスイッチャーで任意の画像を表示する

iOS7からタスクスイッチャーに入っているアプリは、バックグラウンドに遷移した時点の画面がサムネとして表示されていますが、アプリによってはここにあまり表示しない方がよかったり、別の情報を表示できた方がよかったりすることもありますよね。
そんな時はapplicationDidEnterBackgroundで画像を貼ってみましょう!


f:id:cccookie:20140514144434p:plain

実装方法は以下の通り。
ここでは画像(UIImageView)を貼付けていますが、ラベルでも何でもよさそうです。

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    
    UIViewController *blankViewController = [UIViewController new];
    
    //ここでは画像を貼付けていますが、任意のビューを貼ってください
    UIImage* blankImage = [UIImage imageNamed:@"blank.png"];
    UIImageView* blank = [[UIImageView alloc] initWithImage:blankImage];
    blank.frame = CGRectMake(0, 0, 320, 568);
    [blankViewController.view addSubview:blank];
    
    [self.window.rootViewController presentViewController:blankViewController animated:NO completion:NULL];

}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
    
    [self.window.rootViewController dismissViewControllerAnimated:NO completion:NO];

}

参考サイト:タスクスイッチャ−にアプリのスクリーンショットを表示させない | Cocoaの日々情報局

アプリ申請時のスクショはどこまで手を加えてもいいか問題の結論

f:id:cccookie:20140506012120p:plain

アプリ申請時のスクリーンショットは、ユーザーがインストールするか否かを判断する大きな判断材料になります(特に1枚目)。
なので、できるだけいいピクチャをアップしたいですし、できれば装飾なんかも加えて見栄えを良くしたり、よりアピールができるピクチャにしたいものです。

しかしながら、スクショに手を加えたらリジェクトされるんじゃないか?
という思いも少なからず出てくるだろうと思います。


結論としては、

  • スクショに少しくらい手を加えても怒られない。
  • やり過ぎはNG

こんな感じです。(普通の結論ですいませんw)

実際、スクショに手を加えてもリジェクトされる事はありません。
上記ピクチャは画面取りしたそのまんまの画像ではないですね。
ただ、スクショはアプリの内容を説明するものでなくてはならず、盛りすぎるとアプリの内容とかけ離れているとの理由でリジェクトされる事があります(リジェクト実績有り) 。


リジェクトに関しては時期的なものや、担当者による揺らぎ、運*1などに左右されますが、リジェクト理由となった項目は再申請時も必ずチェックされるので、この場合は素直にAppleの方針に従ってくださいませ!

*1:全てのビュー/機能をチェックするわけじゃないので、本来はNGでもたまたま通ってしまうケースもあります。

レビューと言えないような酷い書き込みは削除できるらしい

f:id:cccookie:20140502142847p:plain


App Storeに投稿された不快なレビューを削除する方法


一時期、日本のApp Storeの一部のアプリのレビュー欄で誹謗中傷とか、的外れな批判が酷いという話を聞いた事があるのですが、 レビューと言えないような不快な書き込みは消す事もできるようです。

MacからiTunesを開き、不快なレビューの所にある「問題を報告する」をクリック。 選択肢の中から「問題点」を選択し、コメントを書き込んで「送信」すれば完了のようです。

とはいえ、真っ当な意見の場合は、評価が低くても真摯に受け取りアプリの改善に努めたいですね。

ホームスクリーンに表示されるタイトルもASOの対象?

f:id:cccookie:20140501212505j:plain


BundleDisplayNameを使ったASO対策がローカライズ対応に凄く使える


ASOといえば、

  • アプリタイトル
  • 説明文
  • keyword
  • レビュー評価
  • レビュー文言

などが重要かなと思っていたのですが、タイトル欄に入りきらない文言も検索対象になっているとの事。
個人的にはタイトルが…で省略されるのはちょっといやなんですけど、keywordをガッツリ盛りたい場合は有効かも。

但し、やり過ぎると常にリジェクトのリスクがあります。 エンジニアが死ぬ気で詰めた日程を、このようなリジェクトでふいにしてしまうのは何とも悲しい事ですね。

プログラムの処理時間を計測する

処理時間計測の簡単な方法としてはNSLogを処理の始めと終わりに出力してその時間差を計算する方法がありますが、もう少し正確な計測をしてみたいと思います。

方法は以下の通り!

//これをインポートしておく
#import <sys/time.h>


//任意のメソッド内の記述
struct timeval start, stop;
gettimeofday(&start, NULL);

*** 実際の処理を行う記述 ***

gettimeofday(&stop, NULL);


double sec = stop.tv_sec - start.tv_sec;
double usec = stop.tv_usec-start.tv_usec;
double time = (stop.tv_sec*1000000+stop.tv_usec)-(start.tv_sec*1000000+start.tv_usec);
    
NSLog(@"sec=%f",sec);
NSLog(@"usec=%f",usec);
NSLog(@"time=%f",time);

tv_secで処理時間の秒の部分、tv_usecでマイクロ秒の部分を取得します。
トータルの処理時間は
double time = (stop.tv_sec*1000000+stop.tv_usec)-(start.tv_sec*1000000+start.tv_usec);
このような形で計算します。

[NSThread sleepForTimeInterval:0.5f];
このような形で処理の部分にwait timeを設定して挙動を確認してみてください。

意外と面倒!タイムラインで見られる1時間前、1日前といった文字列を生成する

関連記事  NSDateの罠 - まいまいワークス

サーバーから取得した時間情報を元に、現在の時刻から計算を行い、タイムラインで見られる1時間前、1日前といった文字列を生成します。
サーバーが日本時間を基準に値を返し、iPhoneで設定された時間帯がアメリカだったりすると時差の関係で表示がおかしくなったりしますが、ここも考慮していきます。
日付関連はハマりポイントが多いです!

ソースは以下の通り
サーバーが2014-04-25 12:34:56という値を返したと想定します。

    //サーバーがこの値を返したと仮定
    NSString* dateStr = @"2014-04-25 12:34:56";
    
    //時差補正用数値の指定
    float diff = [[NSTimeZone localTimeZone] secondsFromGMT];   //時差補正用
    float diffTokyoTimeZone = 9*60*60;  //サーバーがGMTの時間を送ってきていればここは0になる
    
    //フォーマット指定
    NSDateFormatter* format = [NSDateFormatter new];
    [format setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    //これで端末の設定に関わらずデータを取得する。
    [format setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
    
    //ロケーションに関わらずその瞬間のGMTを取得
    NSDate* now = [NSDate date];

    //sinceDate以降はロケーションによって値が時差分補正されたGMTになる (datestrは不変)
    //dateWithTimeInterval以降のオフセットはdiffとdiffTokyoTimeZoneで指定。
    //diff:GMTからの時差(秒)で補正。sinceDate以降の値の補正を相殺→ロケーションに関わらず一定のNSDate値を使用できる
    //diffTokyoTimeZone:stringで得られる時刻がJSTで得られる前提でロケーションに関わらず9時間戻す
    NSDate* date = [NSDate dateWithTimeInterval:diff-diffTokyoTimeZone sinceDate:[format dateFromString:datestr]];
    
    
    //経過時間の計算
    NSCalendar* cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents* comps = [NSDateComponents new];
    
    //秒を計算
    comps = [cal components:NSSecondCalendarUnit fromDate:date toDate:now options:0];
    NSUInteger sec = [comps second];
    
    //分を計算
    comps = [cal components:NSMinuteCalendarUnit fromDate:date toDate:now options:0];
    NSUInteger min = [comps minute];
    
    //時間を計算
    comps = [cal components:NSHourCalendarUnit fromDate:date toDate:now options:0];
    NSUInteger hour = [comps hour];
    
    //日数を計算
    comps = [cal components:NSDayCalendarUnit fromDate:date toDate:now options:0];
    NSUInteger day = [comps day];
    
    //週を計算
    comps = [cal components:NSWeekCalendarUnit fromDate:date toDate:now options:0];
    NSUInteger week = [comps week];
    
    //月を計算
    comps = [cal components:NSMonthCalendarUnit fromDate:date toDate:now options:0];
    NSUInteger month = [comps month];
    
    //年を計算
    comps = [cal components:NSYearCalendarUnit fromDate:date toDate:now options:0];
    NSUInteger year = [comps year];

sec,min,hour,day,week,month,yearにそれぞれの単位で計算された差分が入ります。
あとはこれを煮るなり焼くなりなのですが、今回は以下のようにしてみます。
差分が30秒未満ならたった今それ以外なら単純に〜時間前といった値を生成します。

    NSString* outStr;
    if (year != 0) {
        outStr = [NSString stringWithFormat:@"%ld年前",year];
    }else if (month != 0) {
        outStr = [NSString stringWithFormat:@"%ldヶ月前",month];
    }else if (week != 0){
        outStr = [NSString stringWithFormat:@"%ld週間前",week];
    }else if (day != 0){
        outStr = [NSString stringWithFormat:@"%ld日前",day];
    }else if (hour != 0){
        outStr = [NSString stringWithFormat:@"%ld時間前",hour];
    }else if (min != 0){
        outStr = [NSString stringWithFormat:@"%ld分前",min];
    }else if (sec != 0){
        if (sec < 30) {
            outStr = @"たった今";
        }else{
            outStr = [NSString stringWithFormat:@"%ld秒前",sec];
        }
    }else{
        outStr = @"error";
    }

Xcodeユニットテスト事始め

自作クラスに対して、修正が入るたびに手動でテストを行うのはあまりにも非効率なのでユニットテストの調査を行いました。
本エントリはその初歩の初歩。

テスト対象のクラスを作成

ここではテスト用の簡単なクラスを定義します。

#import <Foundation/Foundation.h>

@interface MyClass : NSObject


@property(nonatomic, assign)int a;
@property(nonatomic, assign)int b;
-(int)calcAdd;


@property(nonatomic, strong)NSString* aaa;
@property(nonatomic, strong)NSString* bbb;
-(NSString*)strfunc;

@end

ここではint型のaとbをcalcAddに食わせて結果を得るメソッド
NSString型のaaaとbbbをstrfuncに食わせて結果を得るメソッドの2つを定義します。

#import "MyClass.h"

@implementation MyClass

-(int)calcAdd{
    return _a+_b;
}

-(NSString*)strfunc{
    return [NSString stringWithFormat:@"%@%@",_aaa, _bbb];
}

@end

aとbを加算、aaaとbbbを接続するだけの簡単なクラスです。

テストの記述

xxxx.hファイルは不要、xxxx.mだけで良さそうです。
ここではnewTest.mファイルを作成します。
testではじまるメソッド名のメソッドがテストメソッドと見なされるようです。

#import <XCTest/XCTest.h>       //これはお約束
#import "MyClass.h"         //テスト対象のクラスをインポート

@interface newTest : XCTestCase

@end


@implementation newTest

-(void)testCalcAdd{
    MyClass* calc = [MyClass new];
    calc.a = 3;
    calc.b = 4;
    XCTAssertEqual([calc calcAdd], 7, @"エラー!");
}

-(void)testStr{
    MyClass* str = [MyClass new];
    str.aaa = @"abc";
    str.bbb = @"def";
    XCTAssertEqualObjects([str strfunc], @"abcdef", @"エラー!");
}

@end

各メソッドともにインスタンス化してプロパティ設定を行うところまでは通常のクラスの実行と同じです。
この後の行に書かれているのがテスト関数になります。
XCTAssertEqual(値1, 値2, @"メッセージ");
値1と値2を比較して、値1 == 値2となることが確認できればテスト成功。
失敗の場合はメッセージを出力します。

XCTAssertEqualObjects(object1, object2, @"メッセージ");
object1とobject2を比較して[object1 isEqual:object2]で等しい事を確認できればテスト成功。
失敗の場合はメッセージを出力します。

テスト実行

Product>Testもしくは⌘Uでテストを実行します。

詳細な情報

関数などの詳細な情報はXcodeユニットテストガイドをご一読ください。