2014年3月27日木曜日

[iOS][Objective-C]アプリでスクリーンショットを取得する


どうも。

一応の制限事項というか、
未解決の課題のあるコードです。

下記のコードでは、
statusBarが描画できない…。
今のところ解決する方法が見つかってないけど、
まぁステータスバーの内容って別にないならないでいいかな、
なんて思ってしまう自分もいたりw

ということで、
例のごとく、
utilのようなクラスにクラスメソッドとして定義するとして。

+ (UIImage *)getScreenShotImage {

    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
 
    UIGraphicsBeginImageContextWithOptions(window.bounds.size, NO, 0.0f);
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    // Windowの現在の表示内容を1つずつ描画。
    for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
     
        [window.layer renderInContext:context];
    }
 
    UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
 
    UIGraphicsEndImageContext();
 
    return capturedImage;
}

とりあえず取得したスクリーンショットをUIImage*で返しているが、
この辺りは実情に合わせて欲しい形で返したり、
カメラロールやアプリ内に保存する仕組みとかにしてもいい。

使い途としては、
デバッグ用に何かのトリガーでスクリーンショット取得しておくとか、
SLComposeViewControllerに渡してやってSNS連携とか。
MFMailComposeViewControllerに渡してやってメール送信とか。
例えばゲームの達成度をさくっと画像でシェアする、
なんて用途にもいいかもしれない。


それでは。
ちゃお☆


まこぴー。

2014年3月18日火曜日

[iOS][Objective-C]キーボードからの通知を監視する


どうも。

アプリ上で出現するキーボード。
ユーザの文字入力には必須ですが、
こいつが現れたり隠れたり、
予測変換で大きさが変わったりと、
結構厄介です。

画面の作り方次第なんですが、
標準のメッセージアプリなんかがいい例で、
メッセージ入力部はキーボードの表示とセットになって、
その位置が変わります。

入力部が持ち上がる時には、
その分他のViewも動かしたり、
高さを変更したりしなければならない。

上記を実現するためには、
キーボードの通知を監視し、
通知のコールバックで描画処理をしてやる。

下記のようなメソッドを用意して、
viewDidLoadあるいは、
viewDidLoadで初期化等をしているメソッド内で呼ぶ。
大事なのは通知をaddObserverしておくこと。

- (void)prepareReceiveKeyboardNotification {
 
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];
 
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
 
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardFrameWillChange:)
                                                 name:UIKeyboardWillChangeFrameNotification
                                               object:nil];
 
}

通知のコールバックメソッドをそれぞれ定義する。

- (void) keyboardWillShow:(NSNotification *)note;
- (void) keyboardWillHide:(NSNotification *)note;
- (void) keyboardFrameWillChange:(NSNotification *)note;

上記以外にもキーボード関連の通知があるハズだが、
上記だけで事足りることがほとんど。

それぞれ、パラメータの(NSNotification *)noteに、
キーボードの情報が入ってくる。
下記の様にしてキーボードのframeを取り出す。

    CGRect keyboardFrame;
    [[note.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue: &keyboardFrame];

frameが取り出せたら、
あとはごにょごにょと各パーツの位置やサイズを調整してやればいい。
で、それらの調整については、
keyboardWillHideとkeyboardFrameWillChangeで行う。
keyboardWillShowは、
必要があれば何か前処理を行う機構として、
とりあえず定義しておく。
(少なくとも自分は画面調整の際に使っていない)


それでは。
ちゃお☆


まこぴー。




2014年3月13日木曜日

[iOS][Objective-C]Viewを意味ある単位にまとめる


どうも。

例えば、
標準のメッセージアプリやLINEなんかが、
分かりやすくいい例ですが。

メッセージのやりとりの履歴が表示されるエリアと、
入力のコントロールをするエリアとに、
大きく分かれている。

で、
それらの画面は静的な配置ではない。
入力を開始すると、
キーボードが下から競り上がってくるし、
入力文字の行数が増えてこれば、
入力のTextViewの高さが変わるようになっている。

そういった制御は、
現状では自動的にやってくれるワケでなく、
自力で実装して実現せねばならない。

入力のコントロールエリアなどは、
現状のメッセージアプリやLINEでは、
TextViewの横にうまく配置出来るだけしかないが、
コントロールが増えてくると、
TextViewの下に配置して二段にする、
といったようなレイアウトもありえる。

非常に極端な話ですが、
少なくとも、
・履歴表示エリア
・入力コントロールエリア
という区別が必要になる。

履歴表示エリアには現状ではUITableViewしか存在しないが、
その最下部に何かしらのエリアを追加するようなこともありえそう。
入力コントロールエリアは、
コントロールの増加に伴って拡張がありそう。

という読みをしたときに、
履歴表示エリアと入力コントロールエリアを、
それぞれ1つのUIViewの中に配置しておく。
すると、
キーボードが下から競り上がってきたとき、
2つのViewの座標やサイズを再設定してやればいい。

入力コントロール部が2段になった。
そうしたら、
入力コントロールエリアのViewの中で、
TextViewを含むエリア(高さが可変)とそうでないエリアで、
Viewを分けて配置する。

◎mainView
    ○ナビゲーションバー
    ○ 履歴表示エリアView
    ○ 入力コントロールエリアView
           ・ (上段)入力エリアView
           ・ (下段)コントロールView

上記例は非常に単純な例であるが、
まずは、
「画面仕様からViewのヒエラルキーを整理して捉える」
というクセが重要。

実感として、
スマホアプリの開発では、
要求や仕様が後から変わってくることが結構ある。
その時の変更インパクトを押さえることも出来る。

あと、
個人的に挙げるメリットとして、
デバッグ時にViewに目立つ色をつけて、
意図した通りに可動できているかどうか?
というチェックもしやすくなるw
初期段階ではframeをログ出しするよりも、
その方がよっぽど分かりやすい。


それでは。
ちゃお☆


まこぴー。

2014年3月7日金曜日

[iOS][Objective-C]プルリフレッシュ - UIRefreshControl


どうも。

SNSのタイムラインなんかではおなじみの、
下に引っ張って情報を更新するUI。
今まで、
これを実現しようとすると、
UITableViewのヘッダにViewを追加して、
スクロールを監視して、
なんならデザイン部品も必要で…。
と、
実装がめんどくさかったような気がします。

今では、
UIRefreshControl
というのがあり、
上記のようなことをしなくても、
実現出来る。

viewDidLoad内で、
    _refreshControl = [[UIRefreshControl alloc] init];

    [_refreshControl addTarget:self
                                  action:@selector(refresh)
                   forControlEvents:UIControlEventValueChanged];

    [_tableView addSubview:_refreshControl];

みたいにすれば、
tableViewにrefreshControlが仕込まれる。

デリゲートメソッドがなく、
自力で更新の開始と終了の処理を書いてやって、
beginRefreshing
endRegreshing
を呼んでやらないといけません。

上記の例だと、
refreshというメソッドを定義してやって、
その中でbeginRefreshingを呼ぶことになる。

endRefreshingは、
tableViewをreloadする辺りで呼んでやることになろうかと。

とまぁ、
このエントリを書いている現在はiOS7ですが、
iOS6からある仕組みらしい…。

業務で必要になるケースがなくても、
定期的にOSのアップデートがあった場合は、
リファレンス確認して、
「こういうことができるようになった」
とか確認しておく必要があるなと、
今更ながらに実感w

今回は、
たまたまリファレンスを色々確認してみる調査が発生して、
こんなのがあったと、発見。

んで、
新規開発の場合には、
そろそろiOS6以上をターゲットにしてもいいかな、
と思ったりもするので、
どんどん使おうかな。

ともあれ、
この仕組みの実装が云々よりも、
プルリフレッシュという仕組み自体を最初に思いついた人はすごいな、
なんていつもいつも思うワケです。


それでは。
ちゃお☆


まこぴー。

2014年3月6日木曜日

[iOS][Objective-C](AppName)-Prefix.pchについて


どうも。

Xcodeでプロジェクトを作成すると、
アプリ名-Prefix.pchという、
グローバルなヘッダが勝手にというか、
付随的に出来てて。

で、
このブログでも、
そこをたびたび話題にしてきました。
↓↓↓↓↓
http://synonym-of-raspberry.blogspot.jp/2014/01/iosobjective-cnslog.html
http://synonym-of-raspberry.blogspot.jp/2014/01/iosobjective-c_30.html

なぜそれらが成り立っているかというと、
アプリのグローバルなヘッダで、
明示せずともどこからでも参照出来るから。

ということは、
アプリ全般で使うutilのようなクラスや、
作ったカテゴリやら、
何かをまとめたヘッダのようなものは、
<AppName>-Prefix.pchにimportしちゃえばいい。

これで、
ソース上ヘッダを#importしている箇所が、
ずいぶんスッキリするハズ!

まぁ、
もっと早く気づけよという話ですがw

ということが分かったとして、
新しいアプリプロジェクトを作成した時点では、
utilのような存在はなくて、
後から切り出しては、
色んな人が色んな画面で#importするという、
抗いがたい流れもあるワケです。
しかも画面もコピーで作ってしまうから、
そのまんま色んな#importも意図せず引き継がれるw

これはソースコードというより、
クラス単位で考えてプロジェクトをどう整理するか?
みたいなことでもあるんだろうな…。

自分ひとりで完結するのなら、
・util
・xxxApp(xxxにはアプリ名の略称とか当てはめる)
みたいなクラスを予め用意しておいて、
utilには本当に汎用的なユーティリティを、
xxxAppにはそのアプリに閉じた世界のユーティリティーを、
という風に整理した方がいいな、
とか思ってたり。

そうやって整理することで、
utilの洗練度というか、
より汎用的に作ろうとする気がします。

ここまで書いてみて、
どうせ整理せずにドカドカと#importしちゃうくらいなら、
全部Prefix.pchに書いちゃえばいいんじゃないかと思ったりもするけど、
どうなんだろう?


それでは。
ちゃお☆


まこぴー。

2014年3月1日土曜日

[iOS][Objective-C]NSLogをマクロで使う(その2)


どうも。

以前に、

NSLogはリリース後にもOrganizer等から見ることができるので、
<AppName>-prefix.pchにマクロを記述しておき、
リリースビルド時には自動的に無効化されるようにしておきましょう。
という記事を書いたのですが。

↓↓↓↓↓
http://synonym-of-raspberry.blogspot.jp/2014/01/iosobjective-cnslog.html

内容自体は変わらないのですが、
各プロジェクトでの使い回しなんかを考えると、
ヘッダファイルに切り出して、
そのヘッダを<AppName>-prefix.pchで#importする方が、
有用な気がした。

ので、
そんなヘッダを作ってみる。

[DebugLog.h]

//
//  DebugLog.h
//


#ifndef __DEBUG_LOG_H__

#define __DEBUG_LOG_H__

// ↓↓↓↓↓ DEBUG用ログ出力定義 ↓↓↓↓↓ //
// ※本番用のアーカイブビルドの際には自動で無効化される
//
#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
//
//
// ↑↑↑↑↑ DEBUG用ログ出力定義 ↑↑↑↑↑ //

#endif  // #ifndef __DEBUG_LOG_H__

こういうのを、
組織で共有できると有意義ですが、
いかんせん会社にはiOSのエンジニアは自分しかいないし、
その自分は外に出っぱなしだし、
どうにもこうにも、にんともかんとも…。

まぁ、
NSString+Additionも含めて、
暇を見て個人でgitにでもアップしとこうかな。


それでは。
ちゃお☆


まこぴー。