2013年12月10日 星期二

iOS 開發筆記 - 使用 NSNotification 傳遞與接收事件

通常會設計多個 UIViewController 供使用者觀看與操作,如果 AUIViewController 提供設定參數後,當使用者切換到 BUIViewController 可以馬上就使用到新資料來操作時,這時可以把資料儲存在指定地方,當 BUIViewController 之 viewWillAppear 裡就能夠去重讀資料來用。

然而,有時希望 AUIViewController 改完資料時,可以馬上讓 BUIViewController 也得知,這時做法至少有兩種,第一種就是讓 AUIViewController 可以接觸到 BUIViewController ,例如把 BUIViewController 當作一個 member variable ,也就是 AUIViewController.member = BUIViewController object,只是這招有點破壞架構的美感,但偶爾使用還滿堪用的!第二種,就是採用 Notification 架構,讓 BUIViewController 聆聽專屬於它的事件,而 AUIViewController 在需要的時候送事件出去即可,此架構更加符合 async 。

此例筆記 NSNotification 的用法(此架構也稱 radio station,Android 派則是 broadcast 啦):

BUIViewController.h:

#define kTargetDataUpdate @"TargetDataUpdate"

BUIViewController.m 之註冊聆聽事件:

- (void)setupOptions:(NSNotification *)event
{
        NSLog(@"event:%@, object:%@", event, event.object);
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupOptions:) name:kTargetDataUpdate object:nil];

}


BUIViewController.m 之移除聆聽事件:

- (void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name: kTargetDataUpdate object:nil];
    [super viewWillDisappear:animated];
}


AUIViewController (或其他物件亦可) 之發送事件:

NSDictionary *info = @{@"status":@"ok"};
[[NSNotificationCenter defaultCenter] postNotificationName: kTargetDataUpdate object:info userInfo:nil];

2013年12月9日 星期一

iOS 開發筆記 - Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier myCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

AttriubuteTableViewCellIndentifier DocumentOutlineTableViewCell

最近摸 Storyboard 一陣子,偶爾還會犯一些傻 bug ,就是 Identifier 設錯地方 XD 例如跑去 Identity Inspector 的 Identity 之 Restoration ID :

IndentityTableViewCellRestorationID

結果程式一跑時,就噴訊息了:

** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier myCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

解法就是開啟 storyboard 後,仔細查看左邊的 Document Outline 之 UITableViewController 裡的 Table View Cell 是不是有被標記起來,接著去看看右邊 Attributes Inspector 顯示的 Identifier 是不是跟程式碼一樣的,如此就能 debug 啦。

忘記是不是 iOS 5 SDK 開始,預設的 UITableViewController 的範例程式都是寫成綁定在 Interface builder (xib) / Storyboard 的寫法:

static NSString *CellIdentifier = @"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];


早期 iOS 4 SDK 預設 UITableViewController 之 (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 寫法:

static NSString *CellIdentifier = @"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}


早期 Interface builder (xib) 的用法,須寫程式註冊 Nib:

- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:@“MyCell" bundle:nil]
forCellReuseIdentifier:@“myCell”]; // "MyCell" for MyCell.xib
}

2013年12月3日 星期二

iOS 開發筆記 - 解決 Storyboard UITableViewCell contentView 第一次設定 subViews frame 失效問題

為了讓自己趕快習慣 Storyboard 開發模式,瘋狂地用 Storyboard 做事 XD 除了降低原本的開發速度外,接著碰到一些好像是 bug 的奇妙問題?

這個問題是在 Storyboard 內已經拉好一個 UITableViewController ,接著在裡頭增加 UITableViewCell 後,再上頭加多個 ImageView 把玩,然而,希望 device rotation 後能後平均地顯示在 UITableViewCell 裡頭,例如有五個 ImageView 可以平均地分布。

此時在 (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 已經完成重新計算各個 ImageView 的 frame 座標。

由於 Storyboard 是以 Portrait 模式設計,因此裝置一開始從 Portrait 轉換到 Landscape 時,一切都正常顯示。但如果裝置一開始就是在 Landscape 模式時,反而那些 ImageView 卻還是依照 Storyboard 裡的 Portrait 模式設計,無論我把他們的 frame 座標更新也無用。(但經過 device rotation 後,會顯示正常)

不曉得這是不是 bug ?總之,最後就想到一招就是第一次強制 ImageView 重新歸位 XD 希望這招之後可以拿掉。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
        // ...

        if( cell.tag == 0 )
        {
                cell.tag = 1;
                for (UIView *v in [cell.contentView subviews])
                {
                        [v removeFromSuperview];
                        [cell.contentView addSubview:v];
                }
        }
        return cell;
}

iOS 開發筆記 - iOS app 狀態為 Pending Contract 的處理

通常看到這樣的問題,都是忘了填寫銀行等資訊,不過,若發生在免費的 iOS app 時,這時就請打電話給客服聊聊天吧(Worldwide Telephone Support,台灣打 0800-022-237 )

此例是登入 iTunesConnect 後,上頭一直顯示 program membership expired 訊息,但明明我幾個月前已經 renew 過了 :P 同理切換到 Contracts, Tax, Banking 裡頭,也叫我去 re-new 開發者帳號...打電話問客服...報一下 Apple ID (Email address) 後,得知是當初 re-new 時少完成一個流程(可能是少了某表單的同意,但現況又看不到表單),導致在 iOS Developer Center 是 OK ,但在 iTunesConnect 是不 OK 的現象,解法就是聊個天就解掉了...

2013年11月29日 星期五

iOS 開發筆記 - 重溫 iOS app development


Developing iOS 7 Apps for iPhone and iPad
by Stanford

這星期一口氣連追了 12 堂課,把史丹佛 CS193P 2013 Fall 目前放出來的課程看完了 XD 距離我上一次看它可能是 2011 年,而我第一次看則是 2010 年的春天。說真的,老外上課有豐富的肢體語言,所以不必真的聽懂每一個字,看看投影片、程式碼就真的都學得會啦。

過去從 iOS 3.1 開始接觸 iPhone app development ,跟幾個同好討論怎樣把 UI 搞的炫炫的等等,當時 iOS 3 的特色大概是提出 In-App purchases,接著有幸還追到 iOS 4,帶出了 Background Tasks 跟 Objective C blocks 的概念,之後,我漸漸落伍了,如 iOS 5 提出 Storyboard 、iOS 6 帶入 iCloud 到現在的 iOS 7 等,基本上都沒有正式用過。

最近看了 CS193P 後,才真的理解 Storyboard 跟 Core Data 的好用之處,的確,沒有他們一樣可以做完事,但我相信,再過個一兩年,可能就完全跟不上潮流了

此外,想起前陣子跟新學 iOS 的聊天,被問起到底會什麼,說真的這種問題不是很好 Orz 不是不願意回答,而是答出來跟沒答出來的價值差不了多少。舉個例來說, C 跟 C99 的語法差在哪邊,知道或不知道都不會很大地影響一位工程師,同理 iOS 一直在更新,工作這種事,碰到在學就對了。至於 C 跟 C99 差別嘛,只要記得會用 Google 去查一下就好 XD

經過這次的複習,深深覺得資訊界的技術真的很難撐過兩年 XD 其中的心酸豈可不是 out of date 幾個字可道盡的。