2014年1月31日金曜日

[iOS][Objective-C]UIImagePickerControllerでサムネイルを取得する


どうも。

UIImagePickerControllerを使って、
フォトアルバムから画像を引っ張ってくる。
というのも頻出。
その時に、
画像の本体とは別に、
カメラロールで表示されてるサムネイルが欲しい、
ということもあったりする。

そんなときは、
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info;
の中で、

NSURL* refUrl = [info objectForKey:UIImagePickerControllerReferenceURL];
         
ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
         
[assetsLib assetForURL:refUrl resultBlock:^(ALAsset *asset) {

    // カメラロールで表示されているサムネイルを取得
    thumbnailImage = [[UIImage alloc] initWithCGImage:[asset thumbnail]];
             
} failureBlock:^(NSError *error) {

    // error handling
}];

なんてしてやれば、OK。

サムネイルでなく、普通に画像取得したい場合は、
上記のような面倒なことしなくても、

selectedImage = [info objectForKey:UIImagePickerControllerOriginalImage];

などとしてやれば一発。

thumbnailImageやselectedImageは、
UIImage* として、
画面のメンバ変数かなんかで定義してあるものとして。

諸般の事情から、
一定の範囲内にリサイズする。
という要件もセットだったりするが、
それについては下記のエントリを参照。
http://synonym-of-raspberry.blogspot.jp/2014/01/iosobjective-cuiimage.html



それでは。
ちゃお☆


まこぴー。

2014年1月30日木曜日

[iOS][Objective-C]NSDateの現在時刻をローカル時刻で得る


どうも

ちょろちょろっとググれば、
NSDateを使って現在時刻を取得するのは、

NSDate *now = [NSDate date];

と書けばよい。
ということが分かるが、
上記で取得出来るのはGMT(´・ω・`)
GMTとはグリニッジ標準時間。

GMTではなく、
ローカル時刻を取得したいことがほとんどだと思うので、
そんな場合にはこう。

NSDate *now = [NSDate dateWithTimeIntervalSinceNow:
                   [[NSTimeZone systemTimeZone] secondsFromGMT]];


まぁ、
日本時間はGMTとは9時間差なので、

NSDate *now = [NSDate dateWithTimeIntervalSinceNow:(60*60*9)];

とすることもできるけど…。


それでは。
ちゃお☆


まこぴー。

[iOS][Objective-C]グローバル変数のススメ


どうも。

グローバル変数を使う場合。
・xxx-Prefix.pchに定義する
・AppDelegateに定義する

個人的には、
xxx-Prefix.pchに書くのが、
手間が少なくて好き。

AppDelegateだと、
あまり直感的でないし、
使うのもちょっと手間(´・ω・`)

AppDelegateのプロパティにしといて、
使う時には、
いったんAppDelegateを取得して、
そいつのプロパティにアクセスするようにする。

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSString *str = [appDelegate getXXX];

みたいな感じ。

一方、
xxx-Prefix.pchは、
グローバルなヘッダなので、
変数を定義しておくだけで、
アプリのどこからでも参照出来る。

「グローバル変数を使うのはちょっと…」
などと言う人も多いですが。

iOSで作るアプリ程度の規模で、
そんな堅苦しく考える必要もないし、
そもそも、
必要以上に使わない方がいい、
みたいなセオリーは確かに正しいけど、
必要なら使えばいいと思う。

大体、
グローバル変数を過剰に白い目で見る人に限って、
スコープが理解出来てなくて、
画面ローカルな変数を妙な引き回し方してたり、
やたらとシングルトン(もどき)やexternとか使ってたりして、
そんなんだったらポリシーを決めてグローバル変数使った方が、
よっぽど整理されたコードになる気がする。

もったいないのは、
グローバル変数を使えば即解決する問題を、
「グローバル変数はアレだから…」
なんて迷ってる時間。
仕組みとして用意されているのだから、
必要なときは堂々と使えばいい。

もちろん…。
なんでもかんでもグローバル変数にすればいい、
というものでもないけど(´・ω・`)


それでは。
ちゃお☆


まこぴー。

2014年1月27日月曜日

[iOS][Objective-C]テキストの表示上の高さを得る


どうも。

数が可変なら、
表示内容のテキストの文字数も可変で、
それに応じた可変の高さにする。

なんてことも、
よくある話…(´・ω・`)

まぁ、
SNSのタイムライン的なヤツとか。

例のごとく、
utilのようなクラスに、
クラスメソッドとして用意するなら…。

+ (CGFloat)getTextHeight:(NSString *)text width:(CGFloat)width fontSize:(CGFloat)fSize {
 
    CGSize boundSize = CGSizeMake(width, CGFLOAT_MAX);
 
    CGSize textSize = [text sizeWithFont:[UIFont systemFontOfSize:fSize]
                       constrainedToSize:boundSize
                           lineBreakMode:NSLineBreakByWordWrapping];

    return textSize.height;
}

フォントサイズだけでなく、
UIFontも引数に取った方がより汎用的かも。

sizeWithFontは、
iOS7ではdeprecatedだという話。
でも、
自分が今開発してるのでは、
iOS7で問題なく動いてるんだよな…。
iOS6もターゲットにしてあるから、
互換性が保たれてるのかな?

sizeWithFontの代わりには、
sizeWithAttributesを使えばいいらしい。

// 〜iOS6
[string sizeWithFont:font];

// iOS7
[string sizeWithAttributes:@{NSFontAttributeName:font}];

そうなると、
上記のメソッドの中身も変わってくるんでしょうが…。
まぁ、
それは困ったその時にやるとしてw


それでは。
ちゃお☆


まこぴー。

2014年1月26日日曜日

[iOS][Objective-C]範囲を指定してランダム値を得る


どうも。

本格的に乱数を扱いたい場合には、
はじき出される乱数の偏りがどうとか、
色々気にしなきゃいけないこともあるかもだけど…。

ちょちょいと、
画面のモックアップなんか作ったときに、
固定値でないのは、
少しは気が利いてる(気がするw)

モノによっては、
あまり大きな数字が出たりすると不自然な場合もあるので、
一応範囲を指定出来るようにしてみる。

例えば、
utilのようなクラスに、
クラスメソッドとして用意するなら…。

+ (int)getRandomIntWithMin:(int)min Max:(int)max {

    return min + arc4random() % (max-min+1);
}



それでは。
ちゃお☆


まこぴー。

2014年1月25日土曜日

[iOS]申請時にInvalid Binary


どうも。

たまにはテクニカルトピックでない覚え書き。

iOSの開発で、
面倒なのが申請。

怖いのは、
「Reject」

審査の結果、
諸般の理由により、
突き返されるというアレ。

ただ、
Rejectは比較的理由が明快というか、
身に覚えがなくはないことが多い。
(あまりRejectされたことないですが)

手順をまとめても陳腐化が早いので、
細かなところは割愛。

様々な面倒をクリアして、
いよいよArchiveしたバイナリを、
Organizerからアップロードした直後、
iTunes Connect上で、
「Invalid Binary」
というステータスになることがある。

審査すらしてもらえないのである((((;゚Д゚)))))))

まぁ、
これも、
「iOS Invalid Binary」
とか、
キーワードぶち込んでググったら、
ある程度出てくる。

その、
ある程度出てくる情報の中に、
あまり出てこない事例の覚え書き。

下記を確認してみること。
・実際に開発しているマシンとArchiveするマシンが合っているかどうか?
・マシンが違う場合は、Xcodeのバージョンが合っているか?

自分がInvalid Binaryになったときは、
これが原因だった。
マシンの違い云々は本質の問題ではなく、
原因発見のひとつのアプローチでしかない。

本質的には、
申請用にArchiveするXcodeのバージョンが古く、
Latest iOSが、
開発用と申請用の2つの環境で異なってしまうことが問題。

この問題が起きた時、
iOSのバージョンアップがあったこともあって、
開発用のマシンのXcodeのバージョンをアップしていた。
これにより、
開発環境は最新iOSに対応になった。
一方、
申請用のマシンのXcodeはバージョンアップされておらず、
Latest iOSが、
開発した環境よりもひとつ前のバージョンになっていた。

これは、
プロジェクト常駐のエンジニアには、
要注意事項。

自分のマシンで開発し、
自分のマシンで申請する場合に、
こうした問題は発生しにくい。

実際に、
自分の場合には、
プロジェクト先の管理的な意味合いから、
開発用と申請用のマシンが分けられ、
申請用のマシンは、プロパーのPM管理のマシン。
申請する時にしか使わないため、
バージョンアップなどしていなかった。

プロジェクト先に常駐する場合、
開発環境のバージョンの固定化とか、
管理的視点から申請用のマシンを分けるなど、
意味不明な制約等があり、
上記の様なズレが生じる可能性がある。

ちなみに、
上記のInvalid Binaryの原因調査と対処で、
終電を逃して、
タクシーで午前3時帰りするハメになりました…。

ぶっちゃけ、
よそから引っ張って来てるエンジニアに、
申請とかやらせんなよ…。

別に、
やってもいいんだけど、
申請って、
常駐してるエンジニアには決められないことも出てくるし、
プロパー的な人がやった方がいいんだけど、
大してMacが使えなかったり、
申請の流れすらよく知らない人ばかりで、
いとも簡単に放棄する始末だったり、
そんなのがほとんどですw

手順書とか作ったって、
陳腐化が早いし、
MacBookでデスクトップ切り替えれないとか、
いわゆる右クリックをする操作も分からない人もいたりするので、
操作指導からせねばなりませんね…。


それでは。
ちゃお☆


まこぴー。

[iOS][Objective-C]viewDidLoadを整理する


どうも。

viewDidLoadに限った話ではないが…。
ライフサイクル系のメソッドの中で、
特にぐちゃぐちゃになりやすいのが、
viewDidLoad

Viewが初めて呼び出される時に1回だけ呼ばれるメソッド。
ここで色んな初期化をしたりするワケですが。

viewDidLoadにとりあえず書いて試して、
そのまんまそのコードが採用されてしまったりと、
とにかく汚くなりがちだし、
何をしているのか読みづらい(´・ω・`)

各画面でのエントリポイントだから、
ここが汚いと、
もう読む気なくなるw

で、
どこまで整理するかは、
例によって人それぞれ。

※人生いろいろ 男もいろいろ
 女だっていろいろ 咲き乱れるの
(※くり返し)


最低限、
・データの初期化
・画面部品の外観に関する初期化
くらいの整理はしてもいい気がする。

プロトコルをたくさん継承していて、
setDelegateなどの、
コンポーネントの使用準備のような処理が多くなるなら、
それも切り出していいと思う。

個人的な見解として、
ライフサイクル系のメソッド群や、
各コンポーネントのデリゲートメソッド群に関しては、
中身をキレイにしておいた方がいいという考え方があるのがひとつ。

それから、
とりあえずviewDidLoadに書いたデータの初期化処理を、
画面遷移の絡みなんかがあって、
実はviewWillAppearに移したいとかいうことも、
よくあることだったりする。
全部かもしれないし、
一部かもしれない。

サーバやミドルウェアと絡んでくると、
その使用手続きもあったりするかもしれないし、
とかく肥大化しがちなので…。


それでは。
ちゃお☆


まこぴー。

2014年1月24日金曜日

[iOS][Objective-C]UITableViewについて その2


どうも。

前に、
UITableViewについて書いて、
「コレクションを使ってハンドリングしようぜ(`・ω・´) 」
ってなことを書いたワケですが。
http://synonym-of-raspberry.blogspot.jp/2014/01/iosobjective-cuitableview.html

例えば、
商品一覧をTableViewに表示するとしたら、
・商品名
・価格
・在庫数
など、
複数のデータを表示することになる。

こういうのは、
Itemみたいな名前のクラスに、
ひとまとまりの情報として持っておきましょうとなり。

そうすると、
NSString *name;
int price;
int stock;

みたいなメンバで構成し、
それぞれのGetterやSetterがあって。

で、
TableViewの方に戻ると、
TableViewCellでそれらの情報を表示するときは、
TableViewCell内のUILabelになるワケで。
そうすると、
UILabelのsetTextは引数にNSString*が必要。

ので、
- (NSString *)getPriceStr
みたいなGetterを追加しといてやると、
表示部分のコードがすっきりするんじゃないかと。

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath内で、

Item *item = [itemList objectAtIndex:indexPath.row];
[cell.itemLabel setText:[item getPriceStr]];

と、
それなりにシンプルで読みやすくなりそう。

intの値をGetして変換して、
でもいいけど、
デリゲートメソッドの中がぐちゃぐちゃしちゃうから…。
xxxViewControllerの中に切り出すと、
xxxViewControllerが無用に肥大化するし、
別の画面でも必要なときは、
同じ処理を別のViewControllerにも書かなきゃいけないし。

この辺りは好みによってきたり、
スマートな方法はいくらでもあるし、
プロジェクトによって諸般の事情があったりするとは思いますが。

個人的な経験に照らしてみると、
画面のコード(xxxViewController)や、
UITableViewのデリゲートメソッド内を、
少しでもすっきりさせた方が、
コーディングやメンテナンスが楽になる。

ただでさえ、
UITableViewのデリゲートメソッド内は、
switch文もしくはif文での分岐を余儀なくされるし、
StoryboardのprototypeCellなんかを使えば、
セル内の構成部品の外観に関するコードも、
cellForRowAtIndexPathに書かなきゃならなくなったりと、
ごちゃごちゃしがち(´・ω・`)

後々のこと、
誰か他の人が手を入れる可能性まで考えれば、
なるべくきれいな形にしていた方がいいかな、
などと思ったりするが、
他の人はどうしてるのだろうか…?

ちなみに、
これを書いてるこの日、
強くこのことを再認識した…。

既に担当を外れたメンバーが書いた画面に、
仕様の変更が入って、
俺が手を入れなきゃいけなくなって…。
デリゲートメソッド内がぐちゃぐちゃ(´・ω・`)

トピック的には外れるけど…。
いわゆる変数のスコープとか、
メソッドから値をreturnすることが、
イマイチ分かっていない人だったので、
不必要に広いスコープに変数を持って、
その変数を散らばせて処理してて、
これも骨が折れたw

ともかく、
最終形がUITableViewになる場合は、
その下準備の段階から、
「整理」を意識しておかないと、
バグも検出しづらいし、
改修もしづらい。


それでは。
ちゃお☆


まこぴー。

2014年1月21日火曜日

[iOS][Objective-C]UITableViewについて


どうも。

いろんなiOSアプリがあると思いますが、
高確率で出くわさなきゃいけないコンポーネントが、
UITableView。

設定画面のような、
項目の一覧としての存在だったり、
検索結果の一覧のような存在だったり。

基本的かつ使用頻度が高い割に、
サンプル画面ひとつ作るのにも、
それなりの手間がかかったり…。

TableViewの各行の内容たる、
UITableViewCellというのも、
それなりのクセがあって面倒…。

ボタンやラベルを好きな位置に配置して、
とりあえずRunすれば、
「ラベルとボタンが表示されたよ☆」
というのとはワケが違う(´・ω・`)

最低限、
・セクション数
・各セクション内の行数
・各行に表示する内容
といった、
TableViewの構成要素を、
デリゲートメソッドやら何やら、
オーバーライドして書かなきゃいけない。

上記だけでも、
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
と、
見るだけでめまいが…

しかも、
TableViewを使う側からは、
どれも(感覚的に見れば)静的に設定している。

よくあるサンプルプログラムなら、
ベタ打ちの値でぶち込んで、
「とりあえず表示されてよかったね☆」
と、ちゃんちゃん(`・ω・´)

でいいかも知れませんが、
実際の開発では、
「表示される行数は可変です( *`ω´)」
ということがほとんど。
更に、
非同期でサーバからデータが送られてきたり…。

行き着く答えは、
表示の処理と表示データを切り離して、
NSMutableArrayの様なコレクションクラスで、
表示データをハンドリングするということ。

非同期でレスポンスがあったとして、
・Arrayの内容を更新
・TableViewをリロード(reloadData)

とすることで、
デリゲートメソッドが再び走るので、
行数だの内容だのがリフレッシュされる。

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
では、
[xxArray count]
で行数を返すようにしてやればいいし、

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
では、
[xxArray objectAtIndex:indexPath.row]
で内容を取り出してやればいい。

と、
ベタ打ちの値を出すにしても、
Arrayにぶち込んで、
それをハンドリングするようなクセをつけておいた方がよさそう。

よくあることとして、
まずは固定値でいいから画面イメージというか、
モックアップのようなものを作って、
fixしたらそれをベースに実際のものを作る。
といったような流れ。

この時に、
ベタベタと書くのではなく、
ある程度最終形を見据えておいて、
ベタ打ちだけど、
Arrayでハンドリングされている形だと、
後々のコーディングが非常に楽(`・ω・´)

他にもっとスマートな方法ってあんのかな?


それでは。
ちゃお☆


まこぴー。

2014年1月18日土曜日

[iOS][Objective-C]アスペクト比を保ってUIImageをリサイズ


どうも。

画像のリサイズというのも、
色んなシーンで必要になることが多い。

その場合、
「アスペクト比を保って、300×300の範囲内で」
のようなことがほとんど。

画像を扱うとなると、
複数回の登場も予想されるので、
例えば、
utilクラスにクラスメソッドとして実装するとすれば…。


// アスペクト比を保ってUIImageをリサイズ
+ (UIImage*)resizeAspectFitWithSize:(UIImage *)srcImg size:(CGSize)size {
 
    CGFloat widthRatio  = size.width  / srcImg.size.width;
    CGFloat heightRatio = size.height / srcImg.size.height;
    CGFloat ratio = (widthRatio < heightRatio) ? widthRatio : heightRatio;
 
    CGSize resizedSize = CGSizeMake(srcImg.size.width*ratio, srcImg.size.height*ratio);
 
    UIGraphicsBeginImageContext(resizedSize);
    [srcImg drawInRect:CGRectMake(0, 0, resizedSize.width, resizedSize.height)];
    UIImage* resizedImage = UIGraphicsGetImageFromCurrentImageContext();
 
    UIGraphicsEndImageContext();
 
    return resizedImage;
}

上記のコードにより、
リサイズして、
その画像を表示する場合、
注意が必要。

意図した通りにリサイズされていたとしても、
UIImageViewの設定次第では、
思ったように表示されない可能性があるので、
そちらを併せて確認する必要がある。

で、
これをJPGやPNGにしたい場合は、
標準で準備されている、
下記の関数を使用して、
NSData*として取得する。

NSData * UIImageJPEGRepresentation (
   UIImage *image,
   CGFloat compressionQuality
);

// ex
NSData *imgData = UIImageJPEGRepresentation(img, 0.8f);


NSData * UIImagePNGRepresentation (
   UIImage *image
);

// ex
NSData *imgData = UIImagePNGRepresentation(img);



それでは。
ちゃお☆


まこぴー。

2014年1月17日金曜日

[iOS][Objective-C]NSLogをマクロで使う


どうも。

NSLogでログ出力。

NSLogでの出力内容は、
リリース後のアプリからでも、
Organizerとかから、
実は見れる((((;゚Д゚)))))))

ということで、
<AppName>-prefix.pchに、
下記の様なマクロを記述しておき、
リリースビルド時には自動的に無効化されるようにしておく。

#ifdef DEBUG
    #define LOG(...) NSLog(__VA_ARGS__)
    #define LOG_PRINTF(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
    #define LOG_METHOD NSLog(@"%s", __func__)
    #define LOG_METHOD_AND_ABORT LOG_METHOD; abort()
#else
    #define LOG(...)
    #define LOG_PRINTF(FORMAT, ...)
    #define LOG_METHOD
    #define LOG_METHOD_AND_ABORT
#endif

#ifdef DEBUG
    #define LOG_POINT(p) NSLog(@"%f, %f", p.x, p.y)
    #define LOG_SIZE(p) NSLog(@"%f, %f", p.width, p.height)
    #define LOG_RECT(p) NSLog(@"%f, %f - %f, %f", p.origin.x, p.origin.y, p.size.width, p.size.height)
#else
    #define LOG_POINT(p)
    #define LOG_SIZE(p)
    #define LOG_RECT(p)
#endif


上記は一例で、
ググれば他にも色んなマクロが出てくるだろうし、
自分の好みにあった形で書いてもいいし。
個人的には上記で充足するかな。

自分一人で、
最初から最後までやるときはいいんだけど…。

複数人での開発だったり、
ある程度開発が進んでる状態で後発でアサインされたりとか。
色んな力関係から、
自分が全体に関わるところのコードを書いていいのかどうか分かんないときとか…。

なーんて、
遠慮してたら、
実はプロジェクトメンバの誰も、
知らなかっただけだったり(´・ω・`)


それでは。
ちゃお☆


まこぴー。

2014年1月16日木曜日

[iOS][Objective-C]シングルトンの実現(そこそこに)


どうも。

シングルトンをそこそこに実現する方法。

前提としては、
ARC (Automatic Reference Counting) を採用していること。
(だいたい、このブログでの覚え書きはARC採用してる前提)

Objective-Cという言語の特性上、
厳密で完全なシングルトンを実現するのは、
なかなか難しいので、
「そこそこにシングルトンのような構造」にしておく。

例えば、
aClassというクラスを、
シングルトンっぽく実装するなら…。

(aClass.m内)
static aClass *_instance;

+ (void)initialize {

    @synchronized(self) {

        if (_instance == nil) {
 
  _instance = [[aClass alloc] initInternal];
        }
    }
}

+ (aClass *)sharedInstance {

return _instance;
}

- (id)init {

    [self doesNotRecognizeSelector:_cmd];

    return nil;
}

- (id)initInternal {
 
    self = [super init];

    return self;
}

(aClass.h内)
- (id)init UNAVAILABLE_ATTRIBUTE;

デフォルトの初期化メソッドのinitが機能しないようにし、
代わりにinitInternalを定義する。
もちろん、
initInternalをコールすることは可能ではあるが、
「それはしないでね♡」
という約束事にしておく。


それでは。
ちゃお☆


まこぴー。

[iOS][Objective-C] hexのカラーコード文字列からUIColorを得る


どうも。

手始めの覚え書き。

デザイナーは色をhexで指定してくることが多い。
ので、
何らかの形で、
それを変換してやる必要があったりする(´・ω・`)

utilみたいなクラスに置いておくか、
UIColorのカテゴリにするとか、
汎用化する方法はいくつかあるけど、
とりあえずutilクラスにクラスメソッドとして実装するとすれば…。

// 16進の文字列からUIColorを得る
+ (UIColor *)colorWithHexString:(NSString *)hex alpha:(CGFloat)alpha {
    
    NSScanner *colorScanner = [NSScanner scannerWithString:hex];
    
    unsigned int color;
    
    if (![colorScanner scanHexInt:&color]) {
        
        return nil;
    }
    
    CGFloat R = ((color & 0xFF0000) >> 16) / 255.0f;
    CGFloat G = ((color & 0x00FF00) >> 8) / 255.0f;
    CGFloat B = (color & 0x0000FF) / 255.0f;
    
    return [UIColor colorWithRed:R green:G blue:B alpha:alpha];

}


http://sumihiro3.blog.fc2.com/blog-entry-13.html
のようなサービスもあるけど、
アプリを一本作っていく過程で、
毎度毎度変換をかましているのでは、
疲れるし、

[UIColor colorWithRed:0.9255f green:0.3059f blue:0.8353f alpha:1.0000f];

のようなコードが散在するのでは、
ソースのメンテナンス性も落ちる。

デザイナーとのI/Fという意味でも、
16進で指定できる方法を持っておく方がよさそう。



それでは。
ちゃお☆


まこぴー。