ラベル cocoa の投稿を表示しています。 すべての投稿を表示
ラベル cocoa の投稿を表示しています。 すべての投稿を表示

2011年8月10日水曜日

読書中

風の墓碑銘
風の墓碑銘
posted with amazlet at 11.08.10
乃南 アサ
新潮社
売り上げランキング: 241790

読了。音道シリーズは全部2回以上読んでるわけだが、この作品は再読。久しぶりなのでほとんど内容を忘れていて楽しめた。老人力にもいいところがある。

次はこれ。
天冥の標〈1〉―メニー・メニー・シープ〈上〉 (ハヤカワ文庫JA)
小川 一水
早川書房
売り上げランキング: 116523
日本SFってものすごく進化してるなあ。ビジョルドなみのストーリーティングじゃないのよ。

やっぱ本はええなあ。

2011年8月1日月曜日

Cocoa addGlobalMonitorForEventsMatchingMask

NSEventのやたらと長いクラスメソッド、addGlobalMonitorForEventsMatchingMaskを使えば、デスクトップ上のマウスイベントを比較的簡単に捉えることができる。これもxcatsan師匠の受け売りだけど。

キー入力の捕捉は「ユニバーサルアクセス」の「補助装置にアクセスできるようにする」オプションを有効にしないとできないようだが、マウスの方は関係なく捕捉できる。これはSpinでやったので以前に触れたことがある。

日暮れて道遠し: Cocoa ObjC Spin デスクトップでマウスころころでSpacesを切り替える

これを使ってまたまたなにかやってみよう。SpinがLionで動くようになる目処が立たないので、とりあえずSpinの補助的な機能だったタスクスイッチャにしてみようか、と考え中。

iMac上のBootCampのWin7を使ってからLionに起動しなおすと、Lionの動きが随所に「ひっかかり」がある感じがするのは気のせいか?

特に日本語入力のタイミングが若干遅くなったような。ただあれこれインストールしまくって、OSXそのものが鈍重になった気もするので、ここらでクリーンインストールしてみるのもいいのかな。
ちなみにBootCampのWin7はインストールしたばっかり、アプリはOfficeくらいというクリーンな環境だから速くてあたりまえだけど。

2011年7月26日火曜日

Cocoa NSViewのアニメーション(4)

NSViewにアニメーション効果をつける、という勉強。

やりたいことは、デスクトップ上にボタンをひとつ表示して、そのボタンを押すとビヨーンともうひとつのViewが飛び出てくる、という非常に簡単なもの。


大きさ64×64ピクセルのボタン・・・というかNSViewのサブクラス。これをクリックすると


横からびよーんと伸びてくるもうひとつ。

CATransitionのMoveInが一番思い描いているものに近い動きをするようなので、FakeViewとTransitionViewというふたつのViewを用意してやってみたりしていた。結果から言うと、FakeViewを全く透明にしてしまうと望んだ通りの効果が生まれないらしい、ということがわかった。透明度を下げて、少しでも見える状態に描画するとMoveInができるのだが・・・。

NSAnimationContextを使って
    NSAnimationContext* current=[NSAnimationContext currentContext];
    rect.origin.x-=250;
    rect.size.width=300;
    [current setDuration:0.5];
    [[trasitionView animator] setFrame:rect];
こんなふうにも書いてみたが、ビヨーンと飛び出すふうにはならない。もう少し違う書き方をすればいいのか・・・。

とかやっているうちに結局いつものxcatsan師匠のこのページ、

(旧) Cocoaの日々: Windowアニメーション(その3)NSViewAnimation

の解説のとおりにNSViewAnimationでやってみたところ、ほぼ、考えていた「びよーん」が実現できた。一番簡単な方法一番よかった、というよくあるオチ。

次はもう1回クリックしたらピロピロピロ、と引っ込むようにしてみたり、下から出てきたり上に飛び出したりしてみよう。

おもしろくて何度もボタンを押してビヨーンをやってしまった。

2011年7月25日月曜日

Cocoa NSViewのアニメーション(3)

Lion祭りもそろそろ終了で、ゆっくりとCocoaプログラミングのお勉強。

NSView と UIView (フェンリル | デベロッパーズブログ)

を読んだり。
CATransitionでNSViewを入れ替える操作をしてみたり。

しかしどーもうまくいかないなあ。あせらず勉強しよう。

2011年7月23日土曜日

Cocoa Spacesはどこへ行った?

答え:Dockに統合された

なんのことかという、自作の豆アプリ「Spin」がLionでまともに動かないのでいろいろ調べてみた、という話。

SpinではいわゆるSpacesを変更するのに@"com.apple.switchSpaces"という名前のNotificationをpostしている。これはまだ有効で、まあまともに機能している。

しかし「現在アクティブなSpacesのNo」を得るのに

CGSGetWorkspace

というプライベート関数を使っており、こっちはまともに機能しなくなっている。
Spacesでのデスクトップの数などはcom.apple.dock.plistに記録されていて、これは現在も有効、かと思ったらデスクトップを増やしても変更が反映されていないからもう使われていないのか。


さらにSpaces関連と思われる2つの.plistが新しくできている。

com.apple.spaces.plist



これはデスクトップ毎のuuid(・・・って何?ですな。ユニークな文字列の識別子らしい)を記録している。増やした分のSpacesもちゃんとあるので、デスクトップの数はこいつで取得すればいいようだ。


com.apple.desktop.plist

こっちはデスクトップ毎の壁紙を記録している。

というわけで現在困っているのがアクティブなデスクトップのNoをどうやって取得するのか。
StackOverflowでも早速同じ趣旨の質問が上がっていて、その回答がDockのclass-dumpで現在遊んでいるので、明日詳しく報告するよ、だった。

OSX Lion AppleScript : How to get current space # from mission control? - Stack Overflow

私もこの回答に習ってDockのclass-dumpをとってみた。WVSpaceとかWVSpaceSwitcherとか、それっぽいクラスがあるなあ。

不幸なことに、こうやって取得したヘッダファイルをどうやって使うのか、素人は全然わからないという。(^^;)

2011年7月19日火曜日

Cocoa NSViewのアニメーション(2)

CATransitionについて引き続き調べる。

おそらく非常に有名なアーティクルなのであろう、

CATransitionでトランジションエフェクトを実行する方法 - プログラミングノート

とか、xcatsan師匠

(旧) Cocoaの日々: スライドトランジション

とかを読んで自分でもコードを書いてみた。
xcatsan師匠によるとViewを2つ用意しておいて、それを「入れ替える」形にすることでTransitionが表示する、らしい。

とりあえず自分で試したのは

1,TransitionをつけたいViewのDrawRectでCATransitionを入れてみる
2,superViewになるViewを用意して、addSubViewしたsubViewが描画されるタイミングでTransitionを入れる

結論からいって両方とも「まず自分自身を描画して、それからTransitionをつける」という形になった。
やはり「入れ替わる」ためのViewがいるのね。

ADCのサンプル「ImageTransition」でも2枚の写真を入れ替えてTransitionを表示しているし。ふむふむ。

その実験は明日以降。

2011年7月18日月曜日

Cocoa NSViewのアニメーション

ファイルマネージャは一旦休止。大まかなアウトラインはできたものの、そこから作り込むにはちょっと大変。Finderで十分、なのだからモチベーション維持も難しい。

そんなわけで、本日はNSViewのアニメーションについて調べていた。CAAnimationやら CATransactionやらのドキュメントを読んでもさっぱりわからないけど。(^^;)

BasicCocoaAnimationsというADCのサンプルコードをコピペしてViewを回してみたりもしたが、今イチうまくいかない。ど派手なエフェクトを作るのは難しそうだなあ。

2011年7月16日土曜日

Cocoa Dockの.plistを読み込む

なぜか本日は「Dock」のplistを読み込んで、表示されているappのリストを取得できるか試した。デベロッパドキュメントのあちこちをさまよっているうちに「Property Lists Programming Guide」を読むハメになり、そのサンプルコードがおもしろかったので。

結論からいうと意外と簡単にできる。

    NSError* error;
    NSPropertyListFormat format;
    NSString *plistPath;
    NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
                                                              NSUserDomainMask, YES) objectAtIndex:0];
    plistPath = [rootPath stringByAppendingPathComponent:@"Preferences/com.apple.dock.plist"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
        NSLog(@"What?");
    }
    NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
    NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization 
                                          propertyListWithData:plistXML options:NSPropertyListMutableContainersAndLeaves format:&format error:&error];
    if (!temp) {
        NSLog(@"Error reading plist: %@, format: %lu", error, format);
    }
    NSArray* appArray=[temp objectForKey:@"persistent-apps"];
    NSDictionary *appData=[appArray objectAtIndex:0];
    //NSLog(@"paaData=%@",appData);
    NSDictionary* tileData=[appData objectForKey:@"tile-data"];
    //NSLog(@"tileData=%@",tileData);
    NSDictionary* fileData=[tileData objectForKey:@"file-data"];
    //NSLog(@"fileData=%@",fileData);
    //NSLog(@"URL=%@",[fileData objectForKey:@"_CFURLString"]);
    NSString *urlString=[fileData objectForKey:@"_CFURLString"];
    NSURL* fileURL=[NSURL fileURLWithPath:urlString];

//    NSURL* fileURL=[NSURL URLWithString:[fileData objectForKey:@"_CFURLString"]];
    NSLog(@"URL=%@",[fileURL path]);

うるさく入っているNSLogは無視。

NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];

でplistを読み込むことができたら、あとは
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
propertyListWithData:plistXML options:NSPropertyListMutableContainersAndLeaves format:&format error:&error];

とやればNSDictionaryに変換できる。ただサンプルではpropertyListFromDataを使っていたが、ドキュメントによるとpropertyListWithDataに移行せよ、ということなので差し替えてある。

plistそのものはXcodeであらかじめ開いて、keyを確認してある状態。
入れ子になったDictionaryって地道に読み込んでいくしかないのかなあ。

上のコードではDockに配された最初のappのURLを取得している。うちの場合は環境設定。


この手のデータが入手できるということは、Dockに登録されたappをあらかじめ表示できるラウンチャ、なんかを作ることができる、ということだな。うーん、どうしよう。

2011年7月15日金曜日

Cocoa NSFontManager

NSFontManagerを使ってFontPanelを出してみた。

相変わらずxcatsan師匠のこんなアーティクルがあって、参考になる。

(旧) Cocoaの日々: フォントを変更する(3) NSFontPanelの変更を受け取る

-(void)changeFont:(id)sender{
    NSFont* changedFont=[sender selectedFont];
    [messageFontTextfield setStringValue:[NSString stringWithFormat:@"%@ %.1f",[changedFont displayName],[changedFont pointSize]]];  
}
てな感じで、変更されたFontをNSTextFieldに表示してみた。

stringWithFormatで、floatの小数点以下の表示をどうするのか、けっこう調べるのに時間がかかった。

%.1f

とすれば小数点以下1桁にできることがわかった。「.1」だとわからなくて難儀した。細かい字が読みづらくなっているからなあ。
fontを変更できるようにしよう、なんていう試みも、自分のこの老化現象が原動力。これからのアプリは老人力が高まっている人むけにしないとだめですよ、はっはっは(^^;)。

2011年7月14日木曜日

Cocoa NSFont


こんな感じでPreference、fontの部を作ってみている。NSTabVeiwがTabのままでToolbarをつけていないのはご愛嬌。そのうち気が向いたらつけるかな。

で、fontのeditボタンを押したらFontDialogを出るようにしよう、として、NSFontやらNSFontManagerやらの勉強中。

いろいろなappでFont選択関係のダイアログがどうなっているのか調べたりなんだり。

XcodeのFont選択関係を調べているうちに、Xcode4のThemeってどうなってんの?と興味をもったのが運の尽き。


「Xcode4 Theme」とかでググったら出てくること。で、調子に乗ってインストールして遊んでみた。
うーん、やっぱり初めからついてくる「MidNight」が私は好きかなあ。TextMateからの移植品らしい「BlackBoard」もけっこう見やすいかも。SyntaxColoringがうまく動いていないことがあるような気がするのは気のせいかそれともXcode4のせいか。

2011年7月13日水曜日

Cocoa 環境設定パネルでFontを指定する

Toolbar上にメッセージを表示する窓をつけたので、そこで表示するfontを環境設定パネルから指定できるようにしてみる。

この手の作業は実はやったことがない。ちょうどいい勉強になりそうだ。
とりあえずファイルを表示するTableViewと、ディレクトリを表示するOutlineViewのfontも設定できるようにしておく、と。
まずはメッセージ窓。

PreferenceControllerと表示するウィンドウであるPanelを作って、MainMenuの「Preferences..」とつなげようとしていろいろ悩む。MainMenu関係ってやったことないから迷ってばかり。

IBActionを書いておけば、どのxibからでもFirstResponderでそのアクションを指定できるようだが、クラスメソッドでないとだめだったりして悩んだ。

結局MainControllerにpreferenceControllerというメンバ変数を持たせることにした。

自分ではけっこうCocoaとかXcodeに慣れてきたかなあ、と思っていたけど、同じ作業をやるのにもあれこれ試行錯誤が必要で時間がいくらあっても足りない感じ。もう少し若くて記憶力のよかったころなら・・なんて思うけれど、考えても仕方のないことなのでどんどん前に進むしかない。

こんな感じでバタバタしている間に、OSX LionがリリースされてXcode4.2だったかがリリースされて、ということになりそうだな。

2011年7月12日火曜日

Cocoa TabFile ローカライズを少し(2)

Xcode4のビルド設定を眺めてみたが、ビルド時にLocalizeStringをどーにかするような便利な設定はないようだ。(いや素人目なので落ちはあるかもしれない)

とすると、ローカライズが必要になりそうな文字列を全部NSLocalizedStringで囲んでおいて、あとで一気に genstrings でLocalizable.stringsを生成する、という一般的な方法がやはり一番いいようだ。

ということで当面、UI関係の文字列をNSLocalizedStringで囲む作業だけしておくことにしよう。
今回はそれがわかっただけで大収穫。

それともうひとつは


このappを試すという方法もあるな。ま、もう少し翻訳対象の文字列が増えてからでも全然いいみたいだけど。

2011年7月11日月曜日

Cocoa TabFile ローカライズを少し(Xcode4)

今回はできるだけ初めから

・ローカライズ
・ユーザデフォルト

を考えて作っていこうと思う。で、最初にローカライズ。自分のプロジェクトでまともにローカライズやったことないのでドキドキですな。

とりあえず、Toolbar上のカスタムViewのメッセージを日本語で表示できるようにしてみる。なにはともあれ、プロジェクトを日本語ローカライズ可能にしてみる。プロジェクトのinfoからローカライズを追加すればいい。


この時点でinfoPlist.stringとMainMenu.xibの日本語版が自動でできあがるが、肝心の(と思われる)Localizable.stringsは姿も形もない。

ちょこちょこググって出てくる情報では、

$genstrings -o

とかを使えということなので早速やってみる。ローカライズ関連のファイルはja.lprojというディレクトリに入れるようだ。TabFileのプロジェクトだと、~HOME/Develop/cocoa/TabFile/TabFile/ja.lprojということになる。で、

$genstrings -o ja.lproj TFToolbarMessageView.m
とやってみるとちゃんとLocalizable.stringsが生成されたが・・・残念ながらこれだけでは日本語化してくれなかった。

当然ここで頭を抱えたわけだが、Xcode4からいろいろ変更があったみたいなので、ちょっと足掻いてみた。

genstringsで生成したLocalizable.stringsはこのままだとプロジェクトに自動的に追加されるわけではない。だから手動でやってみた。


わ、今思いついだんたけど、AddFileしたほうがよかったかも(^^;)。ま、いいや。それでこのダイアログが開くので、

先ほど生成されたLocalizable.stringsを上書きで追加。

これで再ビルドしてみたらちゃんと日本語がでた。


しかしこのままだと、いちいち手動でローカライズする文字列を追加しないといけないのか?それとも

$genstrings -o

で上書きすればいいのだろうか。この辺の実験は明日以降。xibもibtoolでやってみないといけないなあ。ユーザデフォルトとprefarenceをいじる予定なのでちょうどいいか。

2011年7月10日日曜日

Cocoa TabFileプロジェクト開始してみた

新しくTabFileという名前でファイルマネージャのプロジェクトを始めてみた。
実験的にガーベジコレクションを有効にしてみたので、これも途中で頓挫する可能性があるな。ま、いいや。当面、retainとかreleaseとかとお別れである。

細かいところはいつもどおり

詳解 Objective-C 2.0 改訂版
荻原 剛志
ソフトバンククリエイティブ
売り上げランキング: 17767

を参照しながら作る、と。

さて、本日はメインウィンドウにNSToolbarはりつけて、ユーザ向けメッセージを表示するカスタムViewを入れてみた。こんな感じ。


もう、まんまXcodeの下手なマネじゃねえか、という(^^;)。カスタムViewの見た目についてはもう少し研究することにする。今はまだ黒で[path fill]しているだけなので。

NSToolbarの設定の仕方もいちいち忘れていて、以前作りかけたPDFKitを使ったプロジェクトを参照した。その手の書き方をちゃんとこのblogにまとめて書いておくと自分としてはかなり便利なんだけど、なかなかそこまでの時間が取れない。残念。

2011年7月9日土曜日

考える

Tabでディレクトリの中身を表示するファイルマネージャを、もう一度頭から書き直すことにして、どこから作業に入るか考えて過ごす。

自分の書いた(書き散らかした)コードを眺めて、下手だよなあとため息をつくことしばし。

Eseのソースを眺めたり、Skimのプロジェクトをあれこれ眺める。
眺めながら、どーせ自分しか使わないよなあ、などという考えが頭をよぎったりもしますな。(^^;)

本日は休養日、全く何もしないで1日を過ごした感じ。それもまたよし。

追記:
とかなんとか書いたあとにやっぱりプロジェクトをいじっていた(^^;)
Tab関係のbugの原因がやっとはっきりしたのが収穫。

2011年7月8日金曜日

Cocoa ファイルの非同期コピー(9)

ひとつのファイルを非同期コピーするクラスを書く。ざっとこんな感じ。コメントはもともとのソースにあったもの。おおもとのソースは

Cocoa Tutorial: File Copy With Progress Indicator

のはず。

#import "FileAsyncCopyer.h"


@implementation FileAsyncCopyer
@synthesize complateCopyBytes,isCopyDone;
-(id)initWithSorceURL:(NSURL*)sURL destURL:(NSURL*) dURL{
    self=[super init];
    if(self){
        sourceURL=[sURL retain];
        distURL=[dURL retain];
        isCopyDone=NO;
       
    }
    return self;
}

- (void) controlCopyingState:(FSFileOperationRef) fileOp
          FileOperationStage: (FSFileOperationStage) stage
                      Status:(OSStatus) error
               DictionaryRef:(CFDictionaryRef)statusDictionary
{
    
    if (error != noErr){
       
    }
    
 if (statusDictionary)
    {
      
  CFNumberRef bytesCompleted;
  bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary, kFSOperationBytesCompleteKey);
 
 
  CFNumberGetValue (bytesCompleted, kCFNumberMaxType, &complateCopyBytes);
       
        NSLog(@"コピー済み:%@ %f Bytes",[sourceURL path] ,complateCopyBytes);
    }
 
 if (stage == kFSOperationStageComplete) {
        NSLog(@"コピー完了 %@",[sourceURL path]);
        isCopyDone=YES;
    }
}

static void statusCallback (FSFileOperationRef fileOp,
                            const FSRef *currentItem,
                            FSFileOperationStage stage,
                            OSStatus error,
                            CFDictionaryRef statusDictionary,
                            void *info )
{
    FileAsyncCopyer* entry=(FileAsyncCopyer *)info;
    [entry controlCopyingState: fileOp FileOperationStage: stage Status: error DictionaryRef: statusDictionary];
    
}

-(void)startCopyAsync{
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);
    
    OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp, runLoop, kCFRunLoopDefaultMode);
    if( status )
    {
        NSLog(@"Failed to schedule operation with run loop: %@", status);
        return;
    }
  
    // Create a filesystem ref structure for the source and destination and
    // populate them with their respective paths from our NSTextFields.
    FSRef source;
    FSRef destination;
    
    FSPathMakeRef( (const UInt8 *)[[sourceURL path] fileSystemRepresentation], &source, NULL );
    
    Boolean isDir = true;
    FSPathMakeRef( (const UInt8 *)[[distURL path] fileSystemRepresentation], &destination, &isDir );    
    
    // Start the async copy.
    FSFileOperationClientContext clientContext = {0};
    clientContext.info = self;
    status = FSCopyObjectAsync (fileOp,
                                &source,
                                &destination, // Full path to destination dir
                                NULL, // Use the same filename as source
                                kFSFileOperationDefaultOptions,
                                statusCallback,
                                1.0,
                                &clientContext);
    
    CFRelease(fileOp);
    
    if( status )
    {
        NSLog(@"Failed to begin asynchronous object copy: %@", status);
    }

}
- (void)dealloc
{
    [sourceURL release];
    [distURL release];
    [super dealloc];
}

@end

このクラスにコピー元、コピー先のURLを渡して非同期コピーをする。 FSCopyObjectAsync を起動する以外は、コピー済みbytesを記録するだけ。

現在、動作を確認中。 FSCopyObjectAsync はコピー元としてディレクトリを渡したら、ディレクトリを1つのファイルのように扱ってもらえるようだ。

コピー元がディレクトリの場合、ディレクトリの中を再帰的に探索していってコピーするべきかどうか、とか、コピー済みbytesは正確に得られるかとかの検証は明日以降。

2011年7月7日木曜日

Cocoa ファイルの非同期コピー(8)

ArukanSoftの中の人から「ファイルを一つずつコピーすればいいのでは?」と教えていただいて驚天動地。本当にそうだわなあ。全然思いつかなかった。コピー用に複数のファイルが渡されたら、それを素直にforループ回してコピー、としか考えなかった。

いかん、老化現象かそれとも単に素人だからか、頭が固い。もっと柔らかくいろいろ考えられるようになりたい。

というわけで、昨日わからなくて首をかしげていたNSTreeControllerの件は、モデルクラスでreloadChildrenという強制的にディレクトリ以下を読み直すメソッドを書き足してちゃっちゃと既決箱に放り込む。

で、FSCopyObjectAsyncのラッパクラスと、それを呼び出すCopyManagerを書くことにする。しかし、これはノートに手書きであれこれアイディアを書いたところで終了。
むかーしむかし、会議中のひまな時間は提案文書の片隅に、プログラミングのアイディアを書き留めていたなあ。思考を整理するには手書きメモが一番有効でございますな、私の場合。

コピーしたい複数ファイルとコピー先URLを受け取って、ダイアログを表示したりするのがManagerクラス、実際のコピーを担当するのがラッパクラス、として、それぞれどんなふうに働かせたらいいだろう。明日以降考えてみる、と。

2011年7月6日水曜日

Cocoa NSTreeController ChildNodeの更新・わからず

現在作っているファイルマネージャのようなもの、外見はこうなっている。


左側NSOutlineView、右側NSTableView、というWindowsでいうところのエクスプローラスタイル2ペインウィンドウでありますな。

TableViewからファイルなりディレクトリなりをDragして、他のTabの上にもっていくとそのTabで表示しているディレクトリがアクティブになってDropできる、という仕様。

Dropされた後はSCEventが「ファイルシステムに変更があったぞよ」と通知してくれるので、通知されたNSTableViewは自分自身を更新する。

・・・しかし、NSOutlineViewの方が困った。ファイルシステムはNSTreeControllerでモデルを操作するようにして、NSOutlineViewは表示を受け持っているだけ。だから「reloadItem」は当然使えない。

へ?どうするんだろう、というところで時間切れ。現在ADCサンプルのDragNDropOutlineViewを読んだりしている。

2011年7月5日火曜日

Cocoa bug噴出

ドバーッと一気にいろいろ不具合があるのがわかった。

なんせ今まで、ファイルシステムの変更をリアルタイムにアプリで更新できるようにしていなかった。ところがDrag&Dropでファイルのコピーができるようになって、しかも非同期コピーの実験を繰り返したものだから、ファイルシステム更新関係のbugが次々に現れる。

そろそろ、ちゃんと書きなおす時期に来ているかな、という感じ。とりあえず動けばいい、というコードがたくさんあるので、自分なりに「せめてここまで」と考えるラインまでちゃんと動作を確認しながら書き直すかな。

Tabがらみのbugもあることはわかっていて原因がわからないものがあるので、その辺りからか。
ついでにGC有効にして書いてみるもの一興か。

それとあとはTestだよなー、と考える素人。ObjCでTest書く、なんて参考資料あるのかしらん。

追記:
OCUnitの解説があちこちにあった。(^^;)
ちょこっと勉強してみよう。

2011年7月4日月曜日

Cocoa ファイルの非同期コピー(7)Copyの進捗状況を表示する

たくさんあちこちにbugを含みつつ、とりあえずこんな感じのダイアログを表示しようとしている。


インジケータにコピーしたbytesを表示していくわけだが、これがどうもうまくいかないので調べてみる。

FSCopyObjectAsyncのcallbackには自クラスのselfを引数で渡して、自クラスのメソッドをさらにcallbackする。このほうがIBOutletとかをいじるのが簡単だし、なにかとCocoaスタイルのほうが初心者には都合がいい。で、そのcallbackのcallbackメソッドの中で

bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary, kFSOperationBytesCompleteKey);
という風にコピー済みbyteを得ているが、複数ファイルをいっぺんに非同期コピーしていると「どのファイルが、どれだけコピー済みか」がわからない。
そこでcallbackの中でコピー中のファイルのURLを得るようにして、それをcallbackのcallback(・・ええい、面倒くさい)に渡すようにしてみた。

だからcallbackのcallbackはこうなっている。
          FileOperationStage: (FSFileOperationStage) stage
                      Status:(OSStatus) error
               DictionaryRef:(CFDictionaryRef)statusDictionary
                  currentURL:(NSURL*)aURL{
最後のaURLが現在コピー中のファイルのURL。で、callbackが呼ばれるたびにこのURLの中身をNSLogしてみると、なんと、「コピー元」と「コピー先」の両方のファイルについて、それぞれcallbackが呼ばれていることがわかった。

つまり、

/hoge/hoge.txt

から

/hage/hoge.txt

にコピーするとして、コピー元/hoge/hoge.txtが100Kbytes分転送されたらcallbackが呼ばれ、コピー先/hage/hoge.txtが100K転写されたらもう1回callbackが呼ばれ、単純に「コピー済みbytes」を合計すると2倍の数値を得ることになってしまう。だからインジケータがすぐに動かなくなるのか。

そこで

[copyComplatedbytesOfFiles setObject:compBytes forKey:[[aURL path] lastPathComponent]];

みたいなNSMutableDictionaryを作っておいて、hoge.txtが何byte転送済みかを記録しておくことにしてみた。

で、
for(NSNumber* num in bytesArray){
allFileComplatedByte+=[num doubleValue];
}

辞書内のコピー済みbytesを集計してインジケータに設定する、と。

たったこれだけのことだけど、思った通りの動作をしてうれしい。問題はコードがめちゃくちゃ汚いことだな。(^^;)書きなおそう。

あとはこの動作全体をNSOperationに入れてやればいいのだろうが、非同期コピーなので大きなファイルをコピー中に他のファイルをコピーしても十分動作する。これでいいかなあ。
問題はダイアログがぴったり重なって表示されることだなあ。