ホームページ へ
更新記録 へ
Cocoa で作る「ちょっとしたプログラム」 へ
− 内容に不完全なところが多数見つかったので全面改定しました −
− ( 過去のTipsも見てみる ) −
− ( 過去のTips2も見てみる ) −
も く じ
設 計
例えば、以下のような見た目で、ボタンを押すと、あらかじめ内部に持っている画像(私のメインページから取ってきたカボチャの画像「k-diary.gif」)がパッと NSImageView の画面に現れるようなものを作ります。
コーディング
画像を扱うときはNSImageを使い、これにファイルで指定した画像を取り込むやり方は、「Cocoaはヤッパリ!」のあちこちで紹介されていますので、これは良く解ります。画像ファイルはプログラムの内部に持つと言うことで、アイコンと同様のやり方で「Project Builder」に登録します。このやり方もバッチリです。ところが問題は、「このように自分で持っているファイルにアクセスするやり方は?」と言う、基本の基本が以外と盲点です。
#import "MyObject.h"
@implementation MyObject
- (IBAction)myAction:(id)sender
{
NSImage *img;
img = [ NSImage imageNamed: @"k-diary" ];
[ myOutlet setImage: img ];
}
@end
|
設 計
ボタンを押すも何もなくて、最初っからあらかじめ内部に持っている画像(ここではカボチャ)が NSImageView の画面に現れるようなものを作ります。
設計1.「Interface Builder」で最初から貼付ける
「Interface Builder」で NSImageView を貼付けたとき、この画像が「Project Builder」に登録してあれば、これをドラッグして貼付けることができます。このやり方では、コーディングは一切不要です。設計2.デリゲートで貼付ける
例えばNSObjectのサブクラスにMyObjectを作成し、アウトレットを加えてコード化します。またこれをコロッとインスタンス化して、アウトレットをウインドウ上の NSImageView と配線します。また、File'sOwner からこのインスタンスへ delegate として配線を張ります。コーディングに関しては下を参照して下さい。設計3.NSImageView を改造する
NSImageViewのサブクラスにMyImageViewを作成しコード化します。またウインドウ上にはいったん CustomView 貼付けて、そのインフォを表示して Attributes を MyImageView に切り替えます。これによって、全く設定のされていない NSImageView のサブクラスが張り付いた形になります。コーディングをしないと、実行時には何も見えません。コーディングに関しては下を参照して下さい。設計4.NSImageView を改造する2(これが本命か)
NSImageViewのサブクラスにMyImageViewを作成しコード化します。またウインドウ上には普通にNSImageView を貼付けて、そのインフォを表示して Custom Class を MyImageView に切り替えます。こうすると、NSImageView のデザインは Interface Builder を使って簡単に出来ますが、initWithFrame: メソッドが使えなくなるので、初期化が必要な場合にはわざわざ設計3.を使うものなのかと思っていました。しかしCocoaはそんな間抜けではありませんでした。awakeFromNib と言う初期化メソッドがちゃんと用意されていたのです。(この設計4.は、2002.4.3に発見)コーディング(設計2.の場合)
先の「ボタンを押すと画像がでる」と全く同じコーディングになります。変わったのは1行だけですので、そこ以外は敢えて読まなくても良いです。ボタンに対応したアクションではなく、File'sOwner の delegate の applicationDidFinishLaunching メソッドの動作になっただけと言うことです。実行してみると、一瞬遅れて画像が現れることが解ります。
#import "MyObject.h"
@implementation MyObject
- (void)applicationDidFinishLaunching: (NSNotification *)aNote
{
NSImage *img;
img = [ NSImage imageNamed: @"k-diary" ];
[ myOutlet setImage: img ];
}
@end
|
#import "MyObject.h"
@implementation MyObject
- (void)applicationDidFinishLaunching: (NSNotification *)aNote
{
[ myOutlet setImage: [ NSImage imageNamed: @"k-diary" ] ];
}
@end
|
コーディング(設計3.の場合)
NSImageView に関して、全く独自に設定をすることになります。これは、initWithFrame メソッドの中で行います。NSImageView の場合であれば、お決まりのブロックは除外して考えると、見た目のデザインのために新しく設定しているのは setImageFrameStyle の1行だけですが、View によってはもっと複雑な設定が必要となるものもあるでしょう。
#import "MyImageView.h"
@implementation MyImageView
-(id)initWithFrame: (NSRect) frameRect
{
[ super initWithFrame: frameRect ];
[ self setImageFrameStyle: NSImageFrameGrayBezel ];
[ self setImage: [ NSImage imageNamed: @"k-diary" ] ];
return self;
}
@end
|
コーディング(設計4.の場合)
この場合は、awakeFromNib と言う初期化メソッドの中に、追加分の画像張り付けの処理だけを書けばよいです。設計2.のシンプルさと、設計3.の初期化処理機能を合わせ持ち、実行時に画像の現れに遅れはなく、これが Interface Builder の開発者の意図した使い方のようです。Nib の無い時代からの古風な人は、initWithFrame: を使った設計3.を使うので、サンプルソフトには設計3.が多いようですが。
#import "MyImageView.h"
@implementation MyImageView
-(void)awakeFromNib
{
[ self setImage: [ NSImage imageNamed: @"k-diary" ] ];
}
@end
|
設 計
ボタンを押すのも最初っから張り付いているのも、いま一つプログラムを書いた快感に乏しいですが、ドラッグして張り付いたとなると、それだけでも結構感動的(おおげさかな)なのではないでしょうか。任意のファイルを貼付ける技さえ取得すれば、その後はその画像を煮るなり焼くなり、いろいろなプログラムに発展できるような気がしてきます。コーディング
ドラッグされたファイルを見るには「Pasteboard」がキモになります。ドラッグされたものから「Pasteboard」と言うものを手に入れて、これからファイル名や画像データなどを取り出して使うと言うものです。プログラム中では「pbd」と言う名前で扱って見ましたので、これが出てくるところだけ読めば、なるほどと解るかと思います。 まずは .h ファイルを見てみます。インスタンス変数を4つ使っていますが、見れば意味が解ると思います。
メソッドは初期化メソッドを含む必須の7つですと言うことで、従来は全部 .h ファイルに書くようにしてました。もちろん書いていけない訳ではありませんが、書くのが面倒なのも事実です。この7つのメソッドはシステムが定義してシステムが呼ぶものですので、将来的なこのファイルの利用を考えても、他のファイルがこの .h ファイルを参照したときにこの7つのメソッドのことを教える必要はありません。と言う訳で、すっきりと省略することにしました。
/* MyImageView */
#import <Cocoa/Cocoa.h>
@interface MyImageView : NSImageView
{
NSPasteboard *pbd;
NSString *path;
NSImage *img;
BOOL dropSign;
}
@end
|
#import "MyImageView.h"
@implementation MyImageView
-(void)awakeFromNib
{
[ self registerForDraggedTypes: [ NSArray arrayWithObjects: NSTIFFPboardType, nil ] ];
dropSign = NO;
}
-(unsigned int)draggingEntered: (id
|
ここまで、2002.4.16 に改定しました。
設 計
上でうまく画像が張付いたので、この調子で同じようにと思って、任意のファイルをドラッグしてテキストボックスに放り込むと、テキストボックスにそのファイルのパスが書き込まれるのをやってみました。
苦 労 談
テキストボックスにドラッグする場合でも、上で書いたプログラムがあるからこれを流用してちょっと変更と思っていたら、これが一筋縄では行かなくて何日も苦労してしまいました。変更箇所が多かったので、あえてここに新しいTipsとして書いてみます。コーディング
まずは .h ファイルですが、これはあえて書くまでもありませんね。上と同じに、書かなくても動作する宣言は省略することにしました。
/* MyTextView */
#import <Cocoa/Cocoa.h>
@interface MyTextView : NSTextView
{
NSPasteboard *pbd;
NSString *path;
BOOL dropSign;
}
@end
|
#import "MyTextView.h"
@implementation MyTextView
-(void)awakeFromNib
{
[ self registerForDraggedTypes: [ NSArray arrayWithObjects: NSFilenamesPboardType, nil ] ];
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
selector : @selector(myBounds:)
name : NSWindowDidUpdateNotification
object : [ self window ]
];
dropSign = NO;
}
- (void)myBounds:(NSNotification *)aNotification
{
NSRect aRect;
float c;
[ [ self superview ] lockFocus ];
aRect = [ self frame ];
aRect = NSOffsetRect( aRect, 0.5, 0.5);
c = 0.9; [ [ NSColor colorWithDeviceRed:c green:c blue:c alpha:1.0 ] set ];
NSFrameRectWithWidth( aRect , -2.5 );
c = 0.6; [ [ NSColor colorWithDeviceRed:c green:c blue:c alpha:1.0 ] set ];
NSFrameRectWithWidth( aRect , -1.0 );
c = 0.9; [ [ NSColor colorWithDeviceRed:c green:c blue:c alpha:1.0 ] set ];
NSFrameRectWithWidth( aRect , 1.0 );
[ [ self superview ] unlockFocus ];
[ self display ];
}
-(void) setDropSign: (BOOL) ds
{
dropSign = ds;
if( dropSign )
[ self setBackgroundColor: [ NSColor lightGrayColor ] ];
else
[ self setBackgroundColor: [ NSColor whiteColor ] ];
[ self display ];
}
-(unsigned int)draggingEntered: (id) sender
{
NSArray *arOfFilename;
arOfFilename = [ [ sender draggingPasteboard ] propertyListForType: NSFilenamesPboardType ];
// ファイル名配列を取得
if ( [ arOfFilename count ] > 1 )
return NSDragOperationNone; // 複数なら拒否!
path = [ arOfFilename lastObject ]; // ファイルへのパスを取得
[ path retain ];
[ self setDropSign: YES ]; // 反応を表示
return NSDragOperationGeneric; // ドロップOK!
}
-(unsigned int)draggingUpdated: (id) sender
{
if( dropSign )
return NSDragOperationGeneric; // ドロップOK!
else
return NSDragOperationNone; // 拒否!
}
-(void)draggingExited: (id) sender
{
[ self setDropSign: NO ]; // 反応を消す
[ path release ];
}
-(BOOL)prepareForDragOperation: (id) sender
{
return YES; // ドロップOK!
}
-(BOOL)performDragOperation: (id) sender
{
[ self setString: path ];
[ self display ];
return YES; // ドロップOK!
}
-(void)concludeDragOperation: (id) sender
{
[ self setDropSign: NO ]; // 反応を消す
[ path release ];
}
@end
|
ここまで、2002.4.16 に改定しました。
設 計
順番が後先になって申し訳ありませんが、テキストを張り付けた話の後で、もう一度画像をドラッグして貼付ける話題を追加します。コーディング
まずは .h ファイルですが、これもあえて書くまでもありませんね。
/* MyView */
#import <Cocoa/Cocoa.h>
@interface MyView : NSView
{
NSPasteboard *pbd;
NSString *path;
NSImage *img;
BOOL dropSign;
}
@end
|
#import "MyView.h"
@implementation MyView
-(void)awakeFromNib
{
[ self registerForDraggedTypes: [ NSArray arrayWithObjects: NSFilenamesPboardType, nil ] ];
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
selector : @selector(myBounds:)
name : NSWindowDidUpdateNotification
object : [ self window ]
];
dropSign = NO;
}
- (void)myBounds:(NSNotification *)aNotification
{
NSRect aRect;
float c;
[ [ self superview ] lockFocus ];
aRect = [ self frame ];
aRect = NSOffsetRect( aRect, 0.5, 0.5);
c = 0.9; [ [ NSColor colorWithDeviceRed:c green:c blue:c alpha:1.0 ] set ];
NSFrameRectWithWidth( aRect , -2.5 );
c = 0.6; [ [ NSColor colorWithDeviceRed:c green:c blue:c alpha:1.0 ] set ];
NSFrameRectWithWidth( aRect , -1.0 );
c = 0.9; [ [ NSColor colorWithDeviceRed:c green:c blue:c alpha:1.0 ] set ];
NSFrameRectWithWidth( aRect , 1.0 );
[ [ self superview ] unlockFocus ];
[ self display ];
}
- (void)drawRect:(NSRect)aRect
{
if( dropSign ){
[ [ NSColor lightGrayColor ] set ];
NSRectFill( aRect );
}else{
[ [ NSColor whiteColor ] set ];
NSRectFill( aRect );
if( img )
[ img compositeToPoint:NSMakePoint(0.0,0.0) operation:NSCompositeCopy ];
}
}
-(void) setDropSign: (BOOL) ds
{
dropSign = ds;
[ self display ];
}
-(unsigned int)draggingEntered: (id) sender
{
NSArray *arOfFilename;
arOfFilename = [ [ sender draggingPasteboard ] propertyListForType: NSFilenamesPboardType ];
// ファイル名配列を取得
if ( [ arOfFilename count ] > 1 )
return NSDragOperationNone; // 複数なら拒否!
path = [ arOfFilename lastObject ]; // ファイルへのパスを取得
[ path retain ];
if ( [ [ path pathExtension ] caseInsensitiveCompare: @"Tiff" ] != NSOrderedSame )
return NSDragOperationNone; // Tiff以外は拒否!
[ self setDropSign: YES ]; // 反応を表示
return NSDragOperationGeneric; // ドロップOK!
}
-(unsigned int)draggingUpdated: (id) sender
{
if( dropSign )
return NSDragOperationGeneric; // ドロップOK!
else
return NSDragOperationNone; // 拒否!
}
-(void)draggingExited: (id) sender
{
[ self setDropSign: NO ]; // 反応を消す
[ path release ];
}
-(BOOL)prepareForDragOperation: (id) sender
{
return YES; // ドロップOK!
}
-(BOOL)performDragOperation: (id) sender
{
img = [ [ NSImage alloc ] initWithContentsOfFile: path ];
[ self display ];
return YES; // ドロップOK!
}
-(void)concludeDragOperation: (id) sender
{
[ self setDropSign: NO ]; // 反応を消す
[ path release ];
}
@end
|
ここまで、2002.4.16 に追加しました。
[ NSFont fontWithName: @"HiraKakuPro-W3" size: 36 ]
|
ここまで、2002.5.10 に追加しました。
ここまで、2002.5.18 に追加しました。
MyWindow.h がこれ。
/* MyWindow */
#import <Cocoa/Cocoa.h>
@interface MyWindow : NSWindow
{
IBOutlet id myOutlet;
}
- (void)close;
@end
|
#import "MyWindow.h"
@implementation MyWindow
- (void)close
{
[ super close ];
[ myOutlet terminate:self ];
}
@end
|
ここまで、2002.5.29 に追加しました。
NSString *adr = @"/Users/makuri/aaa.txt";
NSFileManager *fman = [ NSFileManager defaultManager ];
[ fman removeFileAtPath: adr handler: nil ];
|
NSString *adr = @"/Users/makuri/あああ.txt";
NSFileManager *fman = [ NSFileManager defaultManager ];
[ fman removeFileAtPath: adr handler: nil ];
|
NSString *adr = [ NSString stringWithCString: "/Users/makuri/あああ.txt" ];
NSFileManager *fman = [ NSFileManager defaultManager ];
[ fman removeFileAtPath: adr handler: nil ];
|
ここまで、2003.1.6 に追加しました。