美文网首页
Mac application development note

Mac application development note

作者: Visitor | 来源:发表于2018-10-03 03:39 被阅读46次

    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

    相关文章

      网友评论

          本文标题:Mac application development note

          本文链接:https://www.haomeiwen.com/subject/tcczoftx.html