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~は、そのコントロールが表示されている間に回転されたときのみ呼び出される。

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

そんだけ。

2011年11月10日木曜日

Xcodeにおけるファイル名の扱いいくつか


Xcodeにおけるファイル名の扱いいくつか。

 (1)Xcode上でプロジェクトに入れたファイルは、ローカライズ用を除いて、すべてのフォルダ内容がフラットにされるため、異なるフォルダに同じファイル名が存在する場合、重複と見なされる。

どちらが使われるかは未確認。


同様の理由でに、プロジェクト上で階層フォルダというか階層グループを作ってファイルを格納した場合も、フォルダ名を指定してアクセスする必要はない。

「例」
Alarm3D/imgというグループを作った場合、"img/ファイル名"では読めない



(2)#importはファイル名の大文字小文字が区別されない。MacOS上では区別されるので、
「どっちが読まれてる?」ってな事になることがある。基本的に、英大文字小文字だけで違うファイル名は使わない方が良い。Windows上では区別されないから、相互やりとりするときにも問題を起こす。

(3)リソースファイルはmainBundleのパス内にある。
フォルダはフラットになるが、iOSデバイス内ではちゃんとフォルダ構造を持っているからである。
なので、以下のようにアクセスする。
NSString *dir=[[NSBundle mainBundle]bundlePath];
NSString *path=[[NSString alloc]initWithFormat:@"%@/Icon.png",dir];

2011年11月9日水曜日

nib内にある要素のrelease

nib内にある要素はdeallocでreleaseが必須である。

nib内にあるのだから必要ないのかと思っていたが、実際にはnibの情報に従って読み込み時にメモリ上に領域を確保するようなので、解放が必要。

deallocが呼び出された後にメモリーリークが発生するときは真っ先にこれを疑うべし。

2011年11月8日火曜日

Windows7でのいろいろ

Windows7(32bit)でのいろいろテスト結果。
  • Falcomゲーム:そのままでは実行不可。ゲームエクスプローラーに登録すれば実行可能になる。
    「Ys6」「Ysフェルガナ」「YsOrigin」「Zwei2」はクリアして動作確認済み。
    ただし、Zwei!!はどうやっても実行できなかった。仮想XPモード上ではDirectXを使わないようにすれば一応実行可能だが、「画面が狭い」「マウスカーソルがずれる」「画面描画が遅い」という問題が出る。
  • Cygwin:フォルダを丸ごとコピーした上、mkpasswdとmkgroupさえやり直せばそのまま実行可能。
  • ATOKのパレットが表示されないとき。
    コントロールパネル>地域と言語>キーボードと言語>キーボードの変更>言語バーで設定を変更する。
  • ショートカットのアイコンが消えるとき。
    アイコンキャッシュが壊れている。
    1. c:\User\ユーザー名\AppData\Local\IconCache.dbを削除
    2. 同ディレクトリで右クリック~新規作成~テキスト(名前は適当)
    3. そのテキストをIconCache.dbにリネーム
    4. プロパティで読み取り専用にする
    しばらくするとアイコンが戻る(もしくは再起動)。
    しかし、これでもだめなものもある。
    XPではアイコンが出るのに7では出ないもの。

    というか7でもコピー直後は出てたのに、その後は「使えるアイコンがない」とかいって出てこないもの。
    「あふw」がそう。
    現在は解消。
  • とにかく画面描画が遅いので調整する。
    コントロールパネル>パフォーマンスの情報とツール>視覚効果の調整>視覚効果で
    「デスクトップのアイコン名に影を付ける」と「ドラッグ中にウインドウの内容を表示する」のチェックを外す。
    これでだいぶ速くなる。
    が、結局クラシック表示は止めてAero対応にしてしまった。
    でも後者の設定を外すだけでもだいぶ速い気はする。
  • Aero有効時のWindowキーを使った操作方法。
    TAB3次元表示
    拡大
    縮小
    スペースウインドウを透明化
    デスクトップ表示
    HOMEアクティブ以外を最小化
    1~タスクバーの左から順にウインドウを戻す
    他のAero時の操作方法は
    ここにも記事あり
  • Picasaの送るの変更は、
    Explorerで「プログラムから開く」>規定のプログラムの選択>参照で設定する。
    Picasa自体の中には設定がない。
というようなところ。仮想XPモードを使っても実行が難しいソフトがある様子。
やはりXPマシンは手放せない。

2011年11月7日月曜日

JavaScriptの変数の有効範囲

JavaScriptの変数の有効範囲。

どこにも情報がないので調べてみたが、1HTMLファイルの中だけらしい。
リンク先では参照できない。

どの順番で開いたかによって内容を変えようと思っているのだけど、出来ない。
JavScriptだけじゃ無理?

Google Chromeのcookie、デフォルトでは「ローカルへのデータ設定を許可する」になっているのに、なぜか設定できない(V12/V13で確認)。
正確には、Webサーバー上につないで閲覧したときは設定できるが、ローカルにあるHTML上では代入が無視されてしまう。

Firefox(Win/Mac)では問題ない。同じWebKitのSafari(Win/Mac)でも問題ない。
でもiOSのSafariはだめ。

IEはいちいちチェックをしてくるのがうっとうしいが、動作はする。

レンダリングエンジンとJavaScriptエンジンは別という証拠だけど、iOSだけ訳わからん。

2011年11月6日日曜日

Macでゴミ箱にファイルが入らなくなった時の解決法

Macで、ゴミ箱にファイルが入らなくなった。
「この項目はすぐに削除されます。この作業は取り消すことができません」と出て、即消えてしまう。
調べてみると、同様の現象を発見した。

http://tamuchi.net/2010/01/mac-osx-snow-leopard-106.html
http://masnoko.jugem.jp/?eid=293

うちの場合、「ls -ld ~./Trash」すると「そんなもんない」と言われる。
どうも状況が違う。
後者と合わせて考えるに、.Trashが消えているか、アクセス権がおかしくなっているかのいずれかである。
以下まとめ。
  1. ゴミ箱の位置は/Users/ユーザー名/.Trashである
    ルートにも.Tashesというのがあるが、これは関係ない。
  2. Mac上で隠しファイルを見る方法は上記にもいろいろ書かれているが、一番簡単なのは、ファイラーを使うこと。
    うちではmuCommanderを使っているが、これで一発だった。
  3. .Trashがある場合、「ユーザー名」フォルダのアクセス権が書き換わっている可能性が大

である。
本来、ユーザー名フォルダは、当然そのユーザーに読み書きのアクセス権があるはずだが、
なぜか、アクセスできるユーザーからそれが抜けていた。
なので、Finderでそこを開き、i=情報で共有アクセス権を表示し、鍵を開けて変更可能にして、
+でユーザーをアクセス権=読み書きで追加する。
その後、ギアマークから「内包している項目に適応」で、そのフォルダ内分全てを同じアクセス権にする。
それにより、.Trashのアクセス権も更新される。

これをすると、他PCからアクセスされる共有フォルダのアクセス権も変わってしまうので、そこは別途戻すこと。

2011年11月5日土曜日

Xcodeに.jsファイルを加える

XcodeのプロジェクトにJavaScriptのソースファイル.jsファイルを加えたら、
「no rule to process file」という警告が出た。

警告だけなのでコンパイルと実行できるが、出来れば消しておきたい。
この場合、

  1. projectを開く
  2. TARGETを選択
  3. Build Phasesのタブを選択
  4. Compile Sourceを開く
  5. ここに.jsが入っているので、Copy Bundle Resourcesに移動する

これでOK。日本では情報が見つからなかったが、JavaScriptを使う人が少ないのか?

2011年11月4日金曜日

iOS開発におけるProvisioningのいろいろ

今までの開発経験から、Provisioningのいろいろまとめ。
  • ProvisioningにはDeveloperとDistributionの2種類がある
  • ともにiTunes Connectで作成する
  • ProvisioningはDeveloper/Distributionそれぞれで1つずつ作成すれば良い

    ここで重要なのは、Developerは開発機の特定が必要でDeviceの設定があるが、
    Distributionは公開用なのでDeviceの設定は不要である。
    Deviceの設定がグレーアウトされているのはそういう意味なので、無理にSelectAllを押す必要はない。
  • 証明書にもDeveloperとDistributionの2種類がある
  • 先にProvisioningを作成、後で「それを含んだ」証明書を作る。
    これが非常に重要。この順番を間違えると無意味な証明書が出来てしまう

    無効な証明書でビルドすると、「Application failed codesign verification. The signature was invalid, or it was not signed with an Apple submission certificate. (-19011)」という警告が出る(この状態でもReleaseまでは実機で動作するが、Distributionはエラーが出て通らない)。
  • 出来上った証明書をキーチェーンユーティリティーでキーチェーンのルートとシステムに登録

    この際、キーチェーンユーティリティーにはバグがあり、同時に複数の操作を行えないので、
    「古い証明書の削除→一端終了→再起動→新しい証明書の登録→終了」の手順をDeveloperとDistributionで計4回(ルートとシステムの双方に登録が必要だから)も行う必要がある
  • XcodeのOrganaizerもしくはiPhone構成ユーティリティーでProvisioningを追加、さらに各デバイスを開いてそれをインストール
  • ここで非常に重要なのは、Developerは開発用デバイスの全てにインストールが必要であるが、
    Distributionはインストール不要だと言うことである。というかインストールできないのが正常。
  • Xcodeのコンパイル設定で、Debug/ReleaseはDeveloper、DistributionはDistributionをコード承認に設定する。
    Releaseでは相変わらず-19011の警告が出るが、無視すれば良い。
  • 追記;証明書をいじった後は、Xcodeのコード署名を再設定する(同じファイル名でも)
というややこしい手順が必要になる。

何回も訳がわからないまま証明書から作り直してしまったが、
通常はProvisioningの有効期限切れなら「renew」で更新さえすれば、こんなややこしい手間はしないで済む。

2011年11月3日木曜日

Objective-Cのメモリ管理指示子の違い

Objective-Cのメモリ管理指示子の違いまとめ。

assign アドレスコピー
メモリ領域のアドレスだけをコピーする
release不要
実体の値保証なし=実体の確保/管理の把握が必要(別途allocしてある領域とか)
内容の変更はしないか、したらオリジナルが改変される

retain 開放遅延
メモリ領域の開放を遅延する
release必要
ローカル領域にも有効
内容の変更は、基本的に不可(推奨されない。開放したら消えるから。)

copy
実体コピー
同じ大きさの領域を確保の上、内容もコピーする
release必要
内容の変更も自由。ただし、改変結果は大元には影響しない。

実際のところ、わからないのなら、メモリと速度に余裕があるならcopy~releaseにしておけば
安全。
UI要素はcopy宣言されている、と思う。

・・・

私は、解放タイミングが明確でないautoreleaseは一切使わない。
公開されているライブラリを使うときも、必ず使わないように書き換える。

2011年11月2日水曜日

Windows7のデスクトップからホームグループのアイコンを削除する方法

Windows7のデスクトップからホームグループのアイコンを削除する方法。

レジストリを変更する。

(1)検索にRegEdit.exe入力、実行
(2)以下のキーを変名する
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{B4FB3F98-C1EA-428d-A78A-D1F5659CBA93}
これを#{~}とかにする。削除しても同じ。
(3)regeditを終了
(4)デスクトップでF5を押す(画面の更新)

なお、Pro 64ビット版ではデフォルトで表示されてないような気がする。

2011年11月1日火曜日

「安心メール」発売開始

AIG-SoftのiOS用アプリ第5弾「安心メール」が今日から発売開始。

1ボタンを押すだけでメールの送信が出来ます。

電子機器の扱いに不慣れな方、特に高齢の親御さんと子どもさんの間の連絡に
便利なように設計しました。

iPhone/iPod touch4/iPad
iOS4/5
日英
対応です。

「安心メール」

よろしくお願いします。

近日、写真機能付きのVer2にアップデート予定です。

・・・正直な所・・・

アプリが売れないとここの維持もままならないので、
寄付と思ってお買い上げ頂くと非常にありがたいかと。m(_ _)m

MacのTimeMachine用HDDの交換仕方(MoutainLion対応版)

MacのTimeMachineの交換方法。MoutainLion対応版。

TimeMachine用HDDは特殊なファイル管理下にあるので、フォーマットしてFinderで内容をコピーしようとしても、権限がどうこうと表示されてコピーできない。

以下の方法で交換する。

1.Time Machineをオフにする
2.元ディスクと新ディスクを両方マウントする;TimeMachineのHDDはつながったままだと思うので、新しいHDDをつなげるだけ
3.ディスクユーティリティを立ち上げる
4.元ディスクを選択、その中の復元のタグを選択
5.ソースに元ディスク、復元先に新ディスクを設定。
4.復元を開始
5.旧ディスクをアンマウントする
6.システム設定のTime Machineから新ディスクをバックアップに指定、Time Machineをオンにする
7.新ディスクでの最初のバックアップ時に「前回から変更がある」と言ってくるが、OKする

手順自体は解ってしまえば簡単だが、とにかく時間がかかる。
320GBでも5時間ほど。
この間もMacの利用は出来るが、あまりやりすぎるとバックアップされてないファイルが増えることになるので要注意だ。

夜中にやるのが無難かもしれない。

2011年10月30日日曜日

XcodeのVersionとCFBundleのバージョンの関係

XcodeはV4.1から、従来のVersionという項目がVersionとBuildに分かれた。
このとき、旧来のVersion値がBuildの項目に入力されたが、このあたりの関係と意味が
今1つよく理解出来てなかった。が、前回のアプリ審査提出時に引っかかったので
理解させられることとなった。

結論から言えば、

TARGETSのVersion = CFBundleShortVersionString
         Build   = CFBundleVersion
 
となっている。一般ソフトではVersionの方が上でBuildの方が下の概念だと思うが、
Apple方言ではそうではないわけだ。 
 
アプリのバージョンアップをするとき、このCFBundleVersionが以前のバージョンより
大きくなければエラーが出てアップロードできない。
要注意。
 

2011年10月29日土曜日

画像透過の制約

UIImageやUIButtonで画像を使った場合、PNGを使うと透過を使うことが出来る。

しかし、2つ以上のそれが重なるとき、両方の画像がフルカラーだと透過がうまくいかない。
確認したのは2つであるが、上側に下画像の背景が透過しなくなった。

上側を256色に減色したらうまくいった。

このバグはシミュレーター上では発生せず、実機でのみ発生する。
iOS4.3と5.0どちらでも同様である。

・・・2012/01/31追記
フルカラーで作成されていても透過するデータもあった。
少なくとも、Windows上の花子フォトレタッチで作ったデータはだめ。
データの格納方式に違いがあるのかも知れない。

・・・2012/11/30追記
iOS6でも同様なことを確認。透過しないならしないでいいから、シミュレーター上でも再現して。

Xcode4.2の改良点

Xcode4.2ではいくつかの改良点がある。
私が把握しているのは以下の通り。

(1)iOS上で使えないフォントに対するチェックが厳しくなった
XcodeではMac上にイントールしているフォントが全て選択できるが、
iOS上では使えない物も当然ある。従来はそのチェックがなかったが、
4.2ではチェックが厳しくなった。
ただし、 フォントの設定にはバグが居て、SystemからCustomへの変更が直接できない。
System→SystemBold→Custom→フォント選択という手順を通らなければならない。

(2)コンパイラの文法チェックが厳しくなった。
これは良い点。@class 名前とどっかで記述してるけど、それが実は構造体名だったとき、
「同じ名前で別の定義があるぞ」(意訳)と表示してくれる。
@classは宣言だけで実行には関係ないけど、バグには違いない。

(3)従来XcodeはUSB有線接続したデバイスのみデバッグ対象にしたが、WiFi接続下の機械も使えるようになった。
→しかし、この機能をONにしていると、シミュレーターでの実行を指示しているのに勝手に実機で実行したりするのであまり使えない。よって、うちではOFFに変更。

(4)iOS5対応

2011年10月28日金曜日

Xcode4.2のバグ

Xcode4.2.0(for SnowLeopard)にはいくつかバグが居る。
私が把握しているのは以下の通り。

(1)Settings.bundleが編集できない
Xcode4.1以前で編集するしかない
4.1でSettings.bundleを開いたまま終了し、4.2で同じプロジェクトを開くと、その内容が表示された状態で起動する。
が、ここで編集するとファイルが破壊されることがある。

(2)iPadを含むアプリをArchiveしようとすると、必ず証明書が無効という警告が出る
しかし、実際にはそのArchiveでアップロードできるので問題ない。

(3)タブをまとめようとすると落ちることがある
ファイルを1つ開き、何も操作しないまま別のファイルを開いてタブをまとめようとすると落ちるような気がする。
また、タブをたくさん開いていると落ちやすい。

(4)追加された機能に新たにキーバインドが設定されているため、それと重複する設定があった場合、表面上、注意も何もなしにキーが無効になってしまう
標準のキーのまま使っているなら問題ないのかもしれないけど、キーバインドを変更していてると、
Xcode4.2にしたとたん一部操作が出来なくなることがある。これは、新規の機能に割り付けられたキーバインドと重複しているせい。Preferenceからキーバインドを見て、重複警告が出ている部分を修正する。

(5)CodeSigningの設定が外れている(事があるので)、再設定が必要

(6)エディタでソースの一部を折りたたんでいる状態で編集すると落ちやすい。
1ソースファイル内で折りたたんでいる場所が多いと落ちやすいような気がする。
これは4.2だけじゃなく、4.1でも発生する。
折りたたみは、現状ほぼ使えない状態だと言える。

(7)条件付きコンパイルがあるとき、ブレークポイントの位置がずれる

#if 0 // 実際にはコンパイル条件によりここがコンパイルされないとき
ソース←ここにbreakpointが付いていると
#endif

-(id)method
{
    // ここの最初の実行行でbreakPointが効いてしまう(止まる)
}
このブレークポイントは無視すべき。

(8)プログラムを停止したとき、タブ名と表示されている内容が食い違うことがある
現在選択中のタブ内にプログラムを停止したときにはそのソースと停止した場所が表示されるが、このときに、タブ名(表示しているソースファイル名)を変更しないときがある。
だから、「一体ここはどこ?」となることがある。

(9)日本語ファイル/フォルダ名があるとき、gitがCommit出来ない
エラーが発生する。最初からわかっていれば日本語ファイル名は使わなかったが、
後から適応しようとして困った。
フリーウエアの「gitX」では可能なので、併用する。

Xcodeは4になって、あまりにも遅くなり使い物にならなかくなったが(だからずっと3.2.6使ってた)、4.1で改善されてまともに使えるようになった。でもまだバグが散見される。
致命的な問題もあるので、一刻も早いデバッグを望む。

Xcode4.2.1でどうなっているかは未検証。というか、4.2.1のSnowLeopard対応版がまだ出てないので、試しようがない。以前Lionを導入してえらい目に遭ったので、うちではSnowLeopardに戻したから。


ちなみにうちではV4.2/4.1/3.2.6を全部使えるようにしてある。3本までのソースを同時編集することもあるから。便利。

・・・2012/03/11追記

Xcode4.3.1は、またこれはこれで大バグだらけで困った状況にある。
特にシミュレーターがiOS5のデバッグにおいて、一部状況下で全く使い物にならなくなったのが痛い。


アップルはOSといい開発環境といい、もっともっとデバッグに人員を割くべきだ。
儲かってるんだから。

iOS5の隠れた改良点

iOS5の隠れた改良点として、登録名が変えられるというのがある。
iOS4までは初回設定時に登録した名前を変更できないが、
iOS5では、設定>一般>情報>名前で変更できる。

開発機では名称を変えたいときがあるので便利。


・・・プログラミング上の変更(知った部分のみ)・・・

UIActivityIndicatorに色が付けられるようになった
UIBarButtonItemに色が付けられるようになった
UISwitchの形が変わった
Stepperが追加された
Gesture Recognizerが追加され、ジェスチャー判定が簡単にできるようになった

iOSアプリ審査提出時のタイムアウト

AppleのiOSアプリの登録で、画像をアップロードしようとするとにタイムアウトが発生することがある。また、各種情報を書き換えてSubmitするとそのまま止まってしまうことがある。

Firefox v7とv10で発生する。
SafariやFirefox v8/9では発生しなかった。

Chromeではどうなるか未確認。

やはりSafariを使うのが無難かと。

2013/03/01追記
Firefox v18.0でも1回発生したが、一旦ページを抜けて再度入ったらうまく行った。
どうも今1つ不安定な感じがする。

ようやく利用開始

開設以来、ずっと使えてなかったここをようやく利用し始めます。
当面は、情報を雑多に書き留めます。