Mac開發(fā)不同于iOS開發(fā)绳匀,無需考慮太多性能消耗芋忿、動畫特效炸客、流暢性問題,但也引入了許多新的知識點戈钢,Apple提供給Mac開發(fā)的許多權限是iOS沒有的痹仙。
這兩天在開發(fā)一款本地音樂播放器,簡單的界面殉了,支持數(shù)據(jù)永久化緩存开仰、雙擊播放器界面選擇文件導入、或從Mac文件目錄里選擇文件直接拖拽到音樂播放器里播放薪铜。
效果如下:
當然也包括Status bar里的自定義控件众弓,效果和QQ音樂相仿,大致如下:
1隔箍、文件導出到播放器很簡單谓娃,只需幾行代碼即可實現(xiàn),首先監(jiān)聽Mac鼠標點擊事件蜒滩,當觸發(fā)點擊事件時滨达,判斷是否是雙擊,如果是則處理點擊事件俯艰,代碼如下:
- (void)mouseUp:(NSEvent *)event {
if(event.clickCount >= 2) {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.canChooseFiles = YES;
openPanel.canChooseDirectories = YES;
openPanel.canResolveUbiquitousConflicts = YES;
// 允許同時導入多個文件
openPanel.allowsMultipleSelection = YES;
NSInteger result = [openPanel runModal];
if(result == NSModalResponseOK) {
[self resetPlayerWithURL:openPanel.URL];
}
}
}
音樂播放使用的是AVFoundation框架弦悉,AVPlayer、AVPlayerItem來實現(xiàn)的蟆炊,因為本地音樂文件有的是有封面等信息的稽莉,如下圖:
代碼如下:
- (AVURLAsset *)assetWithURL:(NSURL *)url {
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
for(NSString *format in asset.availableMetadataFormats) {
for(AVMetadataItem *metaDataItem in [asset metadataForFormat:format]) {
// cover
if([metaDataItem.commonKey isEqualToString:AVMetadataCommonKeyArtwork]) {
NSImage *image = [[NSImage alloc] initWithData:(NSData *)metaDataItem.value];
self.coverImage.image = image ? image : [NSImage imageNamed:@"IMG_2567"];
}
// name
if([metaDataItem.commonKey isEqualToString:AVMetadataCommonKeyTitle]) {
self.songNameLabel.stringValue = (NSString *)metaDataItem.value;
}
// musician
if([metaDataItem.commonKey isEqualToString:AVMetadataCommonKeyArtist]) {
// self.artistLabel.stringValue = (NSString *)metaDataItem.value;
}
}
}
return asset;
}
AVFoundation框架里的AVMetadataFormat文件里提供了很多MedadataKey,可根據(jù)需求獲取
// Metadata common keys
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyTitle NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyCreator NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeySubject NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyDescription NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyPublisher NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyContributor NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyCreationDate NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyLastModifiedDate NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyType NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyFormat NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyIdentifier NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeySource NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyLanguage NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyRelation NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyLocation NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyCopyrights NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyAlbumName NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyAuthor NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyArtist NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyArtwork NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyMake NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeyModel NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT AVMetadataKey const AVMetadataCommonKeySoftware NS_AVAILABLE(10_7, 4_0);
2涩搓、拖動文件到播放器的流程污秆,首先在播放器NSView界面,初始化的時候注冊
- (instancetype)initWithCoder:(NSCoder *)decoder {
if(self = [super initWithCoder:decoder]) {
// 注冊所有文件格式
NSString *UTTypeString = (__bridge NSString *)kUTTypeURL;
[self registerForDraggedTypes:[NSArray arrayWithObject:UTTypeString]];
}
return self;
}
其次實現(xiàn)拖動文件的代理
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
NSPasteboard *pasteboard = [sender draggingPasteboard];
NSString *audioVisualContent = (__bridge NSString *)kUTTypeAudiovisualContent;
NSDictionary *filteringOptions =[NSDictionary dictionaryWithObject:audioVisualContent forKey:NSPasteboardURLReadingFileURLsOnlyKey];
if([pasteboard canReadObjectForClasses:@[[NSURL class]] options:filteringOptions]) {
return NSDragOperationCopy;
}
return NSDragOperationNone;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
NSArray *aboardArray = [[sender draggingPasteboard] propertyListForType:NSFilenamesPboardType];
NSURL *url = [NSURL fileURLWithPath:aboardArray.firstObject];
if(self.draggingFile) {
self.draggingFile(url);
}
return YES;
}
這里的draggingFile是一個block,當系統(tǒng)檢測到有文件拖動到播放器界面時昧甘,獲取音頻文件的本地路徑良拼,回掉給播放器播放
typedef void(^PasteboardResponsedBlock)(NSURL *dragingPath);
@property (nonatomic, strong) PasteboardResponsedBlock draggingFile;
3、添加播放器到Mac中充边,Xcode - Target - Info庸推, 選擇Document Types,增加一個types
設置圖標浇冰,Extensions是支持播放的音頻文件格式贬媒,這里僅支持mp3格式的音頻文件,設置完成后肘习,當我們右鍵點擊本地mp3文件彈出菜單選擇播放器時际乘,就可以看到自定義的播放器了
接下來選擇AppDelegat文件,實現(xiàn)如下方法漂佩,用于監(jiān)聽雙擊打開音頻文件的Mac本地路徑:
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
// 雙擊選擇音頻文件的本地路徑脖含, 回調給AVPlayer播放
/Users/xxx/Desktop/timegoesby.mp3
return YES;
}
當然罪塔,在我們運行App后,點擊關閉無法再次打開時养葵,需要在AppDelegate里面征堪,在與AppDelegate綁定的NSWindowController里,添加如下代碼关拒,這樣當關閉App佃蚜,在Dock里點擊App圖標依然可以打開
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag {
[self.windowController.window makeKeyAndOrderFront:nil];
return YES;
}
4、當然如果設置了App圖標夏醉,在Dock里依然可以顯示該App