- 打開Finder,跳到~/Library/Application Support/Developer/Shared/Xcode/Plug-ins
- 右鍵點plugin,顯示套件內容
- 打開Info.plist
- 在DVTPlugInCompatibilityUUIDs的Array加入
A16FF353-8441-459E-A50C-B071F53F51B7 - 重啟Xcode
2015年3月10日 星期二
讓Alcatraz支援Xcode 6.2
2015年2月11日 星期三
支援眾多video與audio格式的MobileVLCKit
支援格式
支援的video格式https://www.videolan.org/vlc/features.php?cat=video
支援的audio格式https://www.videolan.org/vlc/features.php?cat=audio
安裝方式
By CocoaPods
pod 'MobileVLCKit'
從night build下載
http://nightlies.videolan.org/build/ios/
Build source code,使用-f參數build出包含所有arch的framework
./buildMobileVLCKit.sh -f
https://wiki.videolan.org/VLCKit/#Building_the_framework_for_iOS
Sample Code
參考範例DropIn-Player http://cl.ly/0G2Y2M1R1X3U
初始化
記得引用MobileVLCKit的原始檔要從.m改為.mm,這樣編譯才不會出錯。
// Set up a videoView by hand. You can also do that in the nib file
videoView = [[VLCVideoView alloc] initWithFrame:[[window contentView] bounds]];
[[window contentView] addSubview:videoView];
[videoView setAutoresizingMask: NSViewHeightSizable|NSViewWidthSizable];
// Init the player object
player = [[VLCMediaPlayer alloc] initWithVideoView:videoView];
VLCMedia *media = [VLCMedia mediaWithPath:@"/to/my/movie"];
//設置delegate等一下可以知道歌曲有多長
media.delegate = self;
[player setMedia:media];
[player play];
控制播放進度(progress)
progressValue為UISlider傳過來的值
- (void)seek:(float) progressValue
{
[self.mediaPlayer setPosition:progressValue];
}
顯示目前播放進度
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerTimeChanged:) name:VLCMediaPlayerTimeChanged object:nil];
- (void)mediaPlayerTimeChanged:(NSNotification *)aNotification
{
self.playbackTime = [_player.time stringValue];
}
獲取duration
如果有把player裡面的VLCMedia設置delegate的話,就會收到通知
- (void)mediaDidFinishParsing:(VLCMedia *)aMedia
{
//更新duration
}
監控狀態
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerStateChanged:) name:VLCMediaPlayerStateChanged object:nil];
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
{
VLCMediaPlayerState state;
switch (state) {
case VLCMediaPlayerStatePaused:
break;
case VLCMediaPlayerStateStopped:
break;
case VLCMediaPlayerStateBuffering:
break;
case VLCMediaPlayerStatePlaying:
break;
case VLCMediaPlayerStateEnded:
break;
case VLCMediaPlayerStateError:
break;
default:
break;
}
}
2015年1月31日 星期六
在iOS上開發UPnP相關App的資源
說實在的iOS上的UPnP資源有夠難找,大概是因為比較少人做的關係吧,加上如果下錯關鍵字保證是找好幾天找不到什麼東西,就算找到了也不知道能不能達成自己需要的功能,因此就產生了這一篇,希望幫助那些仍在迷途中的靈魂們。
UPnP Control Point Library
顧名思義這邊列出的library主要是做control point的
CyberLink4C
用C語言實現的library,有提供objective-c的wrapper,但並沒有包含全部功能,例如event的部份就需要使用到c api。
Platinum
這是一個以C++實現的解決方案,一樣有提供objective-c的wrapper,control point跟DMS都可以做。注意這個library預設是GPL授權的,如果商業使用需要申請商業授權。
upnpx
核心是C++,加上Objective-C API。VLC for iOS使用了這套library。年代有點久遠,不過堪用。簡單範例可以參考https://github.com/basti2310/upnpx-classes
個人推薦程度 Platinum > upnpx > CyberLink4C
Edit:
兩個新library
UPnAtom
使用swift實現的版本,可以用在Objective-C以及swift專案,目前swift變動很快,production環境不建議使用這個library。
CocoaUPnP
某個開發者因為upnpx無法滿足需求而自己寫的新library,還在非常早期的階段。
UPnP Media Server
Platinum
很多App的Local DMS都是用這套做的,應該沒什麼大問題。
coevo-upnp-server
這個source只能當參考用,年代已經有點久遠,而且裡面有非常多的bug,相信光是要掃雷就會花掉很多時間。
個人推薦程度 Platinum > coevo-upnp-server
順便附上用Platinum做DMS的demo project
https://github.com/vampirewalk/MediaServer
2015年1月29日 星期四
Realm 0.89.0先期研究
近期出現了Realm這個新的mobile database,先做點筆記。
Document: http://realm.io/docs/cocoa/0.89.0/
Subclass RLMObject
// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@property BOOL adopted;
@end
// Dog.m
@implementation Dog
@end //none needed
目前Model物件必須subclass RLMObject。RLMObject只能在創建它的thread內使用。
Supported property types
NSString
NSInteger, CGFloat, int, long, float, and double
BOOL or bool
NSDate
NSData
RLMObject subclasses, so you can have many-to-one relationships.
RLMArray < X >, where X is an RLMObject subclass, so you can have many-to-many relationships.
Write
// Create object
Person *author = [[Person alloc] init];
author.name = @"David Foster Wallace";
// Get the default Realm
RLMRealm *realm = [RLMRealm defaultRealm];
// You only need to do this once (per thread)
// Add to Realm with transaction
[realm beginWriteTransaction];
[realm addObject:author];
[realm commitWriteTransaction];
// Update an object with a transaction
[realm beginWriteTransaction];
author.name = @"Thomas Pynchon";
[realm commitWriteTransaction];
所有對物件的新增修改刪除都必須包在WriteTransaction裡,因為在RLMObject裏的所有ivar都是跟資料庫直接相連的,而不是一般我們習慣的memory。http://stackoverflow.com/a/25002895/1060971 說明了所有對物件property的修改都會被同步進database。
PrimaryKey
在設計Model物件時,PrimaryKey絕對是不可或缺的property之一。在不同的thread之間無法互相傳遞RLMObject使用,因為RLMObject只能在創建它的thread內使用。所以我們只能透過在非main thread查詢到物件的PrimaryKey,然後把PrimaryKey傳到main thread,然後再
[MyObject objectsWhere:"primaryId IN %@", idArray];
獲得一個新的RLMObject,然後才在main thread使用這個新的RLMObject。
參考:https://groups.google.com/d/msg/realm-cocoa/naRBh-WMet0/ONPvTCGaSUkJ
產生primary key的方式常見有兩種,一是流水號,二是uuid。
產生流水號的method在multi-thread環境下記得保證其thread-safe,否則很可能會產生相同的流水號。
uuid的話,在multi-thread環境下直接產生就好,因為衝突機率很小。
[[NSUUID UUID] UUIDString];
Multi async task completion block
假設現在有一些task需要去執行,在method裡面我們想等所有task完成再call completionBlock,那麼可以分兩種情況
1. task是synchronous的
2. task是asynchronous的
1比較好解決,用NSOperationQueue加dependency就OK了,2的話需要用到dispatch_group_t group。
範例:AVAssetExportSession的exportAsynchronouslyWithCompletionHandler方法是用來把iPod Library中的音樂轉成檔案用的。
- (void)exportAsynchronouslyWithCompletionHandler:(void (^)(void))handler;
現在我們選擇了一批歌曲轉出,想要在全部歌曲轉完之後收到通知並呈現UI,但是AVAssetExportSession不提供一次轉多個歌曲,我們只能知道個別task完成的時間,但是不知道哪時候全部完成,那麼要如何在全部轉完之後才收到通知呢?
這時候就可以使用dispatch_group_t group等待group裡的task統統完成再執行收尾工作。
- (void) convertMediaItemsToMp3:(MPMediaItemCollection *) collection completionBlock:(void(^)()) block
dispatch_group_t group = dispatch_group_create();
NSArray *songs = collection.items;
for (MPMediaItem *song in songs) {
dispatch_group_enter(group);
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
initWithAsset:asset
presetName: AVAssetExportPresetAppleM4A];
exporter.outputFileType = @"com.apple.m4a-audio";
exporter.outputURL = ...;
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (block) {
block();
}
});