Application
Mac OS X 與 iOS 上的 application 不太相同,在 Mac OS X 上是 NSApplication,在 iOS 上則是 UIApplication,但基本上都負責相同的工作:把來自外部的種種傳遞給內部,包括硬體事件,與其他各種系統事件。
硬體事件會被傳遞給 window,而其他系統事件,像 App被開啟或關閉、被推到前景或背景、收到 push notification…等等,則是會轉發給 application 的 delegate。
由於 application 位在 responder chain 的最底層,每一個 view 與 window 都不處理的時候,才會丟給 application 處理,所以如果我們希望處理一些會影響整個 App 行為的事件的時候,就適合由 application 這一層處理。
比方說,KKBOX 是一個音樂 App,所以我們會希望用戶可以透過藍芽耳機或線控耳機上的按鈕,切換 KKBOX 當中的歌曲,換到前一首或下一首歌曲。在
iOS 7.1 之前,我們要處理線控耳機的事件,會選擇實作 UIResponder
protocol 中的 -remoteControlReceivedWithEvent:
。因為換歌這件事情應該是對整個 KKBOX 的操作,無論放在哪個 view 或 view controller 都不適合,所以應該要放在 application 這一層。
要讓 application 這一層可以做額外的事情,我們首先要建立自己的 UIApplication subclass:
@interface KKApplication : UIApplication
@end
然後,在 main.m 裡頭,告訴 UIApplicationMain
,我們應該要使用
KKApplication,而不是原本的 UIApplication 的實作:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv,
NSStringFromClass([KKApplication class]),
NSStringFromClass([AppDelegate class]));
}
}
我們就可以在 KKApplication 處理事件了:
@implementation KKApplication
- (void)remoteControlReceivedWithEvent:(UIEvent *)theEvent
{
if (theEvent.type == UIEventTypeRemoteControl) {
switch(theEvent.subtype) {
case UIEventSubtypeRemoteControlPlay:
break;
case UIEventSubtypeRemoteControlPause:
break;
case UIEventSubtypeRemoteControlStop:
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
break;
case UIEventSubtypeRemoteControlNextTrack:
break;
case UIEventSubtypeRemoteControlPreviousTrack:
break;
...
default:
return;
}
}
}
@end
當然,如果我們想要開始接收來自耳機的事件,我們還要對 UIApplication 的
singleton 物件呼叫 -beginReceivingRemoteControlEvents:
。
雖然跟 application 這一層無關,不過提到了耳機線控,就得提一下。蘋果在推出 iOS 7.1 的時候,同時推出了 Car Play 功能,Car Play 允許用戶在車用音響的介面上控制 iOS App,由於車用音響的畫面較大,所以,除了可以用來切換前後首歌曲之外,蘋果還加入了可以對歌曲評分,表示喜歡或不喜歡等功能,於是整個改寫了處理耳機線控的這一塊,推出 MPRemoteCommandCenter 這個 class。
從 MPRemoteCommandCenter 的 singleton 物件 sharedCommandCenter
上,我們可以拿到許多種不同的 MPRemoteCommand,然後對 MPRemoteCommand 設定
target/action。我們之前想要開始播放,會在
-remoteControlReceivedWithEvent:
裡頭處理
UIEventSubtypeRemoteControlPlay,現在會改成向 MPRemoteCommandCenter 要求 playCommand
,然後指定 target/action,例如:
[[MPRemoteCommandCenter sharedCommandCenter].playCommand addTarget:self action:@selector(play:)];