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

2014年1月4日 星期六

Sublime text 2 setup

最近常使用 Sublime Text 來編輯一些文件,有時候,電腦重灌就忘了一些常用套件是怎麼裝旳,以下列一些常用的套件的URL吧!

Sublime Text 2 http://www.sublimetext.com/2
Package Control https://sublime.wbond.net/installation#st2
Markdown preview https://github.com/revolunet/sublimetext-markdown-preview

2013年12月9日 星期一

如何新增一筆聯絡人資料到通訊錄

接著上一篇如何存取iPhone 通訊錄裡的資料,接著就是要新增一筆資料寫入通訊錄了。其實很簡單,只要呼叫 ABPersonCreate(); 便可產生一筆空白資料,多項資料(MultiProperty)比單項資料(Property)多了一道處理程序;直接來看範例~

 NSString *firstName, *lastName, ... ;
 NSDictionary *emails;
 ...

 //產生 AddressBook 物件
 ABAddressBookRef addressBookObj = ABAddressBookCreateWithOptions(NULL, NULL);

 //產生一筆空白聯絡人資料
 ABRecord record = ABPersonCreate();

 //處理單項資料 (property)
 ABRecordSetValue(record, kABPersonFirstNameProperty, CFBridgingRetain(firstName) , nil);
 ABRecordSetValue(record, kABPersonLastNameProperty, CFBridgingRetain(lastName) , nil);

 //處理多項資料 (MultiProperty)
 NSArray *allLabels = [emails allKeys];
 ABMutableMultiValueRef multiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType);
 for(NSString *label in allLabels) {
    NSString *value = [emails objectForKey:label];
    ABMultiValueAddValueAndLabel(multiValue, CFBridgingRetain(value), CFBridgingRetain(label), NULL);
 }
 ABRecordSetValue(record, kABPersonEmailProperty, multiValue, nil);

 //寫入&儲存
 ABAddressBookAddRecord(addressBookObj,record,nil);
 ABAddressBookSave(addressBookObj);

在上面的例子中,是直接產生一筆空白的聯絡人資料;若是換成即有存在的舊資料,也是可以的,但在處理多項資料時,若遇到已經存在的label,不可使用 ABMultiValueAddValueAndLabel,請改用 ABMultiValueReplaceValueAtIndex 去處理;

除了,直接寫入通訊錄外, framework 另外提供了預設的聯絡人的輸入UI,共有二套,一為 "ABNewPersonViewController";另一為 "ABUnknownPersonViewController";差別在於後者可以選擇覆寫即有的資料。

ABNewPersonViewController 的範例~

 ABNewPersonViewController *newPersonVC = [[ABNewPersonViewController alloc] init];
 ABRecordRef aNewRecord = ABPersonCreate();

 newPersonVC.newPersonViewDelegate = self;
 newPersonVC.addressBook = addressBookObj;
 newPersonVC.displayedPerson = aNewRecord;

 [currentNavigationViewController pushViewController:newPersonVC animated:YES];

ABNewPersonViewController 的範例~

 ABUnknownPersonViewController *unknowPersonVC = [[ABUnknownPersonViewController alloc]init];
 unknowPersonVC.unknownPersonViewDelegate = self;
 unknowPersonVC.displayedPerson = aOldRecord;
 unknowPersonVC.allowsAddingToAddressBook = YES;
 unknowPersonVC.allowsActions = YES;

 [currentNavigationViewController pushViewController:unknowPersonVC animated:YES];

2013年12月4日 星期三

如何存取iPhone 通訊錄裡的資料

iPhone 內部是使用 SQLite3 作為通訊錄的資料庫,但是因為沙盒的限制(沙盒「sandbox」: 指程式在一個獨立且受限的虛擬環境裡執行,可確保不會受其它干擾,也不干擾其它程式運作) 。所以,你是無法直接讀取的必需透過 XCode 所提供的 AddressBook.framework 來讀取和寫入資料。

而 AddressBook.framework 所提的 API,比較接近 C 語言,無法直接使用 NSArray, NSString 來定義變數;其實,在學XCode以來,在 AddressBook.framework 所定義的變數保留字,一直給我一種“為定義而定義”的感覺,很多餘。但不列出來,再過一陣子,我一定會忘了;在此,先列出在 AddressBook.framework 中所使用的變數保留字。

  • ABAddressBookRef : 用來宣告 AddressBook物件變數,AddressBook物件在使用上,和 NSManagedObjectContext 一樣,任何對 iPhone 通訊錄的讀取或儲存,都需要透過 AddressBook物件來完成;而建立一個 AddressBook物件指令為 ABAddressBookRef addressBookObj = ABAddressBookCreateWithOptions(NULL, NULL);
  • ABRecordRef : 用來宣告 ABPerson 物件變數,一筆資料即為一 ABPerson 物件。例如: 產生一筆空白資料,指令為 ABRecordRef newPerson = ABPersonCreate();
  • CFTypeRef : 萬用變數宣告,其實它就是 void* ;用於字串、任一數值, 例如: CFTypeRef firstName;
  • CFArrayRef : 用來宣告陣列變數;

知道了 AddressBook.framework 常用的變數保留字,就來將 iPhone 通訊錄中的 所有記錄取出來吧~

 //產生 AddressBook 物件
 ABAddressBookRef addressBookObj = ABAddressBookCreateWithOptions(NULL, NULL); 
 //取出所有聯絡人記錄
 CFArrayRef allPeoples = ABAddressBookCopyArrayOfAllPeople(addressBookObj);
 int numPeople = CFArrayGetCount(allPeoples);
 //依續取出每個聯絡人資料
 for(int i=0 ; i<numPeople; i++ ) {
    ABRecordRef onePeople = numPeople[i];
    // 取出該聯絡人所有資訊
 }

有了聯絡人,接著就著手處理聯絡人所包含資訊。首先,資料有分單一資料(Property)和多項資料(MultiProperty),取出後的處理方法也不一樣; 先釐清一個觀念,CFDictionary(屬性:kABDictionaryPropertyType)是單一資料,雖然,Dictionary 可以包含很多資料;而屬性 kABMultiDictionaryPropertyTyple 才是多項資料;很明顯地,在屬性鍵值有'multi’的字眼,便是多項資料; 取出資料的指令為

 CFTypeRef valueRef = ABRecordCopyValue(oenPeople, kABDictionaryPropertyType);

而要取出所有資料的方法如下:

 for(ABPropertyID propKey = kABPersonFirstNameProperty; propKey <= kABPersonSocialProfileProperty; propKey++) {
    //先取出數值
    CFTypeRef valueRef = ABRecordCopyValue(onePeople,propKey);

    ABPropertyType propType = ABPersonGetTypeOfProperty(propKey); 
    //判斷是單一資料或多項資料
    if(propType & kABMultiValueMask) {
        //多項資料處理
    } else {
        //單一資料處理
    }
 }

接著,讓我們看看在一筆聯絡人記錄中,帶有那些資訊吧~

ABPropertyID 描述
kABPersonFirstNameProperty; First name - kABStringPropertyType
kABPersonLastNameProperty; Last name - kABStringPropertyType
kABPersonMiddleNameProperty; Middle name - kABStringPropertyType
kABPersonPrefixProperty; Prefix ("Sir" "Duke" "General") - kABStringPropertyType
kABPersonSuffixProperty; Suffix ("Jr." "Sr." "III") - kABStringPropertyType
kABPersonNicknameProperty; Nickname - kABStringPropertyType
kABPersonFirstNamePhoneticProperty; First name Phonetic - kABStringPropertyType
kABPersonLastNamePhoneticProperty; Last name Phonetic - kABStringPropertyType
kABPersonMiddleNamePhoneticProperty; Middle name Phonetic - kABStringPropertyType
kABPersonOrganizationProperty; Company name - kABStringPropertyType
kABPersonJobTitleProperty; Job Title - kABStringPropertyType
kABPersonDepartmentProperty; Department name - kABStringPropertyType
kABPersonEmailProperty; Email(s) - kABMultiStringPropertyType
kABPersonBirthdayProperty; Birthday associated with this person - kABDateTimePropertyType
kABPersonNoteProperty; Note - kABStringPropertyType
kABPersonCreationDateProperty; Creation Date (when first saved) - kABDateTimePropertyType
kABPersonModificationDateProperty; Last saved date - kABDateTimePropertyType
kABPersonAddressProperty; Street address - kABMultiDictionaryPropertyType
kABPersonDateProperty; Dates associated with this person - kABMultiDatePropertyType
kABPersonKindProperty; Person/Organization - kABIntegerPropertyType
kABPersonPhoneProperty; Generic phone number - kABMultiStringPropertyType
kABPersonInstantMessageProperty; Instant Messaging - kABMultiDictionaryPropertyType
kABPersonURLProperty; URL - kABMultiStringPropertyType
kABPersonRelatedNamesProperty; Names - kABMultiStringPropertyType
kABPersonSocialProfileProperty kABMultiDictionaryPropertyType

先來看看多項資料,要怎麼處理?

 CFTypeRef *multiValueRef = ABRecordCopyValue(onePeople,propKey);

 //先取得資料的數量
 int numberOfValues = ABMultiValueGetCount(multiValueRef);

 //在來依續取出其中的資料
 for(int i = 0 ; i<numberOfValues ; i++) {
    CFTypeRef *singleValureRef = ABMultiValueCopyLabelAtIndex(multiValueRef,i);
    //單一資料處理
 }

問題來了,取出來的數值型別皆為 CFTypeRef,若是很熟悉 CFType,那直接使用吧,但我就不熟,所以,還是要轉換成 XCode 常用的型別;

 NSString *stringValue = CFBridgingRelease(valueRef);   **//kABStringPropertyType**
 NSNumber *numberValue = CFBridgingRelease(valueRef);   **//kABIntegerPropertyType**
 NSDate *dateValue = CFBridgingRelease(valueRef);       **//kABDateTimePropertyType**
 NSDictionary *dictionaryValue = CFBridgingRelease(valueRef);   **//kABDictionaryPropertyType**

2013年11月13日 星期三

XCode "區塊運算"分享

XCode "區塊運算"分享

Reference from: Blocks Programming Topics
在XCode的開發中,會看到 ^符號,它除了可以作為XOR運算外,在XCode還有一個很常用的操作,^可作為區塊運算的識別字。有關XCode 的區塊運算是一個非常方便的工具,它可以當作 callback function,也可以將一組function直接嵌入你要呼叫的function裡,例如:
//在沒有ARC下,可在ViewController 移除後,將自已給釋放
[myViewController dismissViewControllerAnimated:YES 
    completion:^() {
        [myViewController release];
        myViewController = nil;
    }
];
官網文件中,開宗明義指出You use the ^ operator to declare a block variable and to indicate the beginning of a block literal. The body of the block itself is contained within {}。 '^'符號作為區塊(Block)的開頭文字,在使用方法上,請見下圖不解釋了。

除了官網文件中所提到的範例,另外我也列出一些我在使用上所使用例子:
  • 當 callback function 使用
//MyViewController.h
typedef void (^TouchedInside)();

@interface MyViewController : UIView
{
    __strong TouchedInside  _insideBlock;
}
-(void)setTouchedInsideBlock:(TouchedInside)insideBlock;
@end
//MyViewController.m
@implementation FPTouchView
-(void)setTouchedInsideBlock:(TouchedInside)insideBlock
{
    _insideBlock = [insideBlock copy];
}

@end
  • 把區塊(block)直接嵌入要呼叫函數裡
//Declare in UIViewController.h
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion;
//sample
[myViewController dismissViewControllerAnimated:YES 
    completion:^() {
        [myViewController release];
        myViewController = nil;
    }
];

2013年10月16日 星期三

iOS Development Program (IDP) 瞭解如何設定開發者環境


在註冊成為 Apple 開發者之後,接著便要著手設定環境開始開發 iOS App了;但很多初學者在還沒開始,便已經被如何設定開發環境,搞得昏頭轉向了。 以下便是我作的簡單的整理:

1.取得開發者認證 CA

一位合法的 iOS 開發者,要開發出可散佈的 App,首先要要先從 apple 取得合法的CA (所謂CA,即是Certificate Authority;電子憑證)。而 iOS 開發者認證CA有二種;“iOS Development” ﹠“iOS Distribution” ,就字面意思就是研發用和上架用(其實還有其它四種,這裡先不介紹)。iOS的認證採雙向認證;所以,要開發者自已先產生“憑證要求檔”,再用這個“認證授權檔”上傳至 Apple,來取得你專屬的認證(xxx.cer)。

1.1 建立“憑證要求檔”來向Apple 要求憑證:

先開啟程式“鑰匙圈存取”;在從程式選單選取“鑰匙圈存取“->“憑證輔助程式”->“從憑證授權要求憑證來開啟“憑證輔助程式”視窗。 

1.1.1 使用者電子郵件位址:請填入和開發者帳一致的Email Address.
1.1.2 一般名稱:你的英文名字 或 公司名稱.
1.1.3 已將要求:請選擇“儲存到磁碟”,並勾選取”指定密鑰配對資訊“

按選“繼續”,跳至“密鑰配對資訊”,其預設值為“2048 bit”﹠“RSA”,不用動;再來就一直按“續繼”,直到憑證要求檔建立為止。

當建立憑證要求檔後,在“鑰匙圈存取”的“鑰匙”類別,會產生二個鑰匙。

1.2 取得開發者憑證

1.2.1 上傳“憑證要求檔” 開啟網頁“http://developer.apple.com”,點取“iOS Dev Center”(此時可能會要求你登入,請登入),在右邊的快捷項目中,點取“Certificates, Identifiers & Profiles”;之後,點取"Certificates",來設定開發者憑證。

1.2.2 產生憑證設定檔(Profile) 在“Certificates, Identifiesr & Profiles”頁面下,點取“Certificates”下的“All“。再點取右邉的加號方塊來產生憑證設定檔;當你按了”加號方塊“後,進入Profile 設定頁面。 這時,有個很重要的事,若從來沒有下載和安裝過WWDR憑證(WWDR? 目前,所做的事都是為了取得私鑰,而WWDR就是公鑰啦!開發時需要同時要有公私鑰,請在“Profile設定頁面”的最下方,點連結 “Worldwide Developer Releations Certificate Authority”去下載。下載後,雙擊該檔,就會安裝至鑰匙圈了。

當安裝完Certificate後,在“鑰匙圈存取”的“憑證”類別,可以看到你所安裝的憑證。展開它,可看到它指向產生它的key。


總結上述的步驟;為了要取得開發所需要的電子憑證,要先自行產"憑證要求檔",在產生憑證要求檔後,便會在鑰匙圈內產生專用密鑰;接著登入“Apple 開發者網站”,先取得WWDR公用憑證,依指示上傳“憑證要求檔”,以產生開發者憑證,以上;
但要注意要在發開過程沒有錯誤,這三個東西,一個都不能少;也就是WWDR,開發者憑證和產生憑證用的專用密鑰,全都要都在鑰匙圈內。
有關CA憑證就是這樣啦~ 但可以開發了嗎?答是 ...還早,開發還需Provisioning profiles

2.在“Certificates, Identifiers & Profiles” 頁面中

2.1 Certificates:

用來管理CA憑證的地方,先前就是產生CA的方法。

2.2 Identifiers:

建立和管理另 AppID 的地方, 每一個App都有一組全世界唯一的ID,就在這裡建立管理;除了AppID,另外的是給Passbook和推播用的 ID。

2.3 Device:

建立和管理測試用的iPhone和iPad。一個開發者帳號只能列100個裝備(device)。

2.4 Provisioning profile:

管理Provisioning profile:(它的性質很像是一組設定檔)是Certificate/ID/Devices的集合(注意,我在device 使用複數)。舉例:假設我要開發一個新的App,叫“HappyApp”, 而devices已經建立了我和客戶所有測試用的機器。於是,我先到App ID去建立一組HappyApp專屬的ID,再到Provisioning 建立一組新的開發用profile,在profile設定頁面中,選擇一個開發用CA憑證和”HappyApp”的AppID,再將要測試用的機器勾選起來,最後按建立,便可產生一個給”HappyApp”開發用的provisioning profile
而要給”HappyApp”上架用的profile要另外建立(只需選上架用CA和這個”HappyApp”的ID即可)。最後,將這二個Profiles下載下來,放入XCode的Organizer(在啟起XCode程式,按shift+command+2,即可開啟Organizer)。新建HappyApp這個XCode專案時,要需要AppID一致,並在設定中分別選擇這二個Profiles給開發和上架用,即可

2013年9月28日 星期六

UITextField Usage

UITextField 若需要客製化功能,需要加入UITextFieldDelegate, 使用上:

  1. 在 header 檔內,加入
  2. 在interface builder入,將 UITextField的delegate拉到 File's owner; 若有時候 Controller一直收不到TextField的Event,就大可能是未設定TextField的Delegate;

而在UITextFieldDelegate 中,常用的函數列出如下:

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField;  
//若不允許被編輯,則return NO,  

- (void)textFieldDidBeginEditing:(UITextField *)textField;           
//目前物件成為focus 物件

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField;          
//return YES, 允許結束編輯並重設focus物件的狀能
//return NO,不允許結束編輯`

- (void)textFieldDidEndEditing:(UITextField *)textField;
//??
//may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;   
//return NO,不允許改變文字

- (BOOL)textFieldShouldClear:(UITextField *)textField;        
//當 User按 clear按鈕, 則此函數會被呼叫;return NO 去忽略 clear 命令

- (BOOL)textFieldShouldReturn:(UITextField *)textField;              
//當 User 按 "換行"鈕, 則此函數會被呼叫;return NO 去忽略 換行 命令

以下有我使用上幾個實例,

範例一

我希望當使用者, 點 TextField 時,能跳出一個對話框、執行一個動畫、或一個選單;這時候,我可以使用 UITextField Delegate

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField

進一步,我要 TextField 的內容,皆選單或程式完成,不要使用者直接輸入;這時候,我在這個函數的最後回傳“NO”,即可。有點像將 "User Interaction Disabled".

範例二

這是滿常見的,在將 TextField 的英文,轉換成大寫 (Upper case string)

- (IBAction)textFieldValueChanged:(id)sender {
    firstNameField.text = [firstNameField.text uppercaseString];
}

RN: 2013Oct2:新增 範例一﹠範例二

2013年8月1日 星期四

Apple 的設計真的是走回頭路嗎?

話說今年 WWDC 的開發者大會中, iOS 7.0 的扁平化的設計,當然是有批評的聲音,這些批評直指整個重新的設計沒有新意;讓我們聽聽設計師 evenwu 看出那些內涵了。

2013年5月24日 星期五

為何撰寫 比較有變化視窗程式要學 XAML?

是因為若要開發比較有變化的視窗程式! 所以, 一定要使用 XAML, 比較簡單!

在早期, 有寫過微軟的視窗程式的人應該都知道 MFC4.0, 而Visual C++ 6.0 是活的最久的開發工具. (我在2011年還用了VC6.0 替客戶開發了一整套的應用程式, 只因為要支援 XP SE; 而 .NET3 需要 XP SP3)
在 MFC 的時代, 控制項就那些, 很沒有彈性, 且一點變化也沒有; 而且我們在撰寫時, 隨時要注意有沒有 Memory leak. Memory leak 的測試很花時間, 有時需測到2天2夜才會結果;

進入了 dot COM 時代了, 學了 IUnknown 的觀念, 微軟加強了函數庫的管理和取得的方便性, 和讓 VB/VC/VJ 可共用的函數庫, 也使用了可以互通的資料結構(也就是 Automation 資料型態: VARIANT). 但其實整個視窗開發並沒有很大的變化(只有對網頁ActiveX加強了).

來到了 dot NET 的時代, 微軟使用了 Common Language Runtime (CLR) 機制, 來作資源回收、輾轉呼叫、事件舉發與處理和例外處理. 若對 CLR 很陌生, 沒關係~ 就把它想像成 Java Virtual Mechine 就好了, 只不過是 CLR 是微軟作的, Java VM 是 Sun 作的.

而 dot NET 所開發出來的執行檔(EXE)或動態連結檔(DLL), 在微軟文件稱它叫作 Assembly; 它並不是機器原生碼(Machine Native code), 也就是, 執行電腦內沒有 dot NET 的環境, 它是無法執行的; 和 JAVA 一樣.

Assembly 包含了
  1. Manifest : 包含了全球唯一的識別名稱、版本、文化(Culture)資訊...等等
  2. Type Metadata : 包含了內部所有類別(Class)、資料型別(Type)...等等
  3. MSIL : Microsoft Intermediate Language, 中介語言; 剛提到了 dot NET 開發出來的執行檔並不是機器原生碼, 而所有的程式都會被 Compile 成 MSIL; 而當程式被執行時, CLR 會去開啟 JIT-Compile (Just In Time Compile, 即時編譯器), 透過 JIT-Compile, 產生CPU可執行的機器原生碼.
  4. Resource : 指在開發時, 專案內的圖檔、XAML檔和相關資源檔...等等

前面一大堆, 並都沒提到為何使用 XAML 比較容易開發有變化的視窗程式, 先來看下圖
用XAML開發的程式 

在圖片中,有三個按鈕, 滑鼠在按鈕上會有玻璃特效, 被選取的按鈕有藍色的框, 還有被點擊時, 按鈕有旋轉的特效.. 以上都是 XAML便可達成, 請參考這裡 C# 的指令, 一行都沒加.

在 dot NET 3.0 時(包含3.0之前),只有 Form 視窗程式, 而 Form 視窗程式要作到上述的特效, 那指令可要寫不少; 而在 3.5 之後, 開始支援 Windows presentation foundation (WPF)才開始使用 XAML 語法. 現在, 在 Visual C# 新增一個專案, 仍有 Window Form 和 WPF 可以選擇, 就直接使用 WPF 視窗吧~ 便何況, Visual 2012 己經附了 Expression Blend, 不需要另外購買了.

2013年5月19日 星期日

Visual C#: WPF: Uniform resource identifier (URI)

(Uniform Resource Identifier)URI 統一資源識別元,繼承階層為 System.Uri. 就是以物件的方式去記錄一筆資料,而資料內容就是指到一個資源(這資源可能是 一個圖檔、或一個資料庫、或任何 WPF 的資源檔).

URI 類別和 XCode中的 NSURL 類別很像,而在下面我將協助瞭解 * Uri如何使用:Uri的建構字串 * pack::Pack URI 字串使用的概念:

Uri如何使用:Uri的建構字串

在下面的範例中, 將建立URI的物件.

Uri myPicUri = new Uri("http://www.xxx.com/thePicture.png");  //指到網路某一個圖檔
Uri myBlogUri = new Uri("http://cypress-soho.blogspot.tw/");  //指到本部落格

但如何指到自身程式(Assembly)內的資源檔,那就需要 Pack URI

pack:Pack URI 字串使用的概念:

Pack URI包含了二個元件:授權/路徑.其格式如下

pack://*authority*/*path*

而WPF支援兩種授權:appliction:///siteoforigin:/// , application:///授權 可識別編譯時期己知的應用程式資源檔案.而siteofiorigin:///授權可識別來源網站.

封裝 URI 圖表

而另外需要注意 Pack URI可提供給開放式封裝慣例(OPC)使用,故需要符合OPC要求, 以致 "/"字元必須取代為","字元. 所以在正式使用上,字串為以application:,,,來取application:///

Uri myPng = new Uri("pack://application,,,/cat.png");       //指到Assembly內的cat.png

2013年5月17日 星期五

Markdown 語法 (syntax) 和 範例 (sample)

Markdown 是一個非常方便用來寫部落格的語法。可以透過一些定義好的符號或格式,便可輕易的編排你的文章,先不需要使用大量的HTML,雖然最後仍是需要轉換成HTML。先讓我們先下面的範例:
> ### This is a header
> 
> 1. This is the first list item.
> 2. This is the secord list item.
如此所產生的結果如下:

This is a header

  1. This is the first list item.
  2. This is the secord list item.
就以上所產生的結果,我們可以看到
  1. Blockquotes: Email形式的區塊引言,也在左側上一條灰色的直線,它是利用一個">"的符號所產生的。
  2. 標題:Markdown 所支援的標題從 H1 到 H6。H1 字型最大,而 H6 字型最小;它是以"#"符號來表示,一個#為 H1;而六個#為 H6;
  3. 有序清單:以一個數字開頭接著一個小數點,後面至少要接著一個空格,來表示。另外,還有無序清單,可用符號"*" "+" "-"一個加一個空格來表示。

除了這些,還有
  • 程式碼區塊:以一個tab或四個空格開始,結果會產生如下圖,一個區域。
    我是程式碼區塊
  • 分格線:用三個或以上的星號、減號、底線來建立分隔線。
  • 連結:在字串中,有個關𨧞字會連到指定的網址。以[方括號]標記,接著以括號填入綱址如下:
     如同我在[之前](http://cypress-soho.blogspot.tw/)所提到的... 
  • 強調:在要__強調__的文字,在前後加入一或二個的星號或底線。
    *singleasterisks*
    _single underscores_
    **double asterisks**
    __double underscores__
    
    結果為
    singleasterisks
    single underscores
    double asterisks
    double underscores

  • 坎入圖片:和連結有像,但差別在多了一個驚嘆號。
    ![圖片的替代文字](http://localhost/cat.png)
有關更詳細 markdown語法,請看這裡

2013年4月22日 星期一

Xcode : 如何開啟 facebook 粉絲頁,在facebook app 或 Safari 上

開啟 facebook 粉絲頁,簡單一個指令搞定。
[[UIApplication sharedApplication] openURL: [NSURL URLWithString:@"https://www.facebook/com/taiwanbreezeman"]];
若只是這樣,就遜了。 當 iPhone上有安裝 facebook app,最好能直接打開在 facebook app上。以下便是範例:
NSURL *url = [NSURL URLWithString:@"fb://profile/277473558949767"];
[[UIApplication sharedApplication] openURL:url];
問題來,在 profile 後面那一長串的數字是什麼?? 那便是 facebook 的 profile id。在我解釋如何取得profile id 前;容我介紹一下,social graph 和 profile id 的關係。
social graph 這個詞是2007由 facebook 所提出,其概念很簡單,每個人/事/物都是一個點;而關係是一個便是一條線。你,和親朋好友的關係;和就讀過的學校的關係;和工作的地點的關係;和去過的地點/餐廳或店家的關係; 想像一張白紙,在白紙的中心劃一個點代表你自已,在你自已點的周邊,點上代表著親朋好友/學校/公司/景點或店家的點。再將代表你的點和這些點以線代表關係,連起來。便可形成如下圖(來自Wiki),屬於你的 social graph。
Social_graph

而每一個點都有一個 profile 來描述這個點它代表的人/事/物。而每個點都擁有一個獨一無二的編號,那就是 profile id。
好,言歸正傳,取得 profile id 的方法有二種。 一種是,它就顯示在網址列上:
在網址上

而另一種,無法在網址列上找到。如下圖,是以關鍵字的方式顯示;那就需要 facebook另一個網址來查詢了,查詢網址格式為:"http://graph.facebook.com/{關鍵字}"。頁面顯示便是profile 的內容,在內容中找尋一個名為 "id" 的字串,後面所帶出來的數字,便是 profile id。
透過facebook

2013年4月10日 星期三

Xcode: 讀取 (read) / 寫入 (write) plist file

要開啟 plist file,首先要先知道 plist file 的根 (root) 是何種資料型態。"根"的資料型態有二種:Array / Dictionary。
plist 的根,只能是Array或Dictionary
依資料型態選擇不同,使用不同類別來開啟。

* Array範例:

NSString *plistPath = [[[NSBundle mainBundle] bundle]
stringByAppendingPathComponent:@"MyPlist.plist"];
NSArray *plistArray = [NSArray arrayWithContentsOfFile: plistPath]; //讀取plist file
NSString *propertyString = [plistArray objectAtIndex:2];            //取得字串物件
NSInteger *propertyInt = [[plistArray objectAtIndex:2] intValue]; //取得整數值
Bool propertyBool = [[plistArray objectAtIndex:2] boolValue];    //取得布林值
float propertyFloat = [[plistArray objectAtIndex:2] floatValue];   //取得浮點數值
[plistArray writeToFile:plistPath atomically:NO]; //回寫plist file

* Dictionary範例:

NSString *plistPath = [[[NSBundle mainBundle] bundle]
stringByAppendingPathComponent:@"MyPlist.plist"];
NSDictionary *plistDic = [NSDictionary dictionaryWithContentsOfFile: plistPath]; //讀取plist file
NSString *propertyString = [plistDic objectForKey:@"姓名"];            //取得字串物件
NSInteger *propertyInt = [[plistDic objectForKey:@"年齡"] intValue]; //取得整數值
Bool propertyBool = [[plistDic objectForKey:@"已婚"]  boolValue];    //取得布林值
float propertyFloat = [[plistDic objectForKey:@"身高"]  floatValue];   //取得浮點數

[plistDic writeToFile:plistPath atomically:NO]; //回寫plist file

2013年4月9日 星期二

Xcode : plist file 都支援那些資料格式















在上篇提到,plist file 可以儲存設定 / 資料,但其支援的的資料格式有那些?上圖為從 Apple developer 截圖下來的。一般而言,資料格式有 NSArray / NSDictionary / NSString / NSData / integer / float / Boolean。


  • NSString:儲存字串;
  • NSArray:objective-c 內的陣列型態,和C語言的陣列有些不同,在C語言宣告一個陣列為 "int index[10]",這個 index[] 僅能存放整數,依宣告不同的資料型態,而存放不同類型的資料;而NSArray的第一個不同是,僅能存放 objective-c 的 "id",所謂 "id" 即為物件。第二個不同是不能在放 int 資料,因為 int 資料不是物件,而需要將 int 資料轉為 NSNumber 物件來儲放。第三個不同是NSArray 僅能夠儲放 "id",只是物件都能儲放,可在第零位置上放置字串,第一放置NSData;並不限制要同一種資料型態。
  • NSDictionary:NSArray 這麼好用,在一個陣列中,可以放不同的資料。問題來了,不同位址放不同型態的資料,很難不搞混吧。我也不這麼作,風險太大了。用NSDictionary 吧!NSDictionary 用的不是位址來儲放資料,而是以key值來作索引,例如:以 "name" 來作姓名的索引,便可取出姓名的字串,或用 "age" 來作年齡的的索引,便可取年齡的整數值;
  • NSData:用來儲存 NSData物件,只要是能轉成 NSData的物件皆可;我曾將幾張小圖,分別用 NSData 物件存在 plist file,照樣可以用。
  • integer / float / Boolean:全部都是用 NSNumber 物件來儲存。寫入和讀出的指令要一致;千萬別寫入時用 numberWithBool 寫入boolean 值,而讀取時用 intValue,讀出來的卻是整數值,那就糗了。


ref: Apple developer