Mac application development notes 2

Mac開發(fā)不同于iOS開發(fā)绳匀,無需考慮太多性能消耗芋忿、動畫特效炸客、流暢性問題,但也引入了許多新的知識點戈钢,Apple提供給Mac開發(fā)的許多權限是iOS沒有的痹仙。

這兩天在開發(fā)一款本地音樂播放器,簡單的界面殉了,支持數(shù)據(jù)永久化緩存开仰、雙擊播放器界面選擇文件導入、或從Mac文件目錄里選擇文件直接拖拽到音樂播放器里播放薪铜。
效果如下:


Screen Shot 2018-10-03 at 2.24.38 AM.png

當然也包括Status bar里的自定義控件众弓,效果和QQ音樂相仿,大致如下:


status bar.png

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)的蟆炊,因為本地音樂文件有的是有封面等信息的稽莉,如下圖:

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,可根據(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


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文件,實現(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

Dock.png
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末爽锥,一起剝皮案震驚了整個濱河市涌韩,隨后出現(xiàn)的幾起案子畔柔,更是在濱河造成了極大的恐慌,老刑警劉巖臣樱,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件靶擦,死亡現(xiàn)場離奇詭異,居然都是意外死亡雇毫,警方通過查閱死者的電腦和手機玄捕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棚放,“玉大人枚粘,你說我怎么就攤上這事∑牵” “怎么了馍迄?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長局骤。 經(jīng)常有香客問我家乘,道長杂穷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮狞悲,結果婚禮上,老公的妹妹穿的比我還像新娘搪泳。我一直安慰自己磕诊,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布冰单。 她就那樣靜靜地躺著忧风,像睡著了一般。 火紅的嫁衣襯著肌膚如雪球凰。 梳的紋絲不亂的頭發(fā)上狮腿,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天腿宰,我揣著相機與錄音,去河邊找鬼缘厢。 笑死吃度,一個胖子當著我的面吹牛,可吹牛的內容都是我干的贴硫。 我是一名探鬼主播椿每,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼英遭!你這毒婦竟也來了间护?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤挖诸,失蹤者是張志新(化名)和其女友劉穎汁尺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體多律,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡痴突,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狼荞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辽装。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖相味,靈堂內的尸體忽然破棺而出拾积,到底是詐尸還是另有隱情,我是刑警寧澤丰涉,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布拓巧,位于F島的核電站,受9級特大地震影響昔搂,放射性物質發(fā)生泄漏玲销。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一摘符、第九天 我趴在偏房一處隱蔽的房頂上張望贤斜。 院中可真熱鬧,春花似錦逛裤、人聲如沸瘩绒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锁荔。三九已至,卻和暖如春蝙砌,著一層夾襖步出監(jiān)牢的瞬間阳堕,已是汗流浹背跋理。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留恬总,地道東北人前普。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像壹堰,于是被迫代替她去往敵國和親拭卿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內容