2011年5月31日火曜日

Cocoa NSTreeControllerを調べる(4)

FileManagerでOutlineViewにマウントされているデバイスのディレクトリを表示する。デベロッパドキュメントに載っていたサンプル通りのままだと、外付けHDDなどがマウントされた時にどうすればいいかわからない。そんなんでNSTreeControllerについて調べている。

NSTreeControllerでFileSystemを表示しようとする場合、NSTreeNodeを使わないで直接modelのArrayをNSTreeControllerのContentにしたほうがよいみたいだ、というのが昨日の結論。それなら、さらにNSTreeControllerも使わずにOutlineViewのdatasourceでコードを書いてしまえばいいのでは、となんとなく思ったが、bindingを使ったほうがやはり何かと便利そうだ。コードの量が少なければbugも少なくなるはずだし。

というわけで、本日は昨日の状態からさらに

1,[[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes]でマウントされたVolumesを取得する。(そうするとHide属性のVolumeを拾わないですむ)
2,volumesというNSMutableArrayをメンバにして、読み書き可能なプロバティにしてNSTreeControllerのcontentにbindingさせる
3,NSWorkspaceDidMountNotification、NSWorkspaceDidUnmountNotificationを使ってマウント時、アンマウント時の処理をしてみる

以上をやってみた。

この状態から外付けHDD「for_mac」をマウントすると、


最後尾に追加される。

コードはこうなった。
#import "Controller.h"
#import "FileSystemItem.h"

@implementation Controller
@synthesize volumes;
- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
        volumes=[NSMutableArray array];
    }
    
    return self;
}

- (void)dealloc
{
    [volumes release];
    [super dealloc];
}
-(void)setVolume{
    NSArray* mountedVols=[[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes];
    NSMutableArray *roots=[NSMutableArray array];
    if ([mountedVols count] > 0){
  for (NSURL *element in mountedVols){
            FileSystemItem* item,*parent;
            if([[element path ]isEqualToString:@"/"]){
                item=[[FileSystemItem alloc] initWithPath:[element path] parent:nil];
            }
            else{
                parent=[[FileSystemItem alloc]initWithPath:[[element path] stringByDeletingLastPathComponent] parent:nil];
                item=[[FileSystemItem alloc] initWithPath:[element path] parent:parent];
                [parent release];
                
            }
            [roots addObject:item];
            [item release];
            
        }
  [self setVolumes:roots]; 
 }

    
}
-(void)awakeFromNib{
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(didMount:) name:NSWorkspaceDidMountNotification object:nil];
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(didUnmount:) name:NSWorkspaceDidUnmountNotification object:nil];
    [self setVolume];

}
-(void)didMount:(id)sender{
    NSLog(@"Mount! %@",sender);
    NSDictionary* info=[sender userInfo];
    NSURL* url=[info objectForKey:NSWorkspaceVolumeURLKey];
    FileSystemItem* parent=[[FileSystemItem alloc]initWithPath:[[url path] stringByDeletingLastPathComponent] parent:nil];
    FileSystemItem* item=[[FileSystemItem alloc] initWithPath:[url path] parent:parent];
    [parent release];
    [volumes addObject:item];
    [item release];
    [treeController rearrangeObjects];
  
    
}
-(void)didUnmount:(id)sender{
    NSLog(@"UnMount! %@",sender);
    NSDictionary* info=[sender userInfo];
    NSURL* url=[info objectForKey:NSWorkspaceVolumeURLKey];
    NSUInteger deleteNo;
    for (NSInteger i=0; i<[volumes count]; i++) {
        FileSystemItem* item=[volumes objectAtIndex:i];
        if ([[item fullPath] isEqualToString:[url path]]) {
           deleteNo=i;
        }
    }
    [volumes removeObjectAtIndex:deleteNo];
    [treeController rearrangeObjects];

}
@end

treeControllerのbindingなどはこうなっている。

これでほぼ考えていた動作が実現できた。次はEseに組み込んでみる。これはちょっと苦労するところがあると予想している。@"/"を期待した動作、などを書いているので。

2011年5月30日月曜日

ダイナミックフィギュア 上巻読了


あまりにもおもしろくて読み終わるのが惜しい。ゆっくり読もう、プログラミングたくさんしよう、と思ってもページをめくる手が止まらない。
今日は上まで読んだんだからもうやめ、と思っても、その上巻の終わりが後をひく終わり方で下巻に突入せざるを得ない。

今日が振替休業で休みでよかった。たっぷり楽しめた。

Cocoa NSTreeControllerを調べる(3)

昨日の段階ではNSTreeControllerとファイルシステムの間にNSTreeNodeをはさんでみたが、どーも冗長なのでもう少し調べてみた。

InterfaceBuilderでNSTreeControllerのアトリビュートを表示すると

こうなっていて、実際にはこの「Children Key Path」をうそでもいいから入力しないと、ビルドには成功しても実行時にデバッグコンソールでエラーが出続ける。
で、ここでChildrenとかのKeyPathを入れるということは、Contentに子ノードを表現するArrayなり、Arrayを返すメソッドがあれば自動的にTree表示してくれそうな感じがする。Childrenの他に入力するKeyPathは画像の通り「Count」と「IsLeaf」・・・ふーむ、これはつまり、EseでOutlineView用に使っているクラスを流用すれば、簡単に行けるのでは?

と見当をつけてさっそく作業をしてみる。
EseのOutlineViewで使っているノード用のクラスはデベロッパドキュメントに載っていたFileSystem.mほぼそのまま。若干現在の実験プロジェクト用に書き換えて、さらにbindingも変えた。


「Model Key Path」に「representedObject」を入れるとNSTreeNodeが保持するObjectのKeyPathが要求されるようなので、ここはrepresentedObjectを削除して直接FileSystemクラスのKeyPathを指定した。

そうすると、とりあえずこれだけのコードで
-(void)awakeFromNib{
    NSArray *mountedVols = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths]; 
 NSMutableArray *roots=[NSMutableArray array];
    if ([mountedVols count] > 0)
        
 {
  for (NSString *element in mountedVols){
            NSLog(@"%@",element);
            FileSystemItem* item,*parent;
            if([element isEqualToString:@"/"]){
                item=[[FileSystemItem alloc] initWithPath:element parent:nil];
            }
            else{
                parent=[[FileSystemItem alloc]initWithPath:[element stringByDeletingLastPathComponent] parent:nil];
                item=[[FileSystemItem alloc] initWithPath:element parent:parent];
               
            }
                       [roots addObject:item];
            [item release];
          
        }
  [treeController setContent:roots]; 
 }
    

}

(parentをreleaseしてないの点は現在のところ無視(^^;))
こうなった。

ふむふむ、あとはOutlineViewではディレクトリ名しか必要としないから全部NSStringでPath名を取得しているところを、NSURLに入れ替えるかどうか、というあたりだな。removableなメディアのマウントについてはNotificationがあるので簡単だろうし。

2011年5月29日日曜日

ダイナミックフィギュア

図書館に本を返しに行ったら、上下並べておいてあったので早速借りる。


これはすごい。久しぶりに冒頭からわくわくしている。
エヴァンゲリオンそのものはあまりよく知らない(コミックス版を借りて読んだことがある程度。しかもあまり感心しなかったという)のだけれど、醸しだす雰囲気は「エヴァ・インスパイア」系なのでしょうか。

上巻100ページ読んで、「全ての謎に納得する解決」があることを祈っている自分を発見。この厚さだからきっと大丈夫だろう。

Cocoa NSTreeControllerを調べる(2)

昨日は運動会とその反省会のためプログラミングどころではなく、ビールをがぶ飲みして帰宅後即ベッドに倒れ込む。

本日もけっこう疲れていて気力が湧かず、目もしばしばしてあまり集中力がない状態で1日過ごす。しかし2,3日プログラミングから離れるとあっという間に多くのことを忘れるので多少無理してでもCocoaに触れておいた。

本日はNSTreeController、およびNSTreeNodeについて調べた。

書けたコードはこれだけ。
-(void)awakeFromNib{
    NSArray *mountedVols = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths]; 
 NSMutableArray *roots=[NSMutableArray array];
    if ([mountedVols count] > 0)
        
 {
  for (NSString *element in mountedVols){
            Node* node=[[Node alloc]init];
            NSString *volName=[[NSFileManager defaultManager] displayNameAtPath:element];
            [node setNodeTitle:volName];
            [node setNodeIcon:[[NSWorkspace sharedWorkspace] iconForFile:element]];
            NSTreeNode* treeNode=[NSTreeNode treeNodeWithRepresentedObject:node];
       
            [roots addObject:treeNode];
            [node release];
        }
  [treeController setContent:roots]; 
 }
}

コンテンツ用のNodeクラス(名前とiconだけ)を用意して、それをNSTreeNodeに格納、それをMutableArrayに追加して、NSTreeControllerにsetContentってなんだかすごく冗長なんだけど、いいのかなあこんなんで。
MutableArrayをクラスのメンバにして、InterfaceBuilderでContentに指定したほうがいいか。
とりあえずこれでこんな風な表示になる。


/homeと/netをどうやって消すのかな、とか、この後Childをどうやって指定していくのかな、とか、明日以降への疑問が山積み。

2011年5月27日金曜日

Cocoa NSTreeControllerを調べる

内蔵HDD以外のドライブがマウントされたら、それもちゃんとrootItemにしてNSOutlineViewに表示したい。しかし・・・NSOutlineViewで、rootItemが二つ以上ある場合はどうするの?と調べてみたら全然わからない。

ただ、NSTreeContorollerを使えば実現できることはわかったので、とりあえずそっちを調べてみた。しかしNSTreeControllerもよくわからないクラスだなあ。簡単なサンプルがないかググってみたところ、

Designed Recreations -Cocoa NSTreeController and NSTreeNode

というアーティクルと、ダウンロードできるサンプルが非常にわかりやすかった。サンプルプロジェクトを実行したらこんな感じだった。


ちょっと古いプロジェクトだとビルドできなくてがっくりすることがある。このプロジェクトの場合はTargetを64bitにしてやればビルドできた。

画像でわかるとおり、rootItemがたくさんある。nibでのbindingは、TreeControllerのNodeクラスのnameをOutlineViewにつなげているだけ。

ふむふむ、このコードを参考にして、少し実験してみることにしよう。運動会が終わったら。

2011年5月26日木曜日

運動会

生まれも育ちも北海道なので、この時期に運動会が開催されることに疑問をもったことがない。しかし最近、北海道新聞のコラムに載ったとある大学教授のエッセイを読んで驚愕した。

教授の育った東京では、運動会が秋に開催されるのはもちろんとして、応援といえば学校のそばの文房具屋のおじさんくらい、だったそうな。ところが北海道に来て、娘の小学校の運動会で席取りのために徹夜同然で席を確保し、妻が盛大なお弁当を作る、という文化に激しい違和感をもった、という内容。

ひえー、家族総出、まかり間違えば一族総出で運動会に出かけてきて、昼ごはんはドバーっとお弁当やらおやつやらフルーツやらを大量に並べてほとんど宴会状態になる、というのは北海道だけなのか?それとも日本全国の田舎の風習?

運動会の「席取り」は大変な騒ぎで、私の務める学校でも明日の夕方から1時間にわたって「席取り」が開催される。ちなみに運動会は明後日土曜日ね。
席取りに先立ち、まず子どもに「席取りの順番」を決めるくじを引かせる。くじには、1番から始まって、PTA戸数分の番号が書かれている。で、金曜の5時から、そのくじの順に並んで、順番に運動会を座って見るための場所を確保していく。1家庭畳1枚程度、という約束。
テント用のペグなどを地面に打ち込んで、ビニールテープなどで区分けしていく。中にはビニールシートを早々に引いていく人もいる。

運動会当日は、このシートの上にキャンプ用の保温シートやら座布団やらを敷いて、のんびりビールなどを飲みながら保護者のみなさんは子どもに声援を送りビデオを撮影し写真を撮る。

これって北海道だけ?そうだとしたらすごいカルチャーショックだ。(^^;)

Cocoa FileManager メニューから新しいPathを選択する

現在表示中のディレクトリを、いわゆる「パンくずリスト」で次のように表示している。


この状態から「Devleper」を押すとこんなメニューが出現する。

これは「/Developer/」以下のディレクトリを列挙して選択できるようにしている。

当然、「/」をクリックするとこうなる。

「/」以下のディレクトリを表示するのはいいとして、じゃあ例えば外付けのHDDをマウントしたらどうなるか。

この外付けHDD、今のところ残念ながら/Volumesにしか出現しない。
本来なら、「/」ではなくて「Macintosh HDD」とVolume名で表示して、他にマウントされているVolumeがあればそれも並べて表示できるのがふつうだろう。
まあ現在の仕様上仕方のないところだが、これはなんとかしていきたい。
デベロッパドキュメントの

Low-Level File Management Programming Topics
の「Information about Files and Volumes」をざっと読むと、mountedVolumeURLsIncludingResourceValuesForKeys: options:を使うらしい。

ふーむ、古目のデベロッパドキュメントのサンプルをもとにしたOutlineViewのファイルシステム表示はNSStringベースなのだが、NSURLベースに書き換えないとmountedVolumeURLsIncludingResourceValuesForKeysは使えないなあ。(戻り値がNSURLのArrayなので)

OutlineViewの表示も変更しないといけないが、アプリの根本的な部分に関わるので早めにやっておいたほうがいい感じ。とりあえず運動会が終わってからか。

2011年5月25日水曜日

Cocoa FileManager NSMenu popUpMenuPositioningItem: atLocation: inView:

考えていた機能がそれほど重要なものではないので、とりあえずpopupMenuで表示出来てるからいいか、と思いかけたけれど、ドキュメントにあったNSMenuクラスのpopUpMenuPositioningItem: atLocation: inView:をもう少しだけ研究してみることにした。

popUpContextMenu: withEvent: forView:がクラスメソッドなのに対し、popUpMenuPositioningItemはインスタンスメソッド。

第1引数がNSMenuItemなので、特定のItemだけpopupさせるのかと思ったのだけれど、よく読めばItemはnilでも構わないということなので試してみる。
NSPoint point= NSMakePoint([cell drawedRect].origin.x, [cell drawedRect].size.height);
NSMenuItem* activeitem=[menu itemAtIndex:1];
[menu popUpMenuPositioningItem:activeitem atLocation:point inView:self];

これでこうなった。

マウスカーソルがある場所でクリックしているので、これで期待通りの動作となった。なーんだ、できるんじゃん。ドキュメントをもっとしっかり読まないといかんなあ。

ただし初めはMenuが無効な状態で表示されていた。itemを生成するとき、setTargetしないと有効にならないのね。(^^;)

第1引数でNSMenuItemを指定すると、そのItemが指定したポジションのTopLeftにくるように描画されている。なるほど便利だなあ。

あとはMenuで選択されたディレクトリを表示できるようにすればいい、と。これは明日以降。

Cocoa FileManager NSPopUpButtonCell

NSPopUpButtonCellを使えば、Cellをクリックしたら出てくるPopupメニューを簡単に制御できるか実験してみた。結論からいうと、ちょっと手に余る。

別プロジェクトを作って、NSPopUpButtonCellを表示してみる。
コードはほとんど現在作っているFileManagerのプロジェクトから流用したのでそれほど手間はかかっていない。

ドキュメントを読んだ限りでは、オブジェクトを生成してNSMenuをsetしたら、あとは難しいことはNSPopUpButtonCellがやってくれる、ように思える。(大いなる誤解(^^;))

で、@"/Developer/Applications/Audio"みたいなpathを分解して、それぞれのサブディレクトリをMenuとして表示するPopUpをこの程度まで作ってみた。


各ボタンにMenuをセットして

[button drawWithFrame:rect inView:self];

としてみたら見事にPopUpButtonとして表示してくれるものの、見た目はこれじゃいかん、ということで

[button drawInteriorWithFrame:rect inView:self]

とボタン内部だけ描画してみたらこうなってしまった。

CellなのでEventは表示しているViewで管理しないといけないようだし、drawInteriorWithFrameをカスタマイズしていくのはかなり苦労がありそう。じゃあNSPopUpButtonそのものを使う、と言う手はどうかというと、見た目のカスタマイズはどちらにしろNSPopUpButtonCellの役目なので苦労は同じだろう、と判断した。

というわけでNSPopUpButtonCellをめぐる実験はこれだけで終了とあいなった。

2011年5月24日火曜日

Cocoa FileManager NavigateBar(3)の続き

ドキュメントを読んでから再びXcode4をいじってみたら、やはりNSPopUpButton、あるいはNSPopUpButtonCellを使っているようだ。

ふーむ、NSButtonCellとはかなり扱い方が違うようなので、おもしろそうではある。でもいろいろ苦労するして実装していくかどうかちょっと迷う。

そろそろFileManagerの肝である、「ファイルのコピー、移動、プレビュー」、「ドラッグアンドドロップ」といった濃いところに手をつけてみたいとも思うので。さらにTableViewでのソートとか、グリッドなViewとか。

コピーや移動はUIでユーザとのやりとりも必要だから、ToolBarにも手をつけたいし。ふむ、明日の気分で決めよう。(^^;)

Cocoa FileManager NavigateBar(3)

ディレクトリを表示したCellをクリックしたら、そのディレクトリ以下にあるディレクトリをメニューで表示してみる。


-(NSInteger)cellAtPoint:(NSPoint)point{
    NaviButtonCell* cell;
    for(NSUInteger i=0;i<[buttonArray count];i++){
        cell=[buttonArray objectAtIndex:i];
        NSRect rect=[cell drawedRect];
        if(NSPointInRect(point, rect)){
            return i;
        }
    }
    return -1;
    
}
-(void)mouseDown:(NSEvent *)theEvent{
    NSPoint point = [self convertPointFromBase:[theEvent locationInWindow]];
 NSInteger index = [self cellAtPoint:point];
    if(index!=-1){
        NaviButtonCell* cell=[buttonArray objectAtIndex:index];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL isDir, valid;
        
        valid = [fileManager fileExistsAtPath:[cell fullPath] isDirectory:&isDir];
        if (valid && isDir) {
            NSArray *array = [fileManager contentsOfDirectoryAtPath:[cell fullPath] error:NULL];
            if(array){
                NSMenu* menu=[[NSMenu alloc]initWithTitle:@"Directory"];
                for (NSInteger i=0; i<[array count]; i++) {
                    NSString* testpath=[[cell fullPath] stringByAppendingPathComponent:[array objectAtIndex:i]];
                    valid = [fileManager fileExistsAtPath:testpath isDirectory:&isDir];
                    if(valid && isDir){
                        NSMenuItem* item=[[NSMenuItem alloc]initWithTitle:[array objectAtIndex:i] action:@selector(menuSelected:) keyEquivalent:@""];
                        [menu addItem:item];
                        [item release];
                    }
                    
                    
                }
                
                [NSMenu popUpContextMenu:menu withEvent:theEvent forView:self];
                [menu release];
            }
            
        }
    }


}

この程度で可能。ただ、popUpContextMenuだとmouseDownを拾ったロケーションでメニューがpopupしてしまうわけで、こういう事態も起こる。

うーん、Xcode4とはちょっと違う動作だなあ。もしかしてXcode4、popupButton使っているのかなあ。もう少し調査してみよう。

2011年5月23日月曜日

iPad2 iOSのUI

UIというか、ユーザーインターフェイスの入力デバイス、すなわち「指」インターフェイスはやはり画期的なUIだとつくづく思う。

娘がiPad2でSafariを使っているのを横目で見ていると、人差し指で軽くスクロールさせている。極めて直感的ですぐれた操作方法なので、それを見ていたおかあちゃんがすぐにまねできる。はじく、ひろげる、狭める、という動作が期待通りの結果を見せてくれるすごさ。

これをプログラミングではどうやって処理してるんだろう。ちょっと興味が湧きますなあ。
PalmやZaurusなど、いわゆるPDAでタッチパネルのデバイスを長らく触ったけれど、結局マウスのかわりに指やペンを使う、というところで留まっていたわけで、appleってこのあたりは発想がすごいんだなあ、と改めて感じた。

Cocoa FileManager NavigateBar(2)

本日は「ひとつ上のディレクトリへ」ボタンの実装。
-(IBAction)upButtonAction:(id)sender{
    if ([tabTitlePath isEqualToString:@"/"]) {
        return;
    }
    else{
        NSString* newPath=[tabTitlePath stringByDeletingLastPathComponent];
        [outLineView deselectRow:[outLineView selectedRow]];
        [self setDirectoryRowFromFullPath:newPath];
    }
    
}

rootなら何もせず(というか、rootの時はボタンがDisebleだから一応のエラー対策)、その他の時はもともと持っている「tabTitlepath」という名前の「現在のディレクトリのfullPath」からstringByDeletingLastPathComponentでひとつディレクトリを遡るパスをつくり、それをOutlineViewにセットする、と。
OutlineViewの選択行が変更されると連動してTableViewの中身やTabのタイトルなども変更されるので(KVOですな)、これだけでOKだった。

時間があったのでさらに「現在のディレクトリの場所のビジュアル表現」をやってみた。

モロXcode4のまね。(^^;)一番時間がかかったのが「区切り線」をドット絵で書くこと。
この手の小さなpngはもっぱら

このアプリを使わせてもらっている。慣れるとなかなか便利なアプリであります。

ここから「ディレクトリをクリックした時の処理」は明日以降。

2011年5月22日日曜日

Cocoa FileManager NavigateBar

結局SplitViewのスプリッタ位置復元はとりあえず放り出した。(^^;)ユーザーインターフェースとしては「ユーザーが使いやすくカスタマイズした状態の保持」はすごく大事なのでそのうち再挑戦しよう。

本日はTableViewの上に表示するNavigateBarをいじる。1つ上のディレクトリに移動する、ヒストリ上を進む、戻る、といった機能をつけたい。

本日はここまで。

ボタン3つは楽をしてNSButoonをnibにはりつけた。上矢印があんまり見た目がよくないなあ。(某サイトからいただいたものだから文句は言えないが)

その横のディレクトリの「パンくず表示」はNSButtonCellを使っている。iconとpath名を表示するだけなので、ButtonCellでなくてもいいかも。NavigateBar(NSView)側からイベントを処理するならNSCellのカスタムクラスでいいか。
- (void)drawInteriorWithFrame:はOutlineViewの表示用のCellから流用。というか、このCellそのまま使ってしまってもいいかな、と考えている。

Cell側にメニュー表示を任せようと考えていたが、NavigateBar側でマウスイベントを処理することにすれば、メニューもひとつで済むか。
Xcodeについているこの「ミニツールバー」のまねをしようとしている。

path名をクリックしたら「同じディレクトリの中の他のディレクトリ」をメニューで選択できるようにしてみる、と。OutlineViewで選択すればいいわけだからあまり必要ないといえば必要ないのだけれど・・・。

2011年5月21日土曜日

ipad2 1週間

iPad2が着いて明日で1週間。

劇的に生活を変える、ということはないけれど、ちょこちょこっと楽しくしてくれるデバイスなのかもしれない。

本日午前中は疲れのためプログラミングに手をつける気力が出なかったため、iPad2のアプリをちょろっと調べていくつかインストールしてみた。

iTunesのAppの画面はこんな感じ。

GarageBandも購入した他、極めて一般的なアプリが並んでいる感じがいたしますな。家族のためにインストールした「映画情報」がけっこう楽しくて予告編をたくさん見てしまった。北海道の田舎町の映画館の上映時間もちゃんと教えてくれるので感動しましたよ。

PaperTossとCardSharkは最高のひまつぶしゲーム。PaperTossのHD版はなんかすごくいい。(^^;)

Cocoa FileManager Tab削除の挙動 NSNotificationCenter removeObserver

Tabを削除したら当然Tabに乗っていたオブジェクトも削除される。ってことはいわゆるカウンタ方式のメモリ管理でなにかまちがいがあるのかしらん、といろいろ調べてみる。

しかし不用意なautoreleaseもないようだし、わざわざretainと書いてるところもない。

不具合の出現の仕方がこれまた不規則で、Tab削除後、TableViewの行をダブルクリックした時に発生するところまではわかっていたが、「このオブジェクトにこんなセレクタはないぞ」と怒られるだけの時や、いきなりアプリが落ちる時や、よくわからない挙動をする。

「そんなセレクタはない!」と怒るときのメッセージをよくよくみると、特定のNotificationのaddObserverで登録してあるセレクタだった。うーむ、ということは・・・addObserverしたNotificationを、オブジェクトが消えるときにちゃんとremoveしていない・・・。うおー、また単純なミスだった。

荻原本で確かめたところ、やはりちゃんとremoveObserverしないとセレクタへのポインタが残ってしまうみたい。そのポインタがたまたま他のオブジェクトを指していたときは「そんなセレクタはない!」となり、ポインタが空ならメモリエラーが発生していた、と。

コーディングを急いでいるときはこういうエラーを起こしてしまうんだなあ。もっとていねいにいかないと。

教訓:NotificationのObserverは必ず削除する

そんなんでエラーの原因がわかったところで、気分良くUserDefaultsにあれこれ記録する方法を勉強してみた。
NSRectをそのまま記録しようとして「そんなプロパティは記録できません」といわれて首をひねり、ググって
NSStringFromRect(NSRect)
に行き着く。そんなんでとりあえずこんな感じで記録できた。

そこでこれを復元するわけだが・・・NSSplitViewのスプリッタの位置、どうやって復元させるかまだわかっていない。もちろんひとつだけならInterfaceBuilderで「AutoSaveNmae」を付ければいいわけだけれど、Tabの分だけSplitViewもあるわけで、これはやっぱりSplitView上にあるSubViewの位置を全部記録する必要があるのかなあ。

明日はNavigateViewに掛かりたいけれど、果たしてそこまで行けるか。

2011年5月20日金曜日

Cocoa FileManager Tab削除の挙動

自分で作ったtabインターフェイス、要はNSTabViewをタブレスで表示して、TabViewItemの切り替えをTabでやっている。

よくある、PrefarenceでツールバーでTabViewItemを切り替えているのと同じ。違うところと言えば、動的にTabItemを付け加えたり削除するところ。

addTabするのは順調に動作するのだけれど、removeTabすると挙動がおかしくなるのを発見。今までTabとOutlineViewとTableViewをばらばらにつくってきて、いざ同調させて動かそうとしたとたんに不具合がどんどん出てくる。いささかげっそり。

本日は1週間分の疲れもあって集中力が続かない。明日以降がんばってみよう。

2011年5月19日木曜日

iPad2 あまり使っていない

私自身はあまりiPad2を使ってない。もっぱら娘たちが使っている。
「でかいiPhoneは使いやすい」というのが彼女たちの感想。

すき間時間のゲームってのが使う目的みたいですな。

私は朝、新聞がわりにざっとネット上のニュースを確認する、天気を確認する、予定をチェックする(GoogleカレンダーとMUJICalendarを同期させている)のが主な用途。

早くもう少し使いこなせるようになりたいなあ。

Cocoa FileManager NSOutlineView bug原因判明

OutlineViewのrootItemを取得するのに、autoreleaseをうかつにつけていたのがbugの原因だった。(^^;)
rootディレクトリにアクセスしようとした時だけメモリのエラーでこけるので、最近追加したコードでrootItemに触っていたのを思い出してよくよく見てみたらすぐにわかった。

うー、ちょっと慣れてくるとこういうエラーコードを書いてしまう。気をつけよう。
GC有効にしてもいいのだろうけれど、GC有効にしたとたんTabの中身のViewが全部同じになってしまう、というよくわからない動作をするので今のところパス。

iOSのプログラミングにも当然興味があるわけで、GCで楽するのはもう少し後、と考えている。

さて、これで

この状態から、rootの三角をクリックすると無事

こうなる、と。しかしこの状態でもう一度rootの三角を押すと

OutlineViewの選択行ハイライトは当然消えている。これはFinderでもXcodeでも同じ動作なので仕方のないところ。しかしこれだと「右側のペインはどのディレクトリの中身か」がわかりづらいので、NavigateViewを作っていく。つまりXcodeでいう

今日気づいたんだけれど、結局自分は「Xcode4と同じようなインターフェイスのファイルマネージャ」をつくろうとしているらしい。ふむ、それもおもしろいか。それならそれで目標がはっきりしていていいかも。