2014年2月27日木曜日

[iOS][Objective-C]NSString+Addition


どうも。

コードスニペットとしては、
utilのようなクラスに書く前提の方が楽ですが、
実際の使用感としては、
カテゴリとして定義した方が直感的だし、
メソッド名としてもスッキリさせることができる。

ということで、
以前にスニペット的に覚え書きしておいた、
下記のトピックをカテゴリに定義しておく。

http://synonym-of-raspberry.blogspot.jp/2014/02/iosobjective-cnsstringlength.html
http://synonym-of-raspberry.blogspot.jp/2014/02/iosobjective-c_16.html

カテゴリ名は"Addition"とでもしておきます。
(まぁ、そんなネーミングにしたら手広そうってくらいのノリw)

[NSString+Addition.h]

//
//  NSString+Addition.h
//


#import <Foundation/Foundation.h>

@interface NSString (Addition)

- (NSUInteger)realLength;
- (BOOL)isWhiteSpaceOnly;


@end

[NSString+Addition.m]

//
//  NSString+Addition.m
//


#import "NSString+Addition.h"

@implementation NSString (Addition)


- (NSUInteger)realLength {
    
    NSUInteger count = 0;
    
    if (self == nil) {
        
        return count;
    }
    
    int length = [self length];
    
    for (int i = 0; i < length; i++) {
        
        unichar uchar = [self characterAtIndex:i];
        
        if (CFStringIsSurrogateHighCharacter(uchar)) {
            
            // サロゲートペア:上位サロゲート
            i++;
            ++count;
            
        } else if (CFStringIsSurrogateLowCharacter(uchar)) {
            
            // サロゲートペア:下位サロゲート
            // 無視(何もしない)
            
        } else {
            
            ++count;
        }
    }
    
    return count;
}


- (BOOL)isWhiteSpaceOnly {
    
    BOOL ret = NO;
    NSString *newText = @"";
    
    if (self == nil || [self isEqualToString:@""]) {
        
        ret = YES;
        
    } else {
        
        newText = [self stringByTrimmingCharactersInSet:
                   [NSCharacterSet whitespaceAndNewlineCharacterSet]];
        
        if ([newText length] == 0) {
            
            ret = YES;
        }
    }
    
    return ret;
}


@end

上記例の内容だと、
ソースファイルを取り込んで、
ヘッダをimportしてやって、
色んなプロジェクトでそのまま流用できます。
util的なノリです。

プロジェクト独自ではあるが、
そのプロジェクト内では色んなトコで使うようなものは、
また別のカテゴリにまとめるようにしておく。
色んなところで使えそうなものは、
NSString+Additionに追記していく。
という整理をすれば、
NSString+Additionを育てながら、
色んなプロジェクトで使い回していけるワケです。


それでは。
ちゃお☆


まこぴー。

2014年2月26日水曜日

[iOS][Objective-C]座標/サイズ構造体(CGRect, CGPoint, CGSize)のログ出力


どうも。

UIを扱う場合なんかには、
座標系の構造体、
CGRect, CGPoint, CGSizeを扱うことが多い。
で、
位置とかサイズが可変になってくると、
ログ出しして確認したいのだが…。

CGRectを例にとってみると。

NSLog(@"xxxView.frame:%f, %f, %f, %f",
          xxxView.frame.origin.x,
          xxxView.frame.origin.y,
          xxxView.frame.size.width,
          xxxView.frame.size.height);

なんてやってたら、
面倒で仕方ない。

そんなとき、
NSStringFromCGRectというマクロがあって、
それを使うと…。

NSLog(@"xxxView.frame:%@", NSStringFromCGRect(xxxView.frame));

非常に簡潔に書ける!
こうすると、
xxxView.frame:{{0, 20}, {320, 548}}
といったログがコンソールに出る。
これは、
{{x, y}, {width, height}}
の形式。

CGPointやCGSizeも同じく、
NSStringFromCGPoint
NSStringFromCGSize
といったマクロが用意されている。

もっと出力を分かりやすくするなら、
CGRectCreateDictionaryRepresentationを使って、

NSLog(@"xxxView.frame:%@", CGRectCreateDictionaryRepresentation(xxxView.frame));

なんて書くと、

xxxView.frame:{
    Height = 548
    Width = 320;
    X = 0;
    Y = 20;
}
と、
ログが出ます。
heightとかWidthとか出てくれるんで一見分かりやすいですが…。
CGRectMakeするときなんかもそうなんですが、
一般にX, Y, Width, Heightの順番で捉えているので、
違和感はありますね。

こちらも同じく、
CGPointやCGSizeには、
CGPointCreateDictionaryRepresentation
CGSizeCreateDictionaryRepresentation
が用意されています。

個人的には、
上記のような座標やサイズのログはデバッグ用の使い捨てなので、
NSStringFromCGRectなんかでサクッとやっちゃうのが楽かと☆
小数できちんと確認したいなら自前で書くしかないですが、
そういうシーンにはほとんど出くわしたことない。

ちなみに、
NSStringFromXXXといったマクロには、

NSStringFromCGAffineTransform
NSStringFromCGPoint
NSStringFromCGRect
NSStringFromCGSize
NSStringFromClass
NSStringFromProtocol
NSStringFromRange
NSStringFromSelector
NSStringFromUIEdgeInsets

が用意されていて、
XXXFromStringで逆変換が出来ます。
使ったこともないですがw

ClassやProtocol, Selectorは、
変換と逆変換を巧みに使うことで、
リフレクションみたいなことが実現出来るかもしれませんが。


それでは。
ちゃお☆


まこぴー。

[iOS][Objective-C]enumの注意点


どうも。

enum自体は、
C言語から脈々と受け継がれている仕組みで、
細かく語る必要もないんですが…。

のハズが、
軽くハマった((((;゚Д゚)))))))

何でハマったかというと、
「値を指定しない場合は直前の列挙からのインクリメントとなる」
という、
至極当たり前のことw

enumというのは、
値を指定せずに、
シーケンシャルに列挙することができるワケで、
その特性を活かしているときは構わないのですが、
defineの集合体のように、
値を定義しまくって使うこともできる。

とある仕組みで、
モードをenumで列挙して定義しておいたんですが。
これは正の数で、
シーケンシャルな定義。
ただし、
0はモードなしという定義、
最終の列挙は終端という定義をする。

モードが、
0 < x < 終端
であれば、
モードに応じた処理を実行する。

という仕掛けにしておいた。

で、
これを参考に、
同じようなもので、
エラー処理をハンドリングするようなものを作ってね。
とお願いしたところ…。

エラー処理なので、
「モードはマイナス値の定義であるべき」
という一般的なポリシーは守ってくれたんですが。

飛び番号のマイナス値をenumで、
列挙が進むにつれて、
値が小さくなっていくように書かれていた。
で、
エラー定義上最小の値の次に、
終端を値の指定なしに列挙した。

そうすると、
エラー定義上最小の値と終端の値がかぶってしまうため、
モードが有効範囲内にあるか?
という判断をした時に、
エラー定義上最小の値は無効なモードとして認識されてしまう。
といったことがあって、
「同じように作ったのに、どうしても意図した通りに動きません」
という報告を受けて、
結局自分が手直しするハメになりました…。

実際には、
enumの列挙的な問題以前に、
モードが有効か無効かの条件文自体が、
まず間違っていましたが(´・ω・`)


それでは。
ちゃお☆


まこぴー。

2014年2月24日月曜日

[iOS][Objective-C]iOS上でのintとlongの最大値は同じ



どうも。

iOS上でのlongは32bitしか表現できないので、
事実上intと同じです。

INT_MAX:      2147483647
LONG_MAX:  2147483647

となります。
一般的に言うlongの範囲を使いたいなら、
long long
を使わなければなりません。

これは、
処理系が32bitだからということだと思いますが、
少なくとも32bitと64bitの処理系が混在している間には、
注意が必要。

サーバ連携なんかすると、
long値も受け取らなければならない可能性があるので、
そんな場合には、
iOSアプリ側ではlong longで受け取らなければならない。

まぁ、
64bitの端末以外なくなれば、
long longではなく、
普通にlongがlongとして使えるかもしれませんが。


それでは。
ちゃお☆


まこぴー。

2014年2月21日金曜日

[iOS][Objective-C]数値を3桁カンマ区切りのフォーマットにする


どうも。

数値をカンマ区切りで出力する。
というのもちょいちょいありそうなこと。

例えば、
123456789を、
123,456,789と表示してやる。

まぁ、
理屈は分かっていて、
後ろから3桁に区切りながらカンマを挿入してやればいいですが。
こういうのは理屈が分かっていても、
実際に実装するのはめんどくさいw

で、
便利なのがあるんですわ。

NSNumber *number = [NSNumber numberWithInt:123456789];

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setPositiveFormat:@",###"];

NSString *numberStr = [formatter stringForObjectValue:number];

NSLog(@"%@", numberStr);

などと書くと、
コンソールログには、
123,456,789と表示される。

こっちでもいい。

NSNumber *number = [NSNumber numberWithInt:123456789];

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];

NSString *numberStr = [formatter stringFromNumber:number];

NSLog(@"%@", numberStr);

やりやすい方を使えばいいけど、
見た目に理解しやすいのは後者かな。

日付表示のNSDateFormatterなんてのもあって、
日付も出力形式がシーンによって違ったりするので、
併せて頭の片隅に置いといて、
数値や日付の表示に関してはFormatterを使えば楽。

どういう設定にしたらどうフォーマットされるか?
といったようなことは、
Appleのリファレンスや書籍やネット上の情報で調べがつきます。

本エントリに限ったことではないが、
このブログでの覚え書きというのは、
自分自身が分かればいい尺度だったり、
自分自身の考え方の整理として書いているので、
懇切丁寧なリファレンスではありませんw

極端に言えば、
調べるためのキーワードやスニペットの集合体でしかありません。


それでは。
ちゃお☆


まこぴー。

2014年2月16日日曜日

[iOS][Objective-C]文字列が空白文字のみかどうかの判定


どうも。

ユーザが何かしらの入力をした時に、
空白や改行のみの文字列を許容したくない。

というようなことも、
時々あるので、
やはりutilのようなクラスに持っておきたい。

+ (BOOL)isTextWhiteSpaceOnly:(NSString *)text {
 
    BOOL ret = NO;
 
    if (text == nil || [text isEqualToString:@""]) {
     
        ret = YES;
     
    } else {
     
        NSString *newText = [text stringByTrimmingCharactersInSet:
                   [NSCharacterSet whitespaceAndNewlineCharacterSet]];
     
        if ([newText length] == 0) {
         
            ret = YES;
        }
    }
 
    return ret;
}

仕組みは単純で、
空白文字をトリミングしていって、
文字列長が0なら、
空白しかないという判断です。


それでは。
ちゃお☆


まこぴー。

2014年2月15日土曜日

[iOS][Objective-C]uncaught exceptionで異常終了時のログを取得する


どうも。

uncaught exceptionで異常終了した際に、
その内容が確認出来ると便利。

まぁ、
Xcodeにつないで実機動作中であれば、
コンソールに多少の情報が出て、
それだけでも解決に至ることも多いですが。

ファイルに出しておけば、
XcodeでRunしていなくても取り出して確認できるし、
NSUserDefaultに保存しておけば、
次回起動時に異常終了があったことも検知することもできる。

実現するには、
NSSetUncaughtExceptionHandler関数にハンドラ関数のアドレスを渡す」

これがちょっと面倒というか、
C言語的なんだけど。

ハンドラ関数を作成して、
そのアドレスを渡してやることで、
例外発生時の動作を定義出来る。

とりあえず、
ファイル保存をするとして。

AppDelegate内に、
下記のような関数を定義しておく。

※関数のプロトタイプも必要です!
(AppDelegateの頭の方にでも書いておけばOK)

// 例外発生時の情報を出力
void uncaughtExceptionHandler(NSException *exception)
{
 
    NSString *exceptionOccur = @"\n***** Uncaught exception occured!*****";
    // 例外の名前
    NSString *exceptionName = [NSString stringWithFormat:@"exception name:%@",
                                                  [exception name]];
    // 例外の理由
    NSString *exceptionReason = [NSString stringWithFormat:@"exception reason:%@",
                                                     [exception reason]];
    // 例外のコールスタック
    NSString *exceptionCallStack = [NSString stringWithFormat:@"exception callStackSymbols:\n%@\n",
                                                       [exception callStackSymbols]];
 
    // ログファイルに書き出す内容
    NSString *exceptionLog = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n",
                              exceptionOccur, exceptionName, exceptionReason, exceptionCallStack];
 
 
    // デバッグコンソールにも出力
    NSLog(@"\n%@", exceptionLog);
 
 
    NSString *exceptionLogDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
                                                                     NSUserDomainMask, YES)
                                 objectAtIndex:0];
 
    NSString *exceptionLogPath = [exceptionLogDir
                                  stringByAppendingPathComponent:@"uncaughtException.log"];
 
    NSError *error = nil;
 
    // ファイルに書き込み
    [exceptionLog writeToFile:exceptionLogPath
                   atomically:NO
                     encoding:NSUTF8StringEncoding
                        error:&error];
 
    if (error) {
     
        LOG(@"Cannot write exception log file!!");
    }

        // もしNSUserDefaultsに保存したいならこんな感じ
        //[[NSUserDefaults standardUserDefaults] setValue:exceptionLog 
        //                                                                    forKey:@"exceptionLog"];
}

んで、
AppDelegate.mの、
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
の中で、

// エラー追跡用の機能を追加する。
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

と書いておけば、
やりたいことが実現出来る。

ログの内容や形式は、
必要に応じて替えればいいです。


それでは。
ちゃお☆


まこぴー。

[iOS][Objective-C]iOSのバージョン比較


どうも。

iOSのバージョンを知りたいことがある。

そんなときは、

NSString *iOSVer = [[UIDevice currentDevice] systemVersion];

とでもすれば、
NSString*で取得することが出来る。

ただし、
これを表示することなどないワケで、
取得した後、
バージョン比較なんかしたいケースの方が多い。

理想論から言えば、
バージョンに依存しないコードが望ましいワケだが、
例えばiOS6からiOS7のように、
大きな変化があった場合には、
バージョン比較による分岐をある程度は余儀なくされる。

例えばiOS7の判定をする場合に、
Appleが推奨してる的な方法だと、

    if (floor(NSFoundationVersionNumber)
        <= NSFoundationVersionNumber_iOS_6_1) {
 
  // iOS6.1以前の場合
     
    } else {
     
        // iOS7(以降)の場合
    }

というようなやり方だが、
NSFoundationVersionNumberの定義は、
直前のバージョンまでしか対応していない。
つまり、今現在でいうと、
iOS7の定義は存在しないから、
iOS7であることの判定をするために、
iOS6.1以前でないことを確認せざるを得ないし、
条件文自体がいまいち直感的でない気がする。

ということで、
自分で分かりやすい方法に。

バージョン分岐はしたくないが、
バージョン分岐をせざるを得なくなることも考慮に入れるなら、
utilのようなクラスに持っておいてもいい。

で、
過去の経験からいくと、
iOSのバージョン比較をしたい場合、
メジャーバージョンだけ分かればいいことが多い。
というのと、
NSString*では比較がめんどくさいので、
数値で取得出来るようにする。


#define IOS_VER_NOT_AVAILABLE -1

+ (int)iOSMajorVersion {
 
    NSString *iOSVer = [[UIDevice currentDevice] systemVersion];
 
    if (iOSVer == nil || [iOSVer isEqualToString:@""]) {
     
        return IOS_VER_NOT_AVAILABLE;
    }
 
    NSArray *verArray = [iOSVer componentsSeparatedByString:@"."];
 
    return [[verArray objectAtIndex:0] intValue];
}

これで、
6や7みたいな数値と直感的に比較ができる。

マイナー、マイクロまで比較したい場合には、
もっと色んな考慮が必要で、
ひとつのクラスにして、
そのクラスのメンバに比較メソッドを仕込む方が汎用的になる。
地味だけど面倒なので、
必要になったら作ればいいんじゃないかとw

メイントピックと外れますが、

アプリ自体のバージョンを管理したいなら、
info.plistのBundle Versionに設定しておいて、

NSString *appVersion = [[NSBundle mainBundle]
                                         objectForInfoDictionaryKey:@"CFBundleVersion"];

とすれば、
NSString*で取れるので、
あとはどのようにして使いたい形に持っていくか。
まぁ、
自分の経験に照らせば、
アプリのバージョンは情報として表示することの方が多いので、
NSString*で取れればそれで構わない。


それでは。
ちゃお☆


まこぴー。

2014年2月11日火曜日

[iOS][Objective-C]NSDictionaryの中身を確認する(description)


どうも。

NSDictionaryに限らずですが、
中身をログ出しして確認したいことは多々ある。

NSDictionary *dic;
というdictionaryがあるとして、
[dic description];
なんて書くと、
文字列で中身が返ってくるので、
NSLog(@"%@", [dic description]);
と書いておけば、
コンソールで確認が可能。
keyとvalueで組み合わせて出してくれる。
わざわざループしなくてもいいから楽。

- (NSString *)descrition;
は、
NSObjectのメソッドなので、
NSArrayなんかでも使える。

arrayがNSString*やNSNumber*なら、
そのまんま格納値が出力されるが、
独自のクラスを使った場合はそうはいかない。

MyClassというクラスがあるとして、
メンバに、
int id;
NSString *name;
とか定義されているとして。

NSArray *array;
というarrayがMyClassのArrayである場合、
[array description];
なんてしても、
idやnameを出してくれるワケではなく、
MyClassクラスのオブジェクトであることと、
恐らくそのアドレスであろうHexの値が確認出来るだけである。
状況によりけりではあるが、
その出力が有用ではない場合の方が多い。

大抵の場合、
独自のクラスもNSObjectを継承しているので、
- (NSString *)description;
もオーバーライドできる。

独自クラスでdescriptionをオーバーライドしておくと、
上記例で、
[array description];
としたときに、
意味ある内容を出力することが出来る。

ただし、
メンバの多いクラスだと、
何でもかんでも出すとごちゃごちゃするので、
descriptionは簡易確認が出来ればいいレベルに抑えておくような精査が必要かもしれない。


それでは。
ちゃお☆


まこぴー。

2014年2月6日木曜日

[iOS][Objective-C]NSStringのlengthは絵文字を正確にカウント出来ない


どうも。

おとしあな。

NSStringのlengthメソッドを使うと、
絵文字を含む文字列が正確にカウント出来ません(´・ω・`)

NSStringは内部的にはUTF-16で扱われているが、
lengthでカウントする時にサロゲートペアを考慮していないので、
3バイトや4バイトの文字は2文字でカウントされてしまう。

ということで、
正しく文字数をカウント出来るように、
自前で作成してみる。

上記を考慮するなら、
サロゲートペアを考慮すればいいワケで、
それには、
// 上位サロゲートであるかどうか
Boolean CFStringIsSurrogateHighCharacter(UniChar character);
// 下位サロゲートであるかどうか
Boolean CFStringIsSurrogateLowCharacter(UniChar character);
を使えばいいみたい。

まぁ、
いろんな方法があるが、
例によってutilクラスにクラスメソッドとして定義するとして。
(後述するが、不完全なコード)

+ (NSUInteger)realNSStringLength:(NSString *)text {
   
    NSUInteger count = 0;
   
    if (text == nil) {
       
        return count;
    }
   
    int length = [text length];
   
    for (int i = 0; i < length; i++) {
       
        unichar uchar = [text characterAtIndex:i];
       
        if (CFStringIsSurrogateHighCharacter(uchar)) {
           
            // サロゲートペア:上位サロゲート
            i++;
            ++count;
           
        } else if (CFStringIsSurrogateLowCharacter(uchar)) {
           
            // サロゲートペア:下位サロゲート
            // 無視(何もしない)
           
        } else {
           
            ++count;
        }
    }

    return count;
}

で、
上記を試してみると、
確かに絵文字が1文字としてカウントされるようになった。

しかし、
2文字としてカウントされる絵文字もある。
上記だけでは対応としては不完全なのか…。

んで、
リサーチする中で分かったことは、
絵文字の中でも、
・囲み数字
・国旗
については、
特別な判定をしなければならないこと。

それ以外にも、
いくつか2文字でカウントされてしまうものもある。

ここまでくると、
絵文字の正確な文字コードのマッピングから、
規則性や例外を見つけて、
条件に組み込んでやるしかない。

ただ、
個人的にそれがいいコードとは思えない。
(やる気もないw)

標準のTwitterアプリ(iOS7版)で、
同じような検証を試みたところ、
同等のロジックで文字数を算出しているらしく、
対応出来ない絵文字も同等のよう。

Twitterと同等だからよい、
という判断もよくないが、
それなりの数の絵文字に対応出来ているし、
多少は許容せざるを得ないのかな…。

ちなみに、
実際に今仕事で開発しているアプリでも、
問題が起きている。

文字数がMAX値を超えている場合には、
超えた分をカットしてやる処理があって。
lengthでカウントが正しくできない為、
ケースによってはカット後の末尾が文字化けすることがある。
で、化けるだけならまだしも、
それをC++で作っているミドルウェアに渡した後、
その文字列をC++のstringにUTF-8変換してbrigdeするところで、
クラッシュ((((;゚Д゚)))))))

どう解決するかは、
いくつか案があって、
・きちんと文字数をカウント出来るようにする
・プログラム的にカットするのではなく、
   ユーザが自分でBackSpaceするようにUI的に仕向ける。

カウントを上記のロジックである程度正確にしておいて、
文字数を「xxx / xxx」みたくリアルタイム表示し、
超えた時には特定の操作ができなくする。
というのが最も良い方法な気がします。
(これって、結局はTwitterアプリと一緒かw)



それでは。
ちゃお☆


まこぴー。

2014年2月4日火曜日

[iOS][Objective-C]NSURLConnectionでファイルDLする場合の注意点


どうも。

NSURLConnectionでファイルをダウンロードする場合、
URLを取得して、requestを発行した後、
いくつかのデリゲートメソッドを使用してハンドリングするが、
その際に注意点が発生する。

// ヘッダが返ってきた
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;

// ダウンロード中
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;

// ダウンロードエラー
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

// ダウンロード完了
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;

注意点(1) -----

レスポンスがOK(200)ではなく、
Forbidden(403)などでも、
ファイルとしてDLしてしまうケースがあるので、
didReceiveResponse内でステータスコードを取得しておいて、
didReceiveData内でステータスコードがOK(200)であるかどうか判断する必要がある。
(デリゲートメソッドをまたぐのが面倒…(´・ω・`))

ステータスコードを取得するには、
ヘッダに、
int resStatus;
などと定義しておき、
didReceiveResponse内で、

resStatus = ((NSHTTPURLResponse *)response).statusCode;

とすればよい。

注意点(2) -----

ファイルがDLできなくても、
connectionDidFinishLoadingが呼ばれてしまうことがある。
(具体的なケース等の詳細は不明…。)
念のため、
目当てのファイルがDLできたかどうか?
の確認をした方が確実。
確認方法は実情に合わせて。



それでは。
ちゃお☆


まこぴー。

2014年2月3日月曜日

[iOS][Objective-C]アプリケーションを終了させる


どうも。

これは基本的に禁じ手で、
Rejectされない保証はないです(´・ω・`)

一応、
「ユーザーに確認を取ってからならOK!」
という説もありますが…。

まぁ、
App Storeに申請するアプリでなければ、
そんなことを気にする必要はないです。

やり方は簡単で、
C言語のexitをコールするだけ。
引数には0でも与えとけばいいです。

ユーザーにアプリを一度終了する旨を、
UIAlertViewで確認を取ってやって、
[OK]ならexitしてしまえばいい。

今現在やってるのはトライアル開発というか、
少なくとも今のもの自体を商用化することはないので、
遠慮なく落としてやっていますがw

ただ、
exitを呼ぶと、
本当にストンと落ちます。

それではあまりに不自然なので、
何かしら終了処理をしているフリをするために、
「アプリケーションを終了しています...」
みたいな通知文言と、
UIActivityIndicatorViewを組み合わせて、
UIAlertViewを表示してやるとかすれば、
画面操作もできないし、
何かしら裏で処理してるっぽいw

上記の終了処理実行中の表示をした2秒後くらいに、
exitが遅延実行されるように仕掛けておけばいい。

で、
exitを使うべきかどうかですが、
App Storeにリリースするのであれば、
やるべきでないと思います。
代替えとしては、
何も操作できない全画面の説明表示かなんかして、
再起動手順を促してやるとか。

exitの件然りですが…。
公開されているAPIでできないことでも、
C言語の仕組みを使うことで実現出来る。
しかしながら、
そのこと自体、
Appleは快く思っていないフシがある。

C言語の仕組みで、
Reject対象になるものもあった気もするし、
今は大丈夫でも今後Reject対象になる可能性は否定出来ない。

なんて言いつつ、
諸般の事情で、
やらざるを得ないことも多々ありますが(´・ω・`)


それでは。
ちゃお☆


まこぴー。

2014年2月2日日曜日

[iOS][Objctive-C]iOS7でステータスバーのレイヤが変更


どうも。

仕事で使っているデバッグ実機ではなく、
自分の私物iPhoneを機種変更して、
iPhone5s+iOS7になりました。
(それまでiPhone4s+iOS5)

で、
私物なんで、
自分の好きなようにアプリを入れていいので、
過去に在籍したプロジェクトのアプリを入れてみたんだが、
見事にステータスバーに画面部品が被っていた…。

これは、
iOS7ではステータスバーはコンテンツとは完全に独立したレイヤとして扱うことになった。
というのが明らかな原因。

さすがにこれはもうどうしようもなくて、
きちんと画面サイズに応じて部品の位置を計算している場合には、
iOSバージョンに合わせて計算方法を変えるしかないし、
Interface Builderでガッチリ作っている場合には、
Storyboardやxibを分岐するしかない…。
どちらにせよ、
バージョンを意識した実装をせざるを得ない。

今のプロジェクトでは、
ためらいなく後者を選んだ。

色々と理由はあるが、
Retina4inchを前提として構わなかった為、
そもそも画面サイズに応じて配置を計算する作り方はしていないこと。
であれば、
コピーしてiOS7用に合わせる方が、
ソースコードに及ぼす影響が少ないし、
結局、
iOS7の標準の「設定」のアプリなんか見てると、
UITableViewCellのLabel表示位置とか、
ポリシーみたいなのが変わっている部分もあって、
そういった細やかなiOS7らしさに対応出来るよう、
nibを分岐させた方がいいと考えた。

ちなみに、
問題の過去アプリについては、
自分自身がそれを引き継いですぐのタスクが、
iPhone5対応(Retina4inch対応)だった。
この時は、
nibを分岐させずに、
異なる画面サイズに対応出来るように、
計算して部品を配置するようにした。

これも結局は常駐仕事で、
アプリにはこれ以上やることもなく、
予算も取れないからと、
契約終了で常駐を終えていて、
当然アフターサポートとかないので、
自分にはもう無関係。


それでは。
ちゃお☆


まこぴー。