2014年4月17日木曜日

[iOS][Objective-C]StackとQueue(簡易実装)


どうも。

ずいぶんとご無沙汰しました…。
ごぶさたした理由はいろいろありますが、
書くと長いのでw

で、
今回のお題は、
StackとQueueについて。

Javaには、
java.util.Stackとかjava.util.Queueとかあって、
普通に使えるワケですが、
Objective-Cにはそんなのない…。

StackやQueueというのはデータ構造であって、
その仕組みが分かっていれば、
NSMutableArrayに対して、
相応の処理をしてやればいいだけ。

とは言え、
popとかpushと書いて、
イメージ通りに動く方が見た目にもスッキリなので、
予め実装しておく。

NSMutableArrayを使うのであれば、
カテゴリにしてしまうのが楽ですね☆

カテゴリ名は、
VirtualStack, VirtualQueueとでもしておきます。
Virtualなんてつけるほどすごいことでもないけどw

[NSMutableArray+VirtualStack.m]

#import "NSMutableArray+VirtualStack.h"

@implementation NSMutableArray (VirtualStack)

- (id)pop {

    id lastObj = [self lastObject];
 
    if (lastObj != nil) {
     
        [self removeLastObject];
    }
 
    return lastObj;
}

- (void)push:(id)obj {
 
    [self addObject:obj];
}

@end

[NSMutableArray+VirtualQueue.m]

#import "NSMutableArray+VirtualQueue.h"

@implementation NSMutableArray (VirtualQueue)

- (id)dequeue {
 
    id headObj = [self objectAtIndex:0];
 
    if (headObj != nil) {
     
        [self removeObjectAtIndex:0];
    }
 
    return headObj;
}

- (void)enqueue:(id)obj {
 
    [self addObject:obj];
}

@end

と、
ここまで書いておいて…。
これって、
StackもQueueも両方あったら、
Stackとしてpush-popしてきたものに対して、
いきなりdequeueとか出来ちゃったりするw

それぞれ、
NSMutableArrayをメンバーに持つ、
別なクラスに切り出した方がいいのかもしれない。
複数人開発や、
マルチスレッドでのアクセスがありうるとか、
そういうリスク管理の観点では、
クラスとしてきちんと区別した方がいい。

あくまで、
簡易的に実現するなら、
という方法ですね、これは。


それでは。
ちゃお☆


まこぴー。

2014年4月3日木曜日

[iOS][Objective-C]UIの表示位置やサイズを変更する


どうも。

実際の業務となると、
UIの表示位置やサイズを変更することが、
ちょいちょいとあります。

aViewというUIView*があって、
Y位置を100, 高さを100にしたいとすると…。

aView.frame.origin.y = 100.0f;
aView.frame.size.height = 100.0f;

としてやれるなら簡単ですが、
上記はできません…。

X, Y, Width, Heightのどれかをひとつだけでも変えたい場合でも、
やれることは、
[aView setFrame:xxxx];
だけである。

従って、
新しいCGRectを作ってやって、
そのCGRectをsetFrameしてやるしかない…。
上記の例だと、
XとWidthが変わらないから、
まずは元の位置をベースにしてCGRectを取得し、
YとHeightを変更。
そのCGRectをsetFrameする。

CCRect newFrm = aView.frame;
newFrm.origin.y = 100.0f;
newFrm.size.height = 100.0f;

[aView setFrame:newFrm];

ひとつの部品が1回動くだけならいいけど、
いくつかの部品を相関関係も気にしながら動かさなきゃいけないことの方が多い。
そうなると、
上記のようなコードをたくさん書かねばならず、
可読性やメンテナンス性が大きく損なわれる。

ので、
UIViewのカテゴリにしてしまう。
UIButtonとかUISwitchとか、
その辺も軒並みUIViewを継承しているので、
全般的に使える。

例のごとく、
カテゴリ名はとりあえずAddition…。
ソースはちょっと分量が多いのでヘッダのみ。
実装は上記のようなコードを書いて、
さらに内部でコールしているに過ぎない。

#import <UIKit/UIKit.h>

@interface UIView (Addition)

#pragma mark - Setters of X, Y, Width, Height
#pragma mark (with one parameter)
- (void)setX:(CGFloat)x;
- (void)setY:(CGFloat)y;
- (void)setW:(CGFloat)w;
- (void)setH:(CGFloat)h;
#pragma mark (with two parameters)
- (void)setX:(CGFloat)x Y:(CGFloat)y;
- (void)setX:(CGFloat)x W:(CGFloat)w;
- (void)setY:(CGFloat)y H:(CGFloat)h;
- (void)setW:(CGFloat)w H:(CGFloat)h;
#pragma mark (with all parameters)
- (void)setX:(CGFloat)x Y:(CGFloat)y W:(CGFloat)w H:(CGFloat)h;
#pragma mark (with CGPoint, CGSize)
- (void)setPoint:(CGPoint)p;
- (void)setSize:(CGSize)s;

#pragma mark - Calculation of X, Y, Width, Height
#pragma mark (of X)
- (void)xPlus:(CGFloat)f;
- (void)xMinus:(CGFloat)f;
#pragma mark (of Y)
- (void)yPlus:(CGFloat)f;
- (void)yMinus:(CGFloat)f;
#pragma mark (of Width)
- (void)wPlus:(CGFloat)f;
- (void)wMinus:(CGFloat)f;
#pragma mark (of Height)
- (void)hPlus:(CGFloat)f;
- (void)hMinus:(CGFloat)f;

@end

経験上、
これだけ実装しておけば、
あまり困らないハズ。
とにかく1ステップで位置/サイズ調整が終わるのは、
ソースコードの可読性に歴然と差が出る。
と思う。

それでは。
ちゃお☆


まこぴー。

2014年4月2日水曜日

[iOS][Objective-C]Storyboardの分割



どうも。

未だにStoryboardっつーのが、
よくわからないw
過去のプロジェクトではxibばかり扱ってきたので…。

で、
今のプロジェクトになってようやく、
Storyboardを使っているワケです。

最終的には担当者が自分ひとりになってしまったけど、
最初は3人いて、
ひとつのStoryboardを3人でいじってマージをするという、
悲惨な状況でしたw

根本的に、
xibにせよStoryboardにせよ、
実態はXMLなので、
XMLベースでマージをすればいいんですが、
まぁ大変です。

しかも、
後から画面を付け加えて、
pushViewControllerとかできなかったりして、
直感的なようで直感的でないし、
よくわからない…。

Storyboardをイメージでしか理解していなかったので、
複数人開発には向かないなぁ…。
なんて思っていました。

結局時間もなくて根本改善ができなかったのですが、
個人的にはひとつの答えに行き着きました。

Storyboardを最初から分割すればいいのでは…。
そして、
Push-Pop型の画面遷移の場合、
RootのViewControllerがいるワケで、
そいつからの流れでどんどんPushしてやれば、
Storyboardの見た目上でつながりを持っていなくてもいいのでは…?

で、
Root-Sub1-Sub2-Sub3
と、
それぞれ別のStoryboardにして、
・順番にPushしていって順番にPopできるか?
・popToRootViewControllerできるか?
換言すると、
NavigationControllerによるスタック管理がきちんと成り立つのか?
を検証してみたところ、
オールOK!
期待通りの結果が得られた。

まぁ、
Segueとかいうのを使ってつなげて、
ひとつのStoryboardで全画面をマッピングできるし、
ソースコード量が減らせる。
というのがStoryboardを使う醍醐味のひとつな気もしますがw



それでは。
ちゃお☆


まこぴー。

2014年4月1日火曜日

[iOS][Objective-C]ネット接続の確認


どうも。

Webサービスと連携していると、
ネット接続していることが必須ですね。
当たり前ですがw

先回りしてネット接続を確認して、
あらかじめアラートを出したり、
そもそも処理をさせないようにするとか。

そんなときは、
Reachability
というのが、
iOS Developer Libraryに転がっているので、
そいつを取り込んで使えばいい。
(Reachability.hとReachability.m)

確認の都度、
ごにょごにょと書くのは面倒なので、
utilみたいなクラスに、
クラスメソッドで定義しておく。

+ (BOOL)isAvailableDataNetwork {
 
    Reachability *internetReachability = [Reachability reachabilityForInternetConnection];
 
    NetworkStatus netStatus = [internetReachability currentReachabilityStatus];
 
    if (netStatus != NotReachable) {
     
        return YES;
     
    } else {
     
        return NO;
    }
}

netStatusは、

typedef enum : NSInteger {
    NotReachable = 0,
ReachableViaWiFi,
ReachableViaWWAN
} NetworkStatus;

となっていて、
ViaWWANが3Gとかの接続です。


それでは。
ちゃお☆


まこぴー。

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をログ出しするよりも、
その方がよっぽど分かりやすい。


それでは。
ちゃお☆


まこぴー。