頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


すごく便利!ブロック構文を使って式を得る

アプリ内でたくさんの式を扱うとするよ。
yy = -34.9456869602161078*pow(xx,0)
+110.1711990130272056*pow(xx,1)
-42.2603582000001268*pow(xx,2)
+8.8934314905976777*pow(xx,3)
-1.0504024065048447*pow(xx,4)
+0.0697635676738542*pow(xx,5)
-0.0024233376396043*pow(xx,6)
+0.0000341459080453*pow(xx,7);
↑こんな感じのがたくさんあるんだ。
これは「赤ちゃんの成長グラフ」で使う近似式なんだけど、これをどういう風にアプリ内に持っておけばいいんだろう?って思ってさ。

最初はクラスメソッド(クラス名を'ApproxEquation'ってしとくよ)にしてこんな感じにしてたんだよ。
+(double)getYforX:(double)xx option1:(int)option1 option2:(int)option2 …
{
    double yy;
    if (option1 == 0) {
        if (option2 == 0) {
            switch(option3) {
                //略

    return yy;
} 
↑かなりざっくりだけど、いろんな条件で式を場合分けする必要があるんだよ。
条件としては性別や身長or体重とか、何歳から何歳までに使える式だとかまあそういうのがいくつもあるんだ。
呼び出し元でそういう条件全てとxxを指定してコールするんだけど、値をまとめて欲しい時があるんだよ。
近似式なので近似曲線を描こうとすると、表示する範囲内でxxを100分割して少しずつ大きくしていくxxに応じたyyを得る、とかまあそんなのね。よくあるでしょ。

これを実装しようと
for (int ii=0;ii < 100;ii++) {
    double xx = offsetX + ii*unitX;
    double yy = [ApproxEquation getYforX:xx option1:MALE option2:HEIGHT …
    //配列に入れていくとか
}
こんな感じに記述すると、毎回呼び出し先でいくつも条件判定をしてから計算されることになるよね。
条件判定を何回もするのってなんか無駄じゃない?

結構な計算量になるからなんとか高速化したいと思っていろいろ調べたらさ、ブロック構文で実現できるみたいなんだ。
要は式を取ってくるってこと。

するとさっきの呼び出し元は先に式を得てるからループ内で条件判定する必要がないことになるんだ。
{
    Approximation approximation = [ApproxEquation getEquationOption1:MALE option2:HEIGHT …
    for (int ii=0;ii < 100;ii++) {
        double xx = offsetX + ii*unitX;
        double yy = approximation(xx);
        //配列に入れていくとか
   }
}
↑ああ、なんかすっきりするよね!

Approximationってのは次のように定義されるブロック変数だ。
typedef double (^Approximation)(double xx);
↑'Approximation'って変数名でdouble xxをもらってdoubleの値を返すってことだよね。

式自体を定義して、条件に応じて必要な式を返すクラスメソッドはこんな感じになる。
+(Approximation) getEquationOption1:(int)option1 option2:(int)option2 …
{
    Approximation approximation;
     if (option1 == 0) {
        if (option2 == 0) {
            switch(option3) {
              //略
                approximation = ^(double xx) {
                    return -34.9456869602161078*pow(xx,0)
+110.1711990130272056*pow(xx,1)
-42.2603582000001268*pow(xx,2)
+8.8934314905976777*pow(xx,3)
-1.0504024065048447*pow(xx,4)
+0.0697635676738542*pow(xx,5)
-0.0024233376396043*pow(xx,6)
+0.0000341459080453*pow(xx,7);                    
                };

    //略

    return approximation;
}
↑こんな構成で式を返す。便利だねえ!


Gingerがすごい!のか?

ジンジャー|無料英文チェックソフト|ネイティブ表現|仕事効率

こんなサービス待ってた!
ブラウザ上などで英文を入力してるといろいろと修正してくれるってサービス。
さっそくMacのSafariにインストールしてみたよ。

テキスト入力後

↑こういうのを入力するでしょ。すると自動的におかしいところが青くなるんだ。おもむろにマウスのポインタをその部分へ持って行くと、、、

Gingerによる修正案表示中

↑修正案が表示される。うっすらとしてるのはスクショを撮ろうとキーボードの操作をしたため(修正案はキー操作すると消える)。
修正案への変換は文章全体でも単語ごとでもいける。
なおなぜeverydayのところが青になってるかは不明。個人的には by a car → by car って修正して欲しかったんだけど。

なんとも動作が不安定だったりするし、精度もあれかもしれないけど、時間が経つほどよくなるサービスと期待してしばらく使ってみようっと。

普段Macの純正メモアプリでブログの下書きをしてるんだけど、このメモアプリでも使えるようになると最高なんだけどなあ。


ところで、Gingerはブラウザ上のどこを対象にしてるのかな、と思ってローカル上にtextareaタグで書き込める場所を作って試してみたらちゃんと動いてくれたよ。英文下書き用の自分専用ページでも作っておこうかな。


サブビューを全部消すカテゴリ

あるUIViewオブジェクトで仮に名前をbaseViewとしておくよ、これにいろいろとviewとかlabelなんかを乗せた後、不要になったら全て削除する時ってあるよね。
こんな場合、削除するためによくこんな記述をしてたんだ。
{
    for (UIView *view in [baseView subviews]) {
        [view removeFromSuperview];
    }
}
↑単にbaseViewに乗ってるviewオブジェクトを片っ端からremoveFromSuperviewしてるってだけなんだけどさ。

こういうのもカテゴリで書いておけばすっきりするよねってことでやってみたよ。
実装部分はこんな感じで。
@implementation UIView (RemoveAllSubviews)
-(void)removeAllSubviews
{
    for (UIView *view in [self subviews]) {
        [view removeFromSuperview];
    }
}
@end
こうしておけば、上のコードは
{
    [baseView removeAllSubviews];
}
って一行で書くことができる。ああ快感!


CGPointもカテゴリで配列へ

頭と尻尾はくれてやる! 構造体の定義部分にカテゴリで拡張しておく
↑まあこの続きと言えば続きかな。カテゴリx構造体のネタだよ。

CGPointって構造体があって座標を入れておくのによく使うんだけど、これも配列によく出し入れするのでカテゴリを追加したんだ。

実装部分はこんな感じにしてみた。
@implementation NSMutableArray (AddCGPointToArray)
-(void)addPoint:(CGPoint)point
{
    NSValue *value = [NSValue value:&point withObjCType:@encode(CGPoint)];
    [self addObject:value];
}
@end

@implementation NSArray (GetCGPointFromArray)
-(CGPoint)pointAtIndex:(int)index
{
    NSValue *value = [self objectAtIndex:index];
    CGPoint point;
    [value getValue:&point];
    return point;
}
@end
迷ったのはメソッド名。
ホントは追加時のメソッド名をaddCGPoint:としたかったんだけど、これに合わせると取り出す時のメソッド名が
cGPointAtIndex:
になってしまう。これはメソッド名は小文字で始めるってマイルールがあるからなんだけど、、、これはしっくりこない。
CGPointAtIndex:
cgPointAtIndex:
cgpointAtIndex:
とかいろいろ考えたんだけど、今のところ座標を示す点を持つ構造体はCGPointしか使わないから
addPoint:
pointAtIndex:
という組み合わせにすることにしたよ。
クラス名、メソッド名や変数名ってホント悩むよね。





ステータスバーの上層にビューを表示させたい

頭と尻尾はくれてやる! UIButtonのタップと長押しを検出する
↑この続きね。UIButtonの長押しは検出できた、と。
次に長押ししている間にステータスバーに何かを表示する部分の処理。

これを実装しようとしたんだけど、、、一癖も二癖もあるね。
簡単にステータスバーへの参照を引っ張ってきてそこへaddSubview:すりゃいい、なんて思ってたんだけどそんな簡単なもんじゃなかったよ。

まずは基本的なとこから行ってみる。

どうやらUIWindowオブジェクトを作らなければいけないみたいなんだ。こんな感じで。
{
    UIWindow *window = [[UIWindow alloc] init];
    window.windowLevel = UIWindowLevelStatusBar;
    [window makeKeyAndVisible];
}
どこかにaddSubview:するとかじゃなくて最後のmakeKeyAndVisibleメソッドで表示される。frameプロパティやバックグラウンドなんかを指定すれば簡単にチェックできるよ。
windowLevelプロパティにどんな値を設定するのが適正かはよくわからなかった。UIWindowLevelStatusBar+1なんてしてるのもあったしなあ。iOSのバージョンがアップして不具合を発生すれば要確認ってところかな。
UIWindowクラスはUIViewのサブクラスなので、自分が表示したいUIViewオブジェクトなどをここに乗せればOK。

ステータスバーの上にビューを表示
↑こんな感じで表示できる。ナビゲーションバーのボタンを長押しした場合に表示されてる、というイメージだよ。

ところで、UIWindowオブジェクトなんて普段あまり関わらないよね。
せいぜいAppDelegateのapplication:didFinishLaunchingWithOptions:にまいどー!って出てくるだけじゃない? ああ、このwindowにのっけりゃ表示されるんだな、くらいの理解だったんだ。
改めてリファレンスを見てみるとアプリは通常このwindowを一つしか持てないんだってさ。いや、複数のwindowを作ることはできるんだけど処理してくれるが一つだけみたいで、開発者がどのwindowを有効にするか指定する必要があるんだ。
最初このmakeKeyAndVisibleメソッドを使ったらそれまで使えていたタブなんかが効かなくなって焦ったんだけど、そういうことなんだよね。

有効なwindowをkey windowと呼ぶらしいけど、それじゃそのkey windowを元のに戻すのはこんな感じ。
{
    [[UIApplication sharedApplication].keyWindow makeKeyAndVisible];
}
UIApplicationクラスに文字通りkeyWindowなんてプロパティがあってそこから通常のアプリのwindowオブジェクトにアクセスできる。

なお、一度作ったUIWindowオブジェクト自体を破棄したら自動的に元のkeyWindowに戻るけど、なんとなくはっきり指定した方が気分がいいよね。


これだけわかったらボタン長押しでステータスバーにヒントを表示するビューを表示/非表示させることはできるはず。
ただ、iPad対応や画面の回転にも対応できるようになど汎用性を持たせようとするとかなりめんどくさいんだよね。




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.