星期三, 四月 28, 2010

awakeFromNib 與 initWithNibName:bundle:

程式大了一些之後,我們常常會需要切換不同的 view 元件,一般來說,會把幾個主要的 view 以及它的 controller 包到獨立的 nib 檔中。一來方便管理,不會在 MainMenu.nib 中看到一堆 view、controller,甚至其他的 formatter 之類的。二來,可以依需要動態載入、釋放這些 object,降低佔用的系統資源。

而一般 view nib 檔中除了 view 之外,會把 File's Owner 指定為某個 controller 的類別,而這個類別通常是繼承自 NSViewController。

然後我們就可以在程式中使用
MyViewController * controller = [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil];
來載入這個儲存在 nib 檔中的 view。
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
是 NSViewController 提供的一個 method,用來載入存在 nib 檔中的物件,然後我們就可以對 controller 下操作的命令,一切看起來都很美好。

於是我們開心的在 MyViewController.m 裡面實作 awakeFromNib,在裡面初始化一些 UI 元件,一切依然很美好,然後我們利用
[placeHolder addSubview:[controller view]];
來把 MyView 顯示在某個 view 裡面,運作正常,美好的不得了。

不過問題來了,如果我們要在把 view 顯示出來之前對 controller 先做些事,就會發現:awakeFromNib 還沒有被呼叫過!所以裡面的初始化都還沒做!

整個流程的問題出在,awakeFromNib 會在所有存在 nib 檔中的物件被解開之後,發送給 "所有這些被解開的物件",而 "這些物件" 並不包括我們的 controller,因為它是我們在別的地方 alloc 出來的!也就是說,MyViewController 並不會在 [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil]; 之後接收到 awakeFromNib。因此,初始化的適當地點是,自己實作 MyViewController 的 initWithNibName:bundle: method,並在裡面做初始化的動作。

標籤: , ,

星期四, 四月 22, 2010

Cocoa 手勢支援

在 Macbook Air 推出之後,Apple 就多了手勢操作這項功能,但是這項功能雖然在 10.5 就有了,不過卻直到 10.6 才在 Apple 的 Document 中出現。而要在 10.5 中使用這項功能的話,其實也很簡單,只要實作 10.6 的 Document 裡面出現的幾項 method 即可,也就是下面五個 method:
  • - (void)magnifyWithEvent:(NSEvent *)event;
  • - (void)rotateWithEvent:(NSEvent *)event;
  • - (void)swipeWithEvent:(NSEvent *)event;
  • - (void)beginGestureWithEvent:(NSEvent *)event;
  • - (void)endGestureWithEvent:(NSEvent *)event;
不過除非要自己抓取新的手勢,不然主要只要實作 magnifyWithEvent、rotateWithEvent、swipeWithEvent 這三個即可。

這三個手勢的詳細操作,可以參考 10.6 的 Document,在 10.5 都相容,只有 magnifyWithEvent 要特別處理一下。

magnifyWithEvent 的標準用法如下(Apple Document):
- (void)magnifyWithEvent:(NSEvent *)event {
[resultsField setStringValue:
[NSString stringWithFormat:@"Magnification value is %f", [event magnification]]];
NSSize newSize;
newSize.height = self.frame.size.height * ([event magnification] + 1.0);
newSize.width = self.frame.size.width * ([event magnification] + 1.0);
[self setFrameSize:newSize];
}
但是在 10.5 上,NSEvent 沒有 magnification 這個 method,所以必須自己擴充,code 如下(ref):
#pragma mark defines for 10.6 api not documented in 10.5
#ifndef MAC_OS_X_VERSION_10_6
enum {
/* The following event types are available on some hardware on 10.5.2 and later */
NSEventTypeGesture = 29,
NSEventTypeMagnify = 30,
NSEventTypeSwipe = 31,
NSEventTypeRotate = 18,
NSEventTypeBeginGesture = 19,
NSEventTypeEndGesture = 20
};

@interface NSEvent(GestureEvents)
/* This message is valid for events of type NSEventTypeMagnify, on 10.5.2 or later */
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 - (float)magnification; // change in magnification. #else - (CGFloat)magnification; // change in magnification. #endif @end #endif

這段 code 也把 10.5 中 NSEvent 欠缺的 type id 補齊了,所以如果想要支援手勢操作,最好都把這段 code 貼上來用吧。

標籤: ,