Mac开发不同于iOS开发,无需考虑太多性能消耗、动画特效、流畅性问题,但也引入了许多新的知识点,Apple提供给Mac开发的许多权限是iOS没有的。
这两天在开发一款本地音乐播放器,简单的界面,支持数据永久化缓存、双击播放器界面选择文件导入、或从Mac文件目录里选择文件直接拖拽到音乐播放器里播放。
效果如下:
Screen Shot 2018-10-03 at 2.24.38 AM.png
当然也包括Status bar里的自定义控件,效果和QQ音乐相仿,大致如下:
status bar.png
1、文件导出到播放器很简单,只需几行代码即可实现,首先监听Mac鼠标点击事件,当触发点击事件时,判断是否是双击,如果是则处理点击事件,代码如下:
- (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来实现的,因为本地音乐文件有的是有封面等信息的,如下图:
cover.png 使用了AVURLAsset来获取导入音频文件中的歌曲信息 4D0B59FAE1706D184BDE6D386C0BC694.png代码如下:
- (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,可根据需求获取
// 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;
}
其次实现拖动文件的代理
- (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,当系统检测到有文件拖动到播放器界面时,获取音频文件的本地路径,回掉给播放器播放
typedef void(^PasteboardResponsedBlock)(NSURL *dragingPath);
@property (nonatomic, strong) PasteboardResponsedBlock draggingFile;
3、添加播放器到Mac中,Xcode - Target - Info, 选择Document Types,增加一个types
Screen Shot 2018-10-03 at 3.09.17 AM.png
设置图标,Extensions是支持播放的音频文件格式,这里仅支持mp3格式的音频文件,设置完成后,当我们右键点击本地mp3文件弹出菜单选择播放器时,就可以看到自定义的播放器了
Screen Shot 2018-10-03 at 3.06.36 AM.png接下来选择AppDelegat文件,实现如下方法,用于监听双击打开音频文件的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
Dock.png
网友评论