2017年12月4日 星期一

IPv6 網路環境

Apple 提供一個 IPv6環境的設定方案。請參考 Apple 官方文件。 https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html

2017年9月26日 星期二

指定不同的使用者,git commit

在專案工作根目錄下,去編輯 .git/config. 並將下列內容填入即可。 [user] name = 'xcs' email = 'cypressmobileapp@gmail.com'

2015年10月21日 星期三

iOS : Remote Push Notification

其實,從 iOS8 以後,最大的改變是推播訊加了選項 (官方定義名稱為: Action 動作) 可以選擇,App 可以不需要在前景;使用者可以直接在推播訊息上,選擇你所定義好的選項,當使用者點擊了選項,系統會直接呼叫程式內的 handleActionWithIdentifier: 處理,即使 App 完全沒有在執行也可以。

在 iOS7 的 Remote Notification 如何工作。

首先,註冊直接呼叫指令 [UIApplication registerForRemoteNotificationTypes:]

//註冊
- (void)registerForRemoteNotificationTypes:(UIRemoteNotificationType)types;

//Delegate
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;

當註冊後,推播用的 token會經由 didRegisterForRemoteNotificationsWithDeviceToken: 傳送給手機內; 但若註冊失敗,則會由 didFailToRegisterForRemoteNotificationsWithError: 回報錯誤訊息。

再如何接收推播訊息呢?第一件必需清楚的是,若開啟 App 不是透過點擊推播訊息,才開啟App,App是收不到推播訊息的。

  1. 當App不在背景時,點擊推播訊息去開啟 App。推播訊息會分別由 didFinishLaunchingWithOptions:didReceiveRemoteNotification: 帶入。
  2. 當App在背景時,點擊推播訊息去開啟 App。推播訊息只會由 didReceiveRemoteNotification: 帶入。
  3. 當App在前景時,此時推播訊息送達,推播訊息只會由 didReceiveRemoteNotification: 帶入。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;

iOS8 多了Action 和 background handle

文章前頭有提到播推訊多了動作,可提供給使用者選擇。而這些動作就必需在程式內去定義好它們。另外一個功能,每次推播訊息需要提供的選項,不一定都是一致的。於是我們可以將動作分類,並將分類後,各個類別給與一個識別 (id)。推播伺服器推送訊息時,可以設定使用那個動作類別。

單純的呼叫 registerForRemoteNotificationTypes: 取得Token的方式將無法使用了。將拆解為

定義動作 (Action)
區分類別 (Category)
註冊 (若Location Notification 需要額外的 Core Location 註冊,在另外一篇討論)

定義動作 Action

新建一個使用者選項按鈕方法如下:

    //來產生一個動作
    UIMutableUserNotificationAction *actionLike = [[UIMutableUserNotificationAction alloc]init];    

    //決定這個動作,需不需要開啟App
    actionLike.activationMode = UIUserNotificationActivationModeBackground;

    //這個動作是否對資料或App會有破壞的程序。
    actionLike.destructive = NO;

    //這個動作是否需要使用者驗證。
    actionLike.authenticationRequired = NO;

    //動作的 id 
    actionLike.identifier = @"idLIKE";

    //動作所顯示的文字
    actionLike.title = @"喜歡";

區分類別

首先,先注意推播訊息會出現的四個位置,分別為 Lock screen、Notification center、Banner、和 Modal view。除了 Modal view 可以放三個動作外,其它位置僅能放二個動作。

    //產生一個類別,並且給這個類別一個 id。
    UIMutableUserNotificationCategory *catLike = [[UIMutableUserNotificationCategory alloc]init];
    cat.identifier = @"LikeCategory";

    //UIUserNotificationActionContextDefault,目前可放三個動作,而目前也只有給 Model view 用。
    [cat setAction:@[actionLike, actionNoLike, actionHate] forContext:UIUserNotificationActionContextDefault];

    //UIUserNotificationActionContextMinimal,目前可放二個動作,是較常使用的,除了 Modal view。
    [cat setAction:@[actionLike, actionNoLike] forContext:UIUserNotificationActionContextMinimal];

可以註冊了。

    //將所有的類別 category放在一個集合 (set),以產生一個UIUserNotificationSettings
    NSSet *categories = [NSSet setWithArray:@[catLike, catAccept,...]];
    UIUserNotificationSettings *settings = 
                [UIUserNotificationSettings 
                        settingsForTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)
                        categories:categories];

    //註冊,完成。結果依舊由 didRegisterForRemoteNotificationsWithDeviceToken 和 didFailToRegisterForRemoteNotificationsWithError 取得。
    UIApplication *app = [UIApplication sharedApplication];
    [app registerUserNotificationSettings:settings];

推播內容和處理

若推播訊息需要有提供選項給使用者,則在推播內容內,就需要指定 category。

{"aps":
    {
        "alert":"Do you like iPhone",
        "category":"LikeCategory"  //指定使用那個類別。
    }
}

接收訊息和 iOS7 一樣,另外新增 handleActionWithIdentifier 函式,讓 App 不需要在前景,使用者點擊了動作,系統會呼叫這個函式。

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler; 

2014年10月20日 星期一

解析 iOS 照相和錄影如何達成

請參考 AVCam SampleAV Foundation Programming Guide

iOS 拍照和錄影是透過 AV Foundation framework 來完成的。首先先介紹幾個類別:
1. AVCaptureDevice - 用來描述輸入硬體元件,例如:攝影機或麥克風。
2. AVCautureInput - 用來設定輸入硬體元件輸出埠。
3. AVCaptureOutput - 用來描述要輸出的結果,例出要輸出成照片檔 (AVCaptureStillImageOutput) 或 影片檔 (AVCaptureMovieFileOutput)等。
4. AVCaptureConnection - 用來描述和串聯輸入物件和輸出物件的類別,被使用於 AVCaptureSession 內部,通常會使用 AVCatureSession 物件,而不會直接使用 AVCaptureConnection。
5. AVCaptureSession - 所有輸入硬體物件(AVCaputureInput objects)和輸入物件(AVCaptureOutput objects,可能是照片檔或影片檔)都會被加入 AVCaptureSession 物件,最後透過 -(void)startRunning; 和 -(void)stopRunning; 來啟動和停止工作。
6. AVCaptureVideoPreviewLayer - 這是一個滿重要的類別,如其名,就是用於在螢幕上產生預覽畫面用。在此預覽的畫面是指輸入影像串流直接即時顯示在View上,而不是指拍照後的預覽。

接著,就來分解 AVCam source code 吧~
先來看看初始化要作些什麼吧!

1. 產生 AVCaptureSession 物件,先把 AVCaptureVideoPreviewLayer 指定給 session.

 AVCaptureSession *session = [[AVCaptureSession alloc] init];
[[self previewView] setSession:session];

2. 找到適當的 AVCaptureDevice ,產生適當的 AVCaptureInput
AVCaptureDevice *videoDevice = [AVCamViewController deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject]; AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];

3. 把 AVCaptureInput 加入 session。
if ([session canAddInput:videoDeviceInput])
{
    [session addInput:videoDeviceInput];
    ...
}
if ([session canAddInput:audioDeviceInput])
{
    [session addInput:audioDeviceInput];
    ...
}

4. 初始要輸出的影片檔和圖片檔加到 session。
AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([session canAddOutput:movieFileOutput])
{
    [session addOutput:movieFileOutput];
    ...
}
    
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
if ([session canAddOutput:stillImageOutput])
{
    [session addOutput:stillImageOutput];
    ...
}

接著,來拍照吧!拍照的指令就是
[AVCaptureStillImageOutput captureStillImageAsynchronouslyFromConnection:completionHandler:];
來看看 Sample source code 怎麼寫的。
- (IBAction)snapStillImage:(id)sender
{
    dispatch_async([self sessionQueue], ^{
        ...
        [[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:[[self stillImageOutput] connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
            if (imageDataSampleBuffer)  // imageDataSampleBuffer 指向儲存照片內容的地方
            {
                // 將數據用 NSData 物件化
                NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                // 再轉成 UIImage 物件
                UIImage *image = [[UIImage alloc] initWithData:imageData];
                // 最後,儲存到系統相簿
                [[[ALAssetsLibrary alloc] init] writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:nil];
            }
        }];
    });
}

驚 ~! 拍照怎麼這麼簡單;那攝影呢?來看看吧~!

    //開始錄影並指定錄影要暫存到那個檔案。
    [AVCaptureMovieFileOutput startRecordingToOutputFileURL:recordingDelegate:] 
    //結束錄影
    [AVCaptureMovieFileOutput stopRecording]  

不難,看原始碼就懂了

2014年7月15日 星期二

MAC OS X: Sweep IP Address: 在 MAC 上,去掃描網路上的 ip address。

想知道,網路上有那些 IP 位址己經被使用了嗎了,在 Windows 上,可以使用Angry IP Scanner。 但,在 MAC 上,並不用這麼麻煩,只要打開終端機(terminal), 在上面鍵入以下指令即可。

xCedar$ for x in {1..254}; do ping -c 1 -W 100 192.168.1.$x | grep 'time='; done

以上,簡單吧~! 指令中,以 for ... done 為主的迥圈,設定 x 的值,從 1 到 254; 並變數 x 的值,
以 $x 代入 ping 的指令中。
-c 1: ping 的次數為一次; -W 100: 等待回應時間為 100ms。
最後以關鍵字 "time=" 來列出有回應的 IP 位址。

Ref:Ping Sweep for MAC OS X

2014年6月26日 星期四

iOS 7 的 MotionEffects

iOS 7 剛出來時,大家第一個會注意到的事在 Lock Screen 有一個會動的背景;那就是 Motion Effects。
其實它很簡單,我試著在一個UIView 加上一個 UIButton,接著讓 Button 是浮動的。看程式碼就懂了。

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    aButton.layer.mask.frame = aButton.bounds;
    aButton.layer.masksToBounds = YES;

    UIInterpolatingMotionEffect *xAxis = [[UIInterpolatingMotionEffect alloc]
                                          initWithKeyPath:@"center.x"
                                          type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];

    xAxis.minimumRelativeValue = [NSNumber numberWithFloat:-90.0];
    xAxis.maximumRelativeValue = [NSNumber numberWithFloat:90.0];

    UIInterpolatingMotionEffect *yAxis = [[UIInterpolatingMotionEffect alloc]
                                          initWithKeyPath:@"center.y"
                                          type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];

    yAxis.minimumRelativeValue = [NSNumber numberWithFloat:-90.0];
    yAxis.maximumRelativeValue = [NSNumber numberWithFloat:90.0];

    UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc]init];
    group.motionEffects = @[xAxis, yAxis];
    [aButton addMotionEffect:group];
}

2014年4月28日 星期一

Mac OS X Mavericks 開啟 關閉 系統內建的 FTP Server

在Mac OS X Mavericks 上,己經將原來放置在 "檔案共享"裡的 "FTP伺服器" 選項己經拿掉了。現在只能透過 command line 中的 launchctl 去啟動和關閉系統內建的"FTP伺服器"。

開啟

sudo -s launchctl load -w /System/Library/LaunchDaemous/ftp.plist

關閉

sudo -s launchctl unload -w /System/Library/LaunchDaemous/ftp.plist