Cocoaサンプル - ファイルダイアログ
※注意:以下紹介するサンプルはMacOSX10.6以降対応ですので注意してください。
サンプル例:
# Fileメニュー>Openを選択するとファイル選択ダイアログが表示され、 Openボタンを押すと選択したファイルパスをコンソールに表示する。 Cancelを押すと何も行わない。txtファイルのみ選択することができる。 # Fileメニュー>Save Asを選択して保存ファイル名を入力するとファイル選択ダイアログが表示され、 Saveボタンを押すと選択したファイルパスをコンソールに表示する。 Cancelを押すと何も行わない。txtファイルのみ選択することができる。
CocoaアプリケーションプロジェクトにFileDialogControllerクラスを追加します。FileDialogControllerにはOpen, SaveAsメニュー選択時のActionを定義します。
Actionの定義とViewとの接続
FileDialogController.h
#import <Foundation/Foundation.h> @interface FileDialogController : NSObject - (IBAction)onSelectSaveAs:(id)sender; - (IBAction)onSelectOpen:(id)sender; @end
MainMenu.xibにFileDialogControllerオブジェクトを追加、Recived Actions>onSelectSaveAsとSaveAsメニュー、onSelectOpenとOpenメニューを接続します。
実装
FileDialogController.m
- (IBAction)onSelectSaveAs:(id)sender { NSSavePanel *savePanel = [NSSavePanel savePanel]; NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"txt",@"'TEXT'",nil]; // NSSavePanel interface has changed since Mac OSX v10.6. [savePanel setAllowedFileTypes:allowedFileTypes]; NSInteger pressedButton = [savePanel runModal]; if( pressedButton == NSOKButton ){ // get file path (use NSURL) NSURL * filePath = [savePanel URL]; // save file here NSLog(@"file saved '%@'", filePath); }else if( pressedButton == NSCancelButton ){ NSLog(@"Cancel button was pressed."); }else{ // error } } - (IBAction)onSelectOpen:(id)sender { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"txt",@"'TEXT'",nil]; // NSOpenPanel interface has changed since Mac OSX v10.6. [openPanel setAllowedFileTypes:allowedFileTypes]; NSInteger pressedButton = [openPanel runModal]; if( pressedButton == NSOKButton ){ // get file path NSURL * filePath = [openPanel URL]; // open file here NSLog(@"file opened '%@'", filePath); }else if( pressedButton == NSCancelButton ){ NSLog(@"Cancel button was pressed."); }else{ // error } }
パネルを表示するにはrunModal、ファイルタイプを制限するにはsetAllowedFileTypesメソッドに拡張子の文字列配列を渡します。ちなみにファイルが存在するディレクトリのパスはdirectoryURLメソッドで取得できます。
Androidサンプル - 音を鳴らす
AudioTrackクラスを使って正弦波を鳴らしてみます(Android SDK 2.2で作成)。手順は以下の通りです。
オーディオバッファを作成する
short buffer[] = null; buffer = new short[ SAMPLES * CHANNEL ]; buffer = generateBuffer(); // 信号を生成する関数
正弦波を生成する式です。
a:振幅、t:時刻、f:周波数、fsサンプル周波数、i:現在のサンプル、N:全サンプル数→周期ごとに元の位相に戻す方がよい
signal = amplification * Math.sin(2.0 * Math.PI * frequency * t);
インスタンスを生成する
各引数は以下の通りです。
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, // AudioMangerのモード SAMPLERATE, //サンプル周波数[Hz] AudioFormat.CHANNEL_CONFIGURATION_MONO, // チャネル数 AudioFormat.ENCODING_PCM_16BIT, //ビットレート[bit] bufferSizeInBytes, //バッファサイズ[byte] AudioTrack.MODE_STATIC); // AudioTrackの再生モード
AudioTrackの再生モードにはMODE_STATICとMODE_STREAMの2種類あって処理の流れが異なるので注意してください。
バッファを書き込む
writeメソッドを使います。
audioTrack.write(buffer, offset, buffeSize);
バッファを再読み込みする
一旦停止してから再度再生する前にもう一度バッファを読み込む必要があるようです。なので再生前にreloadStaticDataを実行しておきます。
再生する
playメソッドで再生します。現在の再生状態を取得するにはgetPlayStateを使います。
停止する
stopメソッドで停止します。
バッファを解放する
アプリが終了するときに、バッファを解放しないといけないのでreleaseを呼んでおきます。
以上が大まかな流れになります。
タッチダウンで再生、タッチアップで停止します。
AudioTestActivity.java
public class AudioTestActivity extends Activity { private static final String TAG = "AudioActivity"; private AudioTrack audioTrack = null; protected short[] buffer = null; protected int SAMPLERATE = 44100; // [Hz] protected static final int CHANNEL = 1; // 1:MONO, 2:STEREO protected static final int BITRATE = 16; // [bit/sec] // signal funcion params static private double amplification = 0.5; // [0.0, 1.0] static private double frequency = 720; // [Hz] static private double duration = 1.0; // [sec] @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); generateBuffer(); int bufferSizeInBytes = buffer.length * CHANNEL * BITRATE / 8; // cf // Log.v(TAG, "length:" + buffer.length); // Log.v(TAG, "bufferSize:" + bufferSizeInBytes); // create AudioTrack instance audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLERATE, //[Hz] AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, //[bit] bufferSizeInBytes, //[byte] AudioTrack.MODE_STATIC); // write buffer audioTrack.write(buffer, 0, buffer.length); } @Override public void onStart() { super.onStart(); } @Override public void onStop() { if(buffer != null){ buffer = null; } if(audioTrack != null){ audioTrack.stop(); audioTrack.release(); // release buffer audioTrack = null; } super.onStop(); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if(audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING){ audioTrack.stop(); audioTrack.reloadStaticData(); audioTrack.play(); } break; case MotionEvent.ACTION_UP: if(audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING){ audioTrack.stop(); } break; } return true; } public void generateBuffer(){ int SAMPLES = (int) (duration * SAMPLERATE); buffer = new short[ SAMPLES * CHANNEL ]; double signal = 0; for (int i = 0; i < SAMPLES; i++) { signal = generateSignal(i); buffer[i] = (short)( signal * Short.MAX_VALUE ); } } public double generateSignal(int sample){ double t = (double)(sample) / SAMPLERATE; // y = a * sin (2PI * f * t), t = i/fs, 0 <= i < TotalSamples return amplification * Math.sin(2.0 * Math.PI * frequency * t); } }
Androidサンプル - OpenGLES1による描画
GLSurfaceViewを利用します。使い方は自作RendererをGLSurfaceViewに設定して、setContentViewでActivityに渡します。
OpenGLTestActivity.java
public class OpenGLTestActivity extends Activity { GLSurfaceView glSurfaceView = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); glSurfaceView = new GLSurfaceView(this); glSurfaceView.setRenderer(new MyRenderer()); setContentView(glSurfaceView); } }
MyRendererはRendererインターフェースを継承させて、onSurfaceCreated(初期化)、onDrawFrame(描画処理)、onSurfaceChanged(リサイズ時)を実装します。
MyRenderer.java
public class MyRenderer implements Renderer{ // size of types static final int SIZE_OF_FLOAT = 4; static final int SIZE_OF_BYTE = 1; // for vertex buffer static private FloatBuffer vertexBuffer; static final int VERTEX_DIMENSION = 3; static final int NUM_VERTICES_OR_INDICES = 4; // for color buffer static private FloatBuffer colorBuffer; static final int COLOR_DIMENSION = 4; // for index buffer static private ByteBuffer indexBuffer; // for current stroke color class Color{ public float r; public float g; public float b; public float a; public Color(float red, float green, float blue, float alpha){ r = red; g = green; b = blue; a = alpha; } } protected Color currentColor = null; public MyRenderer() { currentColor = new Color(0.0f, 0.0f, 0.0f, 1.0f); // default color } public void onSurfaceCreated(GL10 gl, EGLConfig config){ } public void onDrawFrame(GL10 gl){ gl.glClearColor(0.0f, 0.2f, 0.1f, 1.0f); // default background color gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // View Transform gl.glMatrixMode(GL10.GL_MODELVIEW);gl.glLoadIdentity(); // Modeling Transform gl.glMatrixMode(GL10.GL_MODELVIEW);gl.glLoadIdentity(); fillRect(gl, 0.0f, 0.0f, 1.0f, 1.0f); } public void onSurfaceChanged(GL10 gl, int width, int height){ // view port transform gl.glViewport(0, 0, width, height); // projection transform gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); // default } void fillRect(GL10 gl,float x, float y, float width, float height){ { // allocate vertex buffer ByteBuffer byteBuffer = ByteBuffer.allocateDirect(NUM_VERTICES_OR_INDICES * VERTEX_DIMENSION * SIZE_OF_FLOAT); byteBuffer.order(ByteOrder.nativeOrder()); vertexBuffer = byteBuffer.asFloatBuffer(); // set raw data vertexBuffer.put(new float[]{ x + width, y, 0.0f, x, y, 0.0f, x + width, y + height, 0.0f, x, y + height, 0.0f }); vertexBuffer.position(0); } { // allocate color buffer ByteBuffer byteBuffer = ByteBuffer.allocateDirect( NUM_VERTICES_OR_INDICES * COLOR_DIMENSION * SIZE_OF_FLOAT); byteBuffer.order(ByteOrder.nativeOrder()); colorBuffer = byteBuffer.asFloatBuffer(); // set raw data float colorVertices[] = new float[NUM_VERTICES_OR_INDICES *COLOR_DIMENSION]; int vertexId = 0; for( int i = 0; i< NUM_VERTICES_OR_INDICES; i++ ){ colorVertices[ vertexId++ ] = currentColor.r; colorVertices[ vertexId++ ] = currentColor.g; colorVertices[ vertexId++ ] = currentColor.b; colorVertices[ vertexId++ ] = currentColor.a; } colorBuffer.put(colorVertices); colorBuffer.position(0); } { // create index buffer indexBuffer = ByteBuffer.allocateDirect(NUM_VERTICES_OR_INDICES * SIZE_OF_BYTE); indexBuffer.put(new byte[]{ 0, 1, 2, 3 }); indexBuffer.position(0); } gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(VERTEX_DIMENSION, GL10.GL_FLOAT, 0, vertexBuffer); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); gl.glColorPointer(COLOR_DIMENSION, GL10.GL_FLOAT, 0, colorBuffer); // draw arrays according to the order of indices; gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, NUM_VERTICES_OR_INDICES, GL10.GL_UNSIGNED_BYTE, indexBuffer); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisableClientState(GL10.GL_COLOR_ARRAY); } }
注意点
・allocateDirectでRawデータを格納する領域を確保している。
・asFloatBufferでFloatに変換
・putした後、カレントポジションを戻すこと
・VERTEX_DIMENSIONなどはvertexBuffer.lengthとしてもOK
Cocoaサンプル - OpenGLで描画
Cocoaアプリケーションプロジェクトをプロジェクト名"CocoaOpenGL"で作成しておきます。
ライブラリの追加
TARGET>Build Phases>Link Binary With Libraries>"+">OpenGL.frameworkをプロジェクトに追加します。
OpenGLViewの作成
MainMenu.xib>Object LibraryからOpenGL Viewを選択、ウインドウにドラッグドロップ>Windowと同じサイズにする
自作OpenGLViewクラスの追加
NSOpenGLViewクラスを継承したMyOpenGLViewクラスをプロジェクトに追加します。NSOpenGLViewに用意されている次のインターフェースを定義しておきます。initWithFrameが初期化、drawRectが描画処理部分、reshapeはリサイズ時の処理です。
MyOpenGLView.h
#import <AppKit/AppKit.h> @interface MyOpenGLView : NSOpenGLView - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format; - (void)awakeFromNib; - (void)drawRect:(NSRect)rect; - (void)reshape; @end
自作ViewとOpenGLViewを関連付ける
Interface BuilderでOpenGLViewを選択>Identity Inspector>Custom Class>MyOpenGLViewにする。
自作Viewの実装
NSOpenGLViewを使ってインスタンス化した場合は初期化処理でinitWithFrameではなくawakeFromNibが呼び出されますのでこちらに初期化処理を記述しておきます。(ちなみに描画処理部分はdrawRectですが、オブジェクトを描画する関数を分けて実装しています。)
MyOpenGLView.h
#import <AppKit/AppKit.h> @interface MyOpenGLView : NSOpenGLView - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format; - (void)awakeFromNib; - (void)drawRect:(NSRect)rect; - (void)reshape; -(void)drawScene; @end
MyOpenGLView.m
#import "MyOpenGLView.h" #import <OpenGL/gl.h> // #import <OpenGL/glu.h> @implementation MyOpenGLView - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format { self = [super initWithFrame:frameRect]; if (self) { // initialize } return self; } // xibにViewを配置しているときはinitWithFrameが呼ばれないので注意 - (void)awakeFromNib { NSLog(@"initialize"); } - (void)drawRect:(NSRect)rect { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // default background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // View Transform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Modeling Transform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); [self drawScene]; glFlush(); } - (void)reshape { int width = [self frame].size.width; int height = [self frame].size.height; // view port transform glViewport(0, 0, width, height); // projection transform glMatrixMode(GL_PROJECTION); glLoadIdentity(); // default } - (void)drawScene { glColor3f(0.7f, 0.2f, 0.2f); glBegin(GL_TRIANGLES); { glVertex3f( 0.0f, 0.0f, 0.0f); glVertex3f( 0.5f, 0.0f, 0.0f); glVertex3f( 0.0f, 0.5f ,0.0f); } glEnd(); } @end
カスタムViewを使う方法
プロジェクトを作成し、OpenGL.frameworkを追加しておきます。
CustomViewの作成
MainMenu.xib>Object LibraryからCustom Viewを選択、ウインドウにドラッグドロップ>Windowと同じサイズにする
自作Viewクラスの追加
今度はNSOpenGLViewクラスを継承したMyCustomViewクラスをプロジェクトに追加します。
MyCustomView.h
@interface MyCustomView : NSOpenGLView { NSOpenGLContext* glContext; } - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format; - (void)awakeFromNib; - (void)prepareOpenGL; - (void)drawRect:(NSRect)rect; - (void)reshape; @end
自作ViewとCustomViewを関連付ける
Interface BuilderでCustomViewを選択>Identity Inspector>Custom Class>MyCustomViewにする。
ここからMyCustomViewを実装します。
初期化
先ほどとちがってInterface BuilderでNSOpenGLViewを作成していないので、xibでNSOpenGLViewがインスタンス化されていません。今回は初期化でinitWithFrameが実行され、逆にawakeFromNibは呼ばれません。初期化処理では、ピクセルフォーマットの設定、レンダリングコンテキストの設定を行います。さらに、初期化処理を実行すると内部でセレクタが呼び出され、prepareOpenGLが自動的に実行されます。ここでOpenGLの初期設定を行います。
描画
drawRectでコンテキストのmakeCurrentContext(コンテキストの有効化)と、flushBuffer(コマンドの実行)を実行します。その間で実際の描画処理を行います。
終了処理
予め用意されているclearOpenGLメソッドを実行してコンテキストを片付けます。
MyCustomView.m
@implementation CustomOpenGLView - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format { NSLog(@"initWithFrame"); // attributeの宣言 NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPFADoubleBuffer, // double buffering NSOpenGLPFAAccelerated , // HW accelaration NSOpenGLPFAStencilSize , 32, // stencil buffer size = 32 bits NSOpenGLPFAColorSize , 32, // color buffer size = 32 bits NSOpenGLPFADepthSize , 32, // depth buffer size = 32bits 0 // dummy }; NSOpenGLPixelFormat* pixelFormat; pixelFormat = [ [ [ NSOpenGLPixelFormat alloc ] initWithAttributes : attributes ] autorelease ]; // ピクセルフォーマット設定 self = [ super initWithFrame : frameRect pixelFormat : pixelFormat ]; // コンテキストを取得して有効にする glContext = [ self openGLContext ]; [ glContext makeCurrentContext ]; return self; } - (void)awakeFromNib { NSLog(@"awakeFromNib");// xibにViewを配置していないので呼ばれない。 } - (void)prepareOpenGL { // 初期化 glEnable~など } - (void)drawRect:(NSRect)rect { [ glContext makeCurrentContext ]; // 描画 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // default background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // View Transform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Modeling Transform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // draw objects glColor3f(0.7f, 0.2f, 0.2f); glBegin(GL_TRIANGLES); { glVertex3f( 0.0f, 0.0f, 0.0f); glVertex3f( 0.5f, 0.0f, 0.0f); glVertex3f( 0.0f, 0.5f ,0.0f); } glEnd(); glFlush(); [ glContext flushBuffer ]; } - (void)reshape { int width = [self frame].size.width; int height = [self frame].size.height; // view port transform glViewport(0, 0, width, height); // projection transform glMatrixMode(GL_PROJECTION); glLoadIdentity(); // default } -(void)dealloc { [glContext release]; [self clearGLContext]; [super dealloc]; }
アニメーション
タイマー開始と更新処理を行う2つのメソッドを追加します。
@interface CustomOpenGLView : NSOpenGLView { NSOpenGLContext* glContext; NSTimer* timer; } // 略 - (void) startTimer; - (void) updateTimer : (NSTimer*) aTimer; @end
初期化の際にタイマーを開始させます。
- (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format { // 略 // .... glContext = [ self openGLContext ]; [ glContext makeCurrentContext ]; [self startTimer]; // タイマー開始 return self; }
タイマー開始する処理の中でセレクタを使って更新処理を直接呼び出します。
- (void) startTimer { timer = [ NSTimer scheduledTimerWithTimeInterval : 0.05 target : self selector : @selector( updateTimer : ) // イベントハンドラを直接実行 userInfo : nil repeats : YES ]; [timer retain]; }
あとはイベントハンドラ内で再描画処理や変数の更新処理を実装します。
- (void) updateTimer : (NSTimer*) aTimer { // 更新処理 // 再描画 [self setNeedsDisplay:YES]; [ self display ]; }
終了時にタイマーを片付ける処理を追加して完成です。
-(void)dealloc { // タイマー片付け [timer invalidate]; [timer release]; [glContext release]; [self clearGLContext]; [super dealloc]; }
その他
初期化のコツ
drawRect内で一度だけ初期化処理を呼び出す手もあります。
- (void)drawRect:(NSRect)rect { if(!isInit) { glMatrixMode(GL_PROJECTION);// など isInit = YES; } // 描画 }
eclipseにsubversiveを入れる手順
IndigoにSubvresive0.7.9をインストールする手順です。2011/8/20の情報になります。
Subversiveのインストール
ヘルプ>新規ソフトウェアの更新>Indigo - http://download.eclipse.org/releases/indigo
を選択して、フィルターにsuvbersiveと入力する>コラボレーションを選択
>次へ>次へ>使用条件の条項に同意したら>完了>自動インストール開始>今すぐ再起動
コネクタのインストール
起動後にコネクタのインストールを問われる画面が出るのでSVN Kit1.3.5(現時点)にチェックする>完了
>インストールダイアログ表示>全て選択されていることを確認して"次へ">インストール詳細
>次へ>使用条件の条項に同意したら>完了>自動インストール開始
途中でセキュリテリ警告が出るがOK>今すぐ再起動
再起動後、Eclipse>Preferences>"チーム"があればインストール成功
チェックアウト
ファイル>新規>リポジトリ・ロケーション>URL, ユーザー名・パスワードを入力>完了
"新規マスターパスワードが作成されました。パスワードの回復は追加情報の提供により使用可能となります。すぐにそうしますか?"と表示されるので>"はい">リマインダの質問と回答を2つ入力する
以上で、SVNリポジトリブラウザに入力したURLのディレクトリが表示される
ここで、チェックアウトしたいディレクトリに移動して右クリック>チェックアウト
コミットなど
Javaパースペクティブに戻ってあとはプロジェクトを新規作成し、SVNのコマンドを使いたいファイルを選択して右クリック>チーム>コミットを選択するとコミットすることができる。更新やブランチなども同様。
おまけ
Subversiveのバージョンを確認する方法
既にインストールされているSubversiveのバージョンを確認するには、
ヘルプ>新規ソフトウェアの更新>すでにインストールされているのは?>pluginの一覧表示で確認できる
Objective-C2.0文法メモ セレクタ
メッセージング
Objective-Cではコンパイル時にメソッドのアドレスが確定している訳ではなく、メソッドのアドレスを各オブジェクト内のテーブルに記録しておいて、実行時にテーブル内を逐次検索して実行するべきアドレスを選択しています。メソッドを識別する最も単純な方法はメソッド実装とメソッド名(文字列)を対応されればよいですが、実際にはメソッド名と1対1に対応する識別子がコンパイル時にテーブルに記録されています。その実行するべきメソッド名に対応する識別子をセレクタと呼びます。セレクタはメソッド名にのみ依存しており、それが属しているオブジェクトについては識別しません。
オブジェクト | |
セレクタ | メソッド実装 |
セレクタ | メソッド実装 |
セレクタ | メソッド実装 |
セレクタはobjc.hにてSEL型として定義されていて、@selectorを使って実装コード上でも扱うことができます。メソッドの関数ポインタを表から取り出して実行するように、セレクタを取得して実行時にメソッドを呼び出すことができます。
セレクタを取得してメソッドを実行する
@interface MyClass:NSObject - (void) method; @end @implementation MyClass - (void) method { NSLog(@"my class: method was called."); } @end @interface YourClass:NSObject - (void) method; @end @implementation YourClass - (void) method { NSLog(@"your class: method was called."); } @end
このメソッドのセレクタを取得するには、
SEL mySelector = @selector(method);
特定のオブジェクトに対して取得したセレクタを実行するには、performSelectorメソッドを使います。
[object performSelector: mySelector];
今2つのクラスMyClass, YourClassに同じ名称のmethodが定義されているとします。セレクタを取得して双方にメッセージを送ってみた例です。
MyClass *myobj = [[MyClass alloc]init]; YourClass *yourobj = [[YourClass alloc]init]; SEL mySelector = @selector(method); if ( [myobj respondsToSelector:mySelector] ) { [myobj performSelector: mySelector]; } if ( [yourobj respondsToSelector:mySelector] ) { [yourobj performSelector: mySelector]; } [myobj release]; [yourobj release];
my class: method was called. your class: method was called.
Target-Actionパターンでセレクタを利用する
CocoaではUI周りのイベントドリブンな処理を実現するのにTarget-Actionパターンと呼ばれる仕組みを利用しています。セレクタを利用すると、JavaのようにLinstenerクラスを作成しなくてもイベントとアクションの仕組みを簡単に実現することができます。
ボタンを押すと、レシーバーにメッセージが送信されるという例を考えてみます。ボタンクラスはターゲットとなるオブジェクトと、クリックしたときのアクションをセレクタとして保持しておきます。
Button.h
#import <Foundation/Foundation.h> @interface Button : NSObject { id target; SEL action; } - (void) onClick; - (void) setTarget:(id)object Action:(SEL)selector; @end
Button.m
#import "Button.h" @implementation Button - (void) onClick { [target performSelector:action]; } - (void) setTarget:(id)object Action:(SEL)selector { target = object; action = selector; } @end
Receiver.h
@interface Receiver : NSObject - (void) execute; @end
Receiver.m
#import "Receiver.h" @implementation Receiver - (void) execute { NSLog(@"action"); } @end
セレクタを使うと、ボタンのアクションを簡単に設定することができます。イメージとしては実行する関数のポインタをターゲットともにイベントテーブルに登録する作業に似ていると思います。
// 準備 Receiver *receiver= [[Receiver alloc]init]; Button *button = [[Button alloc]init]; [button setTarget:receiver Action:@selector(execute)]; // クリック [button onClick]; // 片付け [button release]; [receiver release];
Receiver:execute
Xcode4の使い方メモ 〜 Cocoaアプリケーション
Cocoaアプリを作成するときの操作方法についてのメモです。Interface Builderなど。
Cocoaアプリケーションのプロジェクトを作成する
File>New>New Project>Cocoa Application>Next>Product Nameプロジェクト名を入力"HelloCocoa", Company Identifier:適切に入力>プロジェクト作成パスを選択>Create
プロジェクト構成は以下の通り
・HelloCocoa
・・HelloCocoaAppDelegate.h
・・HelloCocoaAppDelegate.m→ここに初期化コードを記述
・・MainMenu.xib:UI構成→Interface Builderで編集
・・Supporting Files
・・・HelloCocoa-Info.plist→アプリケーションに関する各種情報
・・・InfoPlist.strings
・・・main.m:メイン→フレームワークなので触らない
・・・HelloCocoa-Prefix.pch:プリコンパイルヘッダー→Cocoa.hがコンパイル済み
・・・Credits.rtf
・・Frameworks:ビルドに必要なライブラリ→必要に応じて追加
・・Products:ビルド後実行ファイル
Interface Builder
Xcode4ではXcode自体にInterface Builderが組み込まれています。MainMenu.xibを選択すると右側EditorでUIを作成することができます。Utilityが表示されていなかったら右上Viewボタンで表示切り替えできます。
Utility(上側)の機能について
関連するものをいくつかピックアップします。
Identity Inspector:カスタムクラスのinstanceを設定する、実行時の属性を設定するなど
attributes Inspector:Viewの属性を設定できる
size Inspector:Viewのサイズや位置を設定する
connections Inspector:OutletやActionの設定、ビューとコントローラーを接続するための各種設定
bindings Inspector:バインディングの設定
effects Inspector:Viewのエフェクトを設定する
Utility(下側)の機能について
UIを設計するときは、Object LibraryのViewを選択して、Interface BuilderのEditorにドラッグドロップすればよい。xib内のObjectやControllerなどもObject Libraryを用いて作成できる。
デザイナにViewを追加する
テキストフィールドをウインドウに追加する例:
Object Library→TextField選択→ウインドウにドラッグドロップ
削除するにはdeleteキーまたはEditor外へドラッグドロップする
Objective-Cクラスの追加
File>New>New File>MacOSX>Objective-C class>Next>subclassを選択(NSObject)>作成するパスを指定>Create
または右側UtilityのObject LibraryからObjective-C classをNavigatorまでドラッグドロップしてもよい。同様にUIViewの派生クラスも作成することができる。
xibにオブジェクトを追加する
プロジェクトにControllerというクラスを作成する。(以下、このクラスを例にして説明します)
Controller.h
#import <Cocoa/Cocoa.h> @interface Controller : NSObject { IBOutlet id textField; } -(IBAction)onButtonClicked:(id)sender; @end
Object Library→Objectを選択→Interface BuilderのEditor→Objects欄にドラッグドロップ
Identity Inspector→Custom Classにクラス名を入力"Controller"
アウトレットの接続方法
クラスにIBOutletというメンバを追加して、そのクラスのオブジェクトをxibに追加しておく。
Connections Inspector>Outlets>textFieldを選択してデザイナーに表示されているテキストフィールドへそのままドラッグドロップする。(右端の○を選択してドラッグドロップすること。マウスオーバーで+マークに変化します。)
Outlets欄が"textField----× TextField"になっていればOK。接続解除するには×ボタンを押せばよい。
アクションの設定
まずObjectLibraryでボタンをウィンドウに追加しておきます。
クラスにIBActionを戻り値とするメソッドを追加して、そのクラスのオブジェクトをxibに追加しておく。
Connections Inspector>Received Actions>onButtonClickedを選択して表示されているボタンまでドラッグドロップする。
Received Actionsが"onButtonClicked--× Push Button"になっていればOK。接続解除するには×マークをクリックする。
その他
追記するかもしれません。