2011年12月27日火曜日

NSArray/NSDictionaryをファイルに書き出す方法

NSArray(やNSDictionary)をファイルに書き出す場合、通常は
[array writeTofile:@"パス名"];
一発でいける。

ところが、arrayの要素がNSクラス(から継承されたクラス)でない場合、
これでは書き出せない。
正確には、NSCodingプロトコルに準拠しているクラスでないと書き出せない。
(書き出し時にエラーが発生する。)

たとえば、Cの構造体を要素にしている場合とかがこれに相当する。
NSValueが含まれる場合も、実体が任意のアドレス内容であるため同じ。

そういう場合は、要素毎NSDataに変換して出力する。
こんな感じ。
[[NSFileManager defaultManager]createFileAtPath:path contents:nil attributes:nil];
id fp=[NSFileHandle fileHandleForWritingAtPath:path];
typedef struct {
   // 構造体定義
} ST;
ST st1;
NSValue *val;
NSEnumerator *emu=[lineArray objectEnumerator];
while (val=[emu nextObject]) {
    // 1行ごと構造体に読みなおして書きだす
    [val getValue:&st1];
    NSData *data = [NSData dataWithBytes:&st1length:sizeof(ST)];
    [fp writeData:data];
}
[fp closeFile];
NSValueの場合はNSString stringWithFormat:で文字列化して書き出すという手もある。

ついでにいえば、NSArrayをNSDataに変換する方法、
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
[fp writeData:data];
を使ってもでもだめ。これもNSCodingがないから。

ちょっとしたテクニックかも。








2011年12月25日日曜日

UIScrollView座標について

UIScrollViewとその配下に置くViewの座標の件。

画面回転が有効で、Autosizingの指定が双方で異なると座標がずれてしまう。
特にiPadでは要注意。

Autosizingの設定を同じにしていると座標も同じになる。

2011年12月24日土曜日

iOSで使えるフォント

iOSは5になってフォント数が増えた。
日本語で言えば、「ヒラギノ明朝体」が使えるようになったのは大きい。

iOS5でのみ使えるフォントをUILabel等表示で使っているとき、これをiOS4にもって行っても代替フォントで表示されるので、若干意図する表示とは異なるだろうが、大きな問題にはならない。

しかし、プログラム的にフォントを選択するような場合には、その追加されたフォントは選択出来ないようしなければならない。これは、フォントは番号ではなく名称で管理する必要があることを意味する。

フォントにはfamilyNameとfontNameの2つがある。大分類と詳細名と考えれば良い。

使える全フォントを全部表示するプログラムは以下の通り。

NSArray *namesArray=[NSArray arrayWithArray:[UIFont familyNames]];
NSEnumerator *emu=[namesArray objectEnumerator];
NSString *name;
while (name=[emu nextObject]) {
    NSLog(@"Font familyNames=%@",name);
    NSArray *namesArray2=[NSArray arrayWithArray:[UIFont fontNamesForFamilyName:name]];
    NSEnumerator *emu2=[namesArray2 objectEnumerator];
    NSString *name2;
    while (name2=[emu2 nextObject]) {
        NSLog(@"+-Font fontNames=%@",name2);
    }
}
ついでにそのフォントで例文でも表示させれば良いかも。

・・・追記

逆に、iOS5使えなくなったフォントもある。
気がついた限りでは「Heiti_K」とか。代替フォントはあるけど。

・・・2012/11/30追記
iOS6でもフォントが幾つか追加されている。
でもiOS5から1つフォントが削除された。いわゆる7セグLEDフォント。
電卓や時計に使っている人いるんじゃないかなぁ。
どのフォントが追加・削除され、結局どういうフォント体系になったかは拙作「X−BASIC for iOS」の説明書の中に書いたので、よろしければご購入をm(_ _)m。





2011年12月23日金曜日

iOSシミュレーターでのピンチ/ダブルスライド動作の仕方

iOSシミュレータ上で、ピンチ(ズーム)やダブルスライドの動作がメニュー上には存在しないし、マウスでどうするのかがわかりにくい。

やり方は以下の通り。

・マウスカーソルがシミュレーター上にあるとき、Optionキーを押すと○が2つ出てくるが、
これを離したり近づけたりするとピンチ動作になる。

・その状態でShiftを押すと○の位置が固定されてダブルスライド出来る
 基本的には、○を近づけた状態で+Shiftを押して、その状態でマウスを動かす

私もこれを知るまではズームとかダブルスライドだけは実機で確認してた。

後解ってないのはトリプル以上のタッチやスライド。

ひょっとしてマウスを2本以上つながないとだめなんだろうか。
MagicTouchPadでもだめなのは確認済み。

(2012/01/20;ダブルスライド追記)

2011年12月12日月曜日

「安心メール v2.00」「手書きメール v1.0」発売開始

弊社 iOS用第5弾アプリ、「安心メール」がV2.0になり、 写真撮影機能が追加されました。 ボタン1つ押すだけ、非常に簡単にメールを送信できます。
http://itunes.apple.com/app/reliable-mail/id474716740?mt=8&ls=1


また、第6弾アプリ「手書きメール」が発売になりました。 手書きメールは、手書きの内容をそのままメールで送ることが出来ます。 写真との合成も可能です。
http://itunes.apple.com/app/handwrite-mail/id486255587?mt=8&ls=1

「安心メール」はとにかく簡単に連絡を付けたい人向け、「手書きメール」は、少数の相手に簡単にメールを出したい人向けです。 どちらも、iPhone/iPadの特性を活かし、キー操作がほとんど不要ですので、電子機器に不慣れな方にでも使ってもらえます。

よろしくお願いします。

2012/02/05追記
「手書きメール」がV1.50になりました。
ダブルスライドによるページ送りと、疑似筆圧処理が追加されました。
細かいバグも修正されています。

ここの維持のためにもお買い上げいただくとありがたいかとm(_ _)m

2011年12月11日日曜日

UIImagePickerControllerの使い方(3)

撮影処理中は、画面の回転が検知されない。実際には
- (void)willAnimateRotationToInterfaceOrientation:が呼び出されない。
.view.frameも変化しない。

カメラ自体を横向きに出来ても、画素そのものは回転しないからであろう。
写真アプリで画面を横にしてもボタンの(方向は変わるが)位置は変わらないのはそのためである。

しかし、強制的に回転処理を作り出すことは出来る。
回転に対する通知を登録し、それを受信してビューに細工すればいい。

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(orientationChanged:)
                                                     name:UIDeviceOrientationDidChangeNotification
                                                   object:nil];
        - (void)orientationChanged:(NSNotification *)notification

ただ、回転処理をOSに代わって行う必要があるため、かなり面倒である。
ラベルは、文字列には縦書きがないため基本的に使えないし、
アイコンを使うなら回転したアイコンを用意する必要がある。
実はこれをうまく処理する方法もある。Viewを回転させれば良いのである。

UIKitはよく出来ていてViewを回転させると、その上にある各要素の座標も適宜変換してくれる。だから、ボタンなども、回転後の位置でちゃんと効く。

具体的な方法は省略。
少しは自分で調べよう、と言うことで。







2011年12月9日金曜日

UIImagePickerControllerの使い方(2)

iOS標準のカメラ画面ではなく独自に画面を作成する場合、
        picker.showsCameraControls  = NO; // デフォルトのカメラコントロールを非表示にする

を入れ、独自にビューを作成する。
そのビューはpicker.cameraOverlayViewに設定するか、もしくは
            [window addSubview:ビュー];
で表示する。

オーバーレイの場合、viewDidLoadなどが呼び出されないので、nibが非常に使いにくい。
特に、pickerは起動時間にばらつきがあり、この時間をプログラム的に判定するには
viewWillApearを使う必要があるため、オーバーレイは使えない。
従って、オーバーレイをnibで作成する場合はaddSubviewで表示した方が良い。
というか、オーバーレイを使う意味はほとんどないと思うのだが。

カメラ画面を独自に作ってシャッター([picker takePicture])をユーザーが処理する場合、
その押下タイミングには注意が必要である。
まだ前回の撮影に伴う画像処理が終わっていないのに再度シャッターを切った場合、
内部的にエラーが発生し、撮像されない(シャッターが無視された形になる)。
このエラーはコンソールにログを出していると見えるのだが、プログラム的には検出できない。

これを避けるには、フラグを作って、imagePickerController:が呼び出されるまでは
次のシャッターを切らないようにする必要がある。

2011年12月7日水曜日

UIImagePickerControllerの使い方(1)

UIImagePickerControllerはiOS上で画像の撮影・選択処理を一括して行えるクラスである。
この「撮影」と「選択」処理を1つのクラスで扱えるとはいえ、
この2つは無理矢理統合されている感が強く、基本的にはどちらで使うかによって
呼び出し方が異なる。

撮影処理は、クラスインスタンスを確保し、モードを設定して表示するだけで良い。
シャッターや前後カメラ切り替えなど、必要なボタンなどもすべて用意してくれる。

    UIImagePickerController *picker= [[UIImagePickerController alloc]init];
    picker.sourceType = UIImagePickerControllerSourceTypeCamera; // カメラを使う

    // 以下↓の設定は、↑この設定の後でなければならない
    picker.delegate             = self;
    picker.cameraCaptureMode    = UIImagePickerControllerCameraCaptureModePhoto; // 静止画
    [self presentModalViewController:picker animated:YES];
UIImagePickerControllerはUINavigationControllerの子クラスでありそれを含んでいるが、
外部のUINavigationControllerとは連結できない。
従って、上位のビューがUINavigationControllerの管理下にあったとしても、
[self.navigationController pushViewController:ビューコントローラー animated:YES];
で表示するのではなく、上記のようにpresentModalViewControllerを使う。

pickerの表示は、初回のみ時間がかかる。
シャッターを押すと撮影されるが、実際の画像ができあがるまでには時間がかかるため、
できあがった時点でデリゲートが呼び出される。
-(void)imagePickerController:(UIImagePickerController *)picker_ didFinishPickingMediaWithInfo:(NSDictionary *)info
// 撮像処理(delegate)
// 写真が取得可能になったら並行動作で呼び出され、この中を走っている間、メインも走り続ける
{
    // 画像はinfoから取り出す。
    UIImage *image=[info objectForKey:UIImagePickerControllerOriginalImage];
}

(次回に続く)







2011年12月5日月曜日

Webページの表示仕方

WebページはUIWebViewで簡単に表示できるが、その中に画像の貼り付けなどがある場合、
その参照を解決する指定が必要になる。

クラスはこんな感じ。外部のURLへのリンクを貼る場合は、その確認のためUIAlertViewを表示する必要があるため、UIAlertViewDelegateも必要となる。

@interface HTMLViewController : UIViewController
<
UIWebViewDelegate,UIAlertViewDelegate
>
{
    IBOutlet    UIWebView *webView;
    NSURL       *url;
    UIAlertView *aview;
    BOOL        fopenAlert;
}
@property (nonatomic, retain) UIAlertView *aview;
ここではwebViewを含むnibを別途読み込んでいる。
(だからIBOutletがある。)

nib上でwebViewに以下の属性をチェックしておく。
Scaling Scales Page Fit
Detection Links
HTMLの読み込みはviewDidLoadで行う。

NSString *path=[[NSBundle mainBundle]pathForResource:@"HTMLファイル名" ofType:@"html"]; // リソース内のパスを得る
    NSString *html=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
    NSURL *base=[NSURL fileURLWithPath:path]; // HTML内の画像のリンクを解決するため、これが非常に重要

表示
[webView loadHTMLString:html baseURL:base];
読み込んでからの設定が必要
webView.delegate=self;
    aview.delegate  =self; // 念のため
    url=nil;

    aview=[[UIAlertView alloc]initWithTitle:@"リンク"
                                    message:@"サポートページを開きますか?"
                                   delegate:self
                          cancelButtonTitle:@"いいえ"
                          otherButtonTitles:@"はい",nil];
    //
    fopenAlert=NO; // アラートを表示していないフラグ
    ~

HTML内のリンク(a href=)はそのままではwebView内で開いてしまうので、
内部のリンクはそのまま、外部へのリンクはSafariブラウザが開くように処理を差し替える。
そのため、webViewからのデリゲートを使う。

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    switch (navigationType) {
        case UIWebViewNavigationTypeLinkClicked: // リンクがクリックされたとき
            { // switch内でローカル変数を作るときはスコープ宣言が必須
            NSURL *url0=[request URL];
            if ([[url0 scheme]hasPrefix:@"file"]) {
                break; // file:の時は内部なので、何もしないで開けば良い
            }
           
            // 外部へのリンクの時は、飛ぶ前にUIAlertViewで確認する
            // 確認なしで飛ぶアプリも多いが、アップルの仕様書中では「確認せよ」と書いてある。
 
            url = [url0 copy]; // copyしないと消えてしまうので要注意
            } // ローカルスコープ
            [aview show]; // 表示したらすぐ終わる
            fopenAlert=YES; // アラートを表示しているフラグ
            return(FALSE);
        defalt:
            break;
    }
    return(TRUE);
}
urlをcopy属性で保存しているのは、アラートビューは独立して実行されるからである。

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
// アラートビューでボタンがクリックされた時に呼び出されるデリゲート
{
    NSLog(@"button=%d",buttonIndex);
    if (buttonIndex!=alertView.cancelButtonIndex) { // 「はい」のとき
        NSLog(@"直前url=%@",url);
        // リンクへ飛ぶ
        [[UIApplication sharedApplication] openURL:url]; // httpがあるので自動的にブラウザが立ち上がる
    }
}

- (void)dealloc
{
    webView.delegate=nil; // なんか必要なのだそうな
    if (url!=nil) [url release]; // copy属性だから
    [aview release];
    [webView release];
    [super dealloc];
}
画面の回転を有効にしている場合、回転ごとに内容の再読込をしないと画面幅が調整されない。

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
// 回転検出
{
    [webView reload]; // こうしないと画面幅が調整されないので
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // 回転有効
    return YES;
}
「重要」なこと。
UIWebViewで扱う文字エンコードはUTF-8にしないといけない。
HTML内のエンコード指定

とファイル自身の文字エンコードをUTF-8にするのを忘れないように。
(秀丸エディタだとファイル書き出し時に文字コードが指定できて楽。)









2011年12月3日土曜日

設定値の保存/読み出し方

設定値の保存にはNSUserDefaultsを使う。

設定値は辞書形式で記録されるので、その名称となるキー値を決めておく必要がある。
NSString *文字列である。
これらは通常.hに書いておく。

#define keyCameraDevice     @"cameraDevice"
#define keyFalarmRetrigger  @"falarmRetrigger"
#define keyExitDelay        @"ExitDelay"
#define keyLatestDate       @"LatestDate"

書き出し側
    NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; // これは定型書式

    // 記録するオブジェクトの型によってメッセージが変わる
    [defaults setInteger:cameraDevice       forKey:keyCameraDevice ];
    [defaults setBool:falarmRetrigger       forKey:keyFalarmRetrigger];
    [defaults setFloat:tmExitDelay          forKey:keyExitDelay];
    [defaults setObject:latestDate          forKey:keyLatestDate];

    // 書き込み終わったら同期をかける。iOS4以降では必須
    [defaults synchronize];
読み出し側
    NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
    // インストール直後かどうかを判定
    if ([defaults boolForKey:keyInitialSetup]==NO) {
        [self initSetup];
    } else {
        cameraDevice        =[defaults integerForKey:keyCameraDevice];
        falarmRetrigger     =[defaults boolForKey:keyFalarmRetrigger];
        tmExitDelay         =[defaults floatForKey:keyExitDelay];
        latestDate          =[defaults objectForKey:keyLatestDate];
    }

一番最初の初期化
-(id)initSetup
    NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
    [defaults setBool:YES forKey:keyInitialSetup];  // 初期設定をした印
    // 以下初期値で書き出す処理を書く

というような感じ。







2011年12月1日木曜日

viewDidLoad/viewWillApear/viewDidApear等のタイミング

viewの読み込み・表示の各タイミングでデリゲートが呼び出されるが、
それぞれには重要な意味がある。

特に、nib内容に対する設定、読み出しはviewDidLoad以降でないとすべて無効になる。
中で読み込んでいるUI要素へのデリゲート設定もそう。
これ以前にやってもエラーは出ないのに誤動作する(というか動作しない)のでわかりにくい。

    class=[[ClassName alloc]initWithNibName:@"ClassNibName" bundle:[NSBundle mainBundle]];
    // この先並行動作する
    // nib読み取り終了はviewDidLoadで判定(かなり先になることもある)

    //view表示命令
    [window addSubview:navigationController.view];
とか
[mainView.navigationController popToViewController:self animated:YES];
と記述した場合、

viewDidLoad
Nibの読み込みが実際に終わったときに呼び出される
メモリーオーバーによってViewDidUnloadが呼び出されない限り、初回のみ。

viewDidUnload
メモリーオーバーが発生したときに呼び出される。
不要なワークがあれば、ここで解放する。
次回Viewを表示する際には先にviewDidLoadが呼び出される

viewWillApear
表示直前に呼び出される
要素表示のon/off(.hidden=YES/NO)やラベルの表示内容の設定などはここまでで行う

UIPickerViewのカーソル位置設定はここでしないとうまくいかない
[pickerView_ selectRow:selectRow inComponent:0 animated:NO];

viewDidApear
表示直後に呼び出される
この後Viewへ制御が移る

viewWillDisapear
ビューが消える直前に呼び出される

viewDidDispear
ビューが消えた直後に呼び出される


なお、UIImagePickerControllerのオーバーレイではこれらの処理は通らない。
従って、オーバーレイはnibで作ると制御が難しいため、
オーバーレイではなく、普通のUIViewControllerで作って重ね合わせる方が得策である。


・・・2012/11/30追記
iOS6では細かい呼び出しタイミングが変更になっている。
各処理の終了を待たずに次の処理が走ることがあるのだ。
これに依存している場合は変更が必要になるので注意。





2011年11月30日水曜日

UIActivityIndicatorViewの使い方

重い処理を実行するときは、UIActivityIndicatorViewを表示して動作中であることを示した方がいい。

UIActivityIndicatorView *indicator=[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:style];
表示位置は適当で良いが、viewのど真ん中に出す場合はこんな感じ
    CGRect frame;
    frame.origin=CGPointZero;
    frame.size.width=37;
    frame.size.height=37; // インジケーターのサイズ
    indicator.frame=frame;
    indicator.center=view.center;
    [view addSubview:indicator];

    [indicator startAnimating];
UIActivityIndicatorViewはRunLoopに戻らないと表示されないので、一瞬戻す。
これが非常に重要。
本来は、重い処理毎別スレッドにして並行動作させる事を意図しているから、こうなっているのかもしれない。
    [[NSRunLoop currentRunLoop]runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.0]]

    ~重い処理~
    [indicator stopAnimating];
    [indicator removeFromSuperview]; // 外す
    [indicator release];
iOS5からは色を付けられるようになった。
(iOS4まではbackgroundColorだけ。)
IB上で色を設定しててもiOS4上では無視される。







2011年11月29日火曜日

Xcode;Expression is no assignable


Xcode上でObjective-Cで式を書いていると、Expression is no assignableというエラーが出ることがある。ほとんどの場合の原因は、「構造体とクラスを混在した式を書いている」事にある。

「例」
UIImageViewのプロパティframeにアクセスする場合

    CGRect frame1=imageView.frame;        // OK
    CGSize size1 =imageView.frame.size;    // Expression is no assignable

frameはCGRect構造体を返してくる。
struct CGRect {
   CGPoint origin;  // frame.origin.x   frame.origin.y
   CGSize size;     // frame.size.width frame.size.height
};
なので、構造体のメンバーにアクセスするframe.sizeはCの式としては正しく見える。

ところが、Objective-CではimageView.frameはプロパティー式であり
    [imageView getFrame]
相当式なので、それに.sizeを付けても読み出せない。
従って、
    CGRect frame1=imageView.frame;    // OK;これはObjective-Cのプロパティー式
    CGSize size1 =frame1.size;    // OK;これはCの構造体メンバーにアクセスする式
としなければならない。

ここでは読み出し側を書いたが、書き込み側も全く同様。

Objective-C、特にUIKit上ではプロパティと構造体の命名規則に区別がないため非常にわかりにくい。
これはUIKit命名規則設計上の最大の失敗だと感じている。

Expression is no assignableが出たら、それがプロパティーなのか構造体のメンバーなのかを
確認した方が良い。

2011年11月28日月曜日

UIAlertViewとUIActionSheet利用上の注意

UIAlertViewとUIActionSheetは、常にallocとinitを同時に行う必要がある。
すなわち、そのクラスインスタンスを使い回す場合でも、allocだけ先に行い、実利用時にinitだけ行って再利用すると言うことが出来ない。

文法上は問題ないのだが、実実行時、2回目以降に表示が異常になる。

 ~~だめな書き方~~
// 初期化時
UIAlertView *alertView=[UIAlertView alloc];



// 利用場所
[alertView initWithTitle:~];


~~正常動作する書き方~~


// 初期化時

UIAlertView *alertView;
alertView=nil;

// 利用場所
if (alertView!=nil) [alertView release];

alertView=[[UIAlertView alloc]initWithTitle:~];


ちなみに、ActionSheetでは(複数の)選択項目とCancelを設定出来、iPhoneではCancelボタンが表示されるが、iPadでは表示されない。アクションシート外をタップするとキャンセル相当という操作のようである。「Cancelが表示されない!」と悩まないように(実は自分)。


UIKitでは、Objective-Cの理論上記述可能なはずなのに、実際には呼び出し順序に制約がある組み合わせが他にもあるので、実行時に異常が発生する場合は、そのあたりも要注意である。

2011年11月25日金曜日

Xcode;The service is invalid

Xcodeで実機をつないでずっと開発をしていると、

The service is invalid
Please check your setup and try again
(0XE8000022)

と出るようになることがある。
Xcode3.2.6/4.1/4.2いずれを使っても出るのでバージョンは関係ない。


これは、どうやらXcode内と実機内部で把握している状況が食い違った場合に出るようである。

こうなってしまった場合は、

  1. Xcodeを閉じる

  2. 実機を外す
  3. 実機上から開発中のアプリを削除
  4. 実機の電源を切って、再起動、再接続
  5. 実機の電源を切って、再起動
  6. Xcodeを再起動

で回復する。Macも再起動するとなおよさそう。

あくまで感覚的にではあるが、どうも複数の実機をつなぎ、複数のアプリのプロジェクトを切り替えてテストしているときに発生しやすいような気がする。シミュレーターと実機での実行を交互にやったりしてると余計。

2011/12/22追記
つないでいる実機で、アプリケーションのアップデートを行うと発生することが判明。

2011年11月24日木曜日

Xcode;Analyzeの結果行のバグ

XcodeのAnalyzeはPotential leak of an object alloc の結果行が1行下にずれるバグが居る。
3.2.x時代からずっと直ってない。
なので、示している行を信じると解らないことになる。
(ごくまれに正しいことがあるが、本当にまれ。)

他の結果はずれない。

ちなみに、この警告が出たからと言って、必ずしもプログラムが間違っているわけではない。
確保と解放が正しく一致する構造がクラス内で構築されているなら、無視して良い。

それだけ。



2011年11月21日月曜日

nib(xib)で配置したクラスのinitについて

InterfaceBuilderでカスタムクラスを配置した場合の初期化の注意事項。

InterfaceBuilderで配置したクラスは、読み込み時に自動的に初期化されている。
従って、改めてinitを発行する必要はない。
というか、「発行してはいけない」。
せっかくOSが行ってくれた初期化を無効にしてしまう。

カスタムクラスにメッセージが伝わらない場合は、これも疑ってみると良い。

初期化が必要なときは、独自に初期化メソッドを実装し、その中で[super init]を発行しないようにする。


initはプログラム的にallocした場合にのみ必要なメソッドなのである。

2011年11月15日火曜日

iOS;文字列をグラフィックに描画する

文字列をグラフィックに描画するには以下のようにする。

// 文字列
NSString *text=@"2011/11/15 20:00:00";

// フォント
UIFont *font=[UIFont fontWithName:@"Times New Roman" size:14];

// 合成位置
CGPoint point;
point.x=50;
point.y=50;

// 描画色の設定
UIColor *color=[UIColor redColor];
[color set];

// 合成表示
[text drawAtPoint:point withFont:font];

存在しない(不正確な)フォント名を与えると描画されない(正確にはサイズ=0になる)。

これはNSStringのUIKit拡張である。
表示対象は現在の「グラフィックコンテキスト」。
簡単に言えば、UIGraphicsBeginImageContext(サイズ)~UIGraphicsEndImageContext()の間なら、そのサイズにおける相対座標からに描画される。

言葉で書くとわかりにくいのだけど、やってみると解る。








2011年11月13日日曜日

Quartz2D色指定方法

Quartz2Dでは線の色と塗りつぶしの色を別に指定する必要がある。

たとえば、赤の円を塗りつぶしで描画しようとして、以下のコードをUIViewに記述して実行しても、うまく動作しない。
-(void)drawRect:(CGRect)rect
{
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(c,1.0);
    CGContextSetStrokeColorWithColor(c, [UIColor redColor].CGColor); // 線色指定
    CGContextAddArc(c,100,100,10,0,2*M_PI,1);
    CGContextFillPath(c); // 塗りつぶし
    CGContextStrokePath(c); // 描画指定
}
これで実際に描画されるのは黒塗りつぶし円である。
理由は先に書いたとおり、Quartz2Dでは線の色と塗りつぶしの色を別に指定する必要があるためである。
線色指定の下の行に
CGContextSetFillColorWithColor(c, [UIColor redColor].CGColor);
を追加すると、目的通りの赤塗りつぶし円になる。

これでだいぶ悩まされた。
なので、覚え書き。








2011年11月12日土曜日

UIViewController;画面回転の認識

画面回転のwillAnimateRotation~は、そのコントロールが表示されている間に回転されたときのみ呼び出される。

回転されてから遷移してきたときは呼び出されないので、。別途回転処理を行う必要がある

そんだけ。