2012年4月30日 星期一

storyboard和xib不同的人生道路



從Storyboard和Xib產生的view controller其實在一開始初始化時即有著細微的差異。如上圖所示,雖然最後都會來到viewDidLoad的終點,但在之前卻各自經歷不同的人生。從Storyboard產生的view controller比較坎坷,繞了遠路,經歷initWithCoder:和awakeFromNib,從xib產生的view controller只經歷了initWithNibName:bundle:。除了呼叫method的差異,還有以下幾點需注意之處:

1. IBOutlet連結的元件要等到viewDidLoad裡才出現。
 如果我們太心急,在awakeFromNib即存取,只會空手而回,取到空氣。(nil)

2. 當記憶體不足時,viewDidUnload可能被呼叫。到時候view controller的畫面要再出現時,viewDidLoad將再度被呼叫。因此viewDidLoad可能被多次呼叫,不適合將只想做一次的事置於其中。比方計數的count變數,若是在viewDidLoad時設定初始值0,那麼當 viewDidLoad再次被呼叫時,辛苦累積多時的count就被清空了,多可惜呀。所以我們最好將設定初始值的程式碼置於initWithCoder: , awakeFromNib, initWithNibName:bundle:。



2012年4月28日 星期六

App開發的Reversibility

就像張信哲的出道成名作"我們愛這個錯"的歌詞描述般,
"每個人的臉都變了,不哭不笑也不在乎 ,
曾經是你的雙手拉著我,一次一次對我說 ,
讓我永遠愛妳,永不離開妳 ,
山盟海誓已忘記 天崖海角何必尋覓"

就算是山盟海誓,都有變調的可能,更何況是App的開發呢? 所以有經驗的人談戀愛隨時做好對方變心的心裡準備。同樣的,有經驗的人開發App時,隨時做好功能修改,畫面翻新的準備,開發時一定要儘可能地保持彈性。

Tip: There Are No Final Decision

最好能有什麼都會改變的心裡準備,不要有某某方案是唯一解法,就算滄海變桑田也不會改的想法。比方原本我們針對台灣的市場需求,設計一個虛擬女友的App,但也許在某個神秘的國度裡,男生比女生還稀有,那兒的女生需要虛擬男友的陪伴。所以若能在一開始設計App時多方設想,保有彈性, 即可在一開始設計時加入此考量,而不是完全假定主角一定是女生,玩家一定是男生。

HTTPS加密的V.K克鋼琴大賽

第一屆琴之翼V.K克國際鋼琴大賽 即將開始,



可惜彼得潘沉迷於App開發多年,
早已忘了如何彈琴。
但是,
彼得潘的字典裡沒有放棄呀,
為了提醒自己練習,
彼得潘決定將比賽的圖片收藏於Server,
每天經由iPhone連到Server觀看比賽圖片,
提醒自己比賽日期的逼近。

接下來彼得潘將示範如何經由iPhone存取遠在天邊Server上的圖片。
存取圖片沒什麼特別,
然而當它是https時,
就有些值得一提的密技了。

1. 經由https下載圖片

2. 宣告儲存下載資料的receiveData

我們從遙遠天邊網路下載的資料為NSData型別,由於一張圖片可能很大,我們將收到一連串的NSData物件,因此我們宣告NSMutableData型別的receiveData,利用它蒐集所有收到的NSData物件。一定要湊齊構成圖片的所有NSData,美美的圖片才能完整呈現。


3. 實作NSURLConnectionDataDelegate和NSURLConnectionDelegate宣告的接收資料相關callback method。

執行App:
https果然不是好惹的,
我們完全下載不到任何資料,

connection:didFailWithError:的訊息殘酷地出現在我們眼前。

-[TestHttpsViewController connection:didFailWithError:]

4. 破解https


在iOS 5之後, 破解https變得更容易了, 只要實作connection:willSendRequestForAuthenticationChallenge:。
但在iOS 5之前, 稍微麻煩點, 需要實作以下2個method。 connection:canAuthenticateAgainstProtectionSpace:
connection:didReceiveAuthenticationChallenge:

執行App:


距離2012.8.11還有約100天。
志在參加,
還要得獎。
接下來彼得潘要白天寫App,晚上練琴了!

2012年4月27日 星期五

解除spotlight顯示檔案路徑的封印

Mac上強大的spotlight方便我們快速找尋檔案。
然而有些時候我們想知道的其實是檔案真正的路徑。
其實spotlight知道答案,
但我們必須給它通關密語,
它才願意顯示檔案路徑。
接下來彼得潘將以找尋甜美的Kiss照片為例說明。
(感謝百吻巴黎楊雅晴提供照片)

從spotlight找到目標後,
將游標移至目標上。

此時將浮現quick look視窗。





按住command鍵,
此時quick look視窗將產生奇妙的變化。



當我們按住command鍵時,
quick look視窗裡圖片下方的文字將一下子顯示檔名,
一下子顯示檔案路徑地輪播。
由上圖我們可發現圖片所在位置為 peterpan/kiss下。

Tip:
  按住command鍵,解除spotlight顯示檔案路徑的封印


2012年4月24日 星期二

翻滾吧,紅茶 - 偵測iPhone的晃動


有些時候,我們並不特別需要知道iPhone傾斜的角度,而是單純想偵測使用者是否晃動iPhone,比方像App LINE一樣利用晃動來和鄰近的陌生人交朋友。接下來,彼得潘將繼續以可愛的紅茶做主角,拍攝翻滾吧,紅茶,每當我們晃動iPhone,紅茶的特寫圖片即優雅地旋轉15度角。

(1) 建立Single View Application專案ShakeTest。

(2) 新增連結至紅茶圖片的dogImageView和儲存圖片旋轉角度的rotateDegree。


(3) 開啟ShakeTestViewController成為first responder的能力。



說明:
想要讓ShakeTestViewController能夠偵測到iPhone的晃動,就好像想看到鬼般,必須先啟動某個開關。只要打開陰陽眼,我們就可以看到鬼。不過這實在太危險了,我們還是打開偵測晃動的開關就好。在iOS的國度裡,只有兩種物件遺傳偵測晃動的血液,一個是UIView,一個是UIViewController。但除此之外,還必須成為first responder才能真正偵測到晃動。由於view controller預設將成為first responder的能力封印起來,因此我們得先覆寫canBecomeFirstResponder,回傳YES解除封印。另外在我們的例子裡,由於沒有其它物件和ShakeTestViewController搶,所以它當然而然成為first responder的不二人選,否則在多人競爭的環境,我們還必另針對欲偵測晃動的對象呼叫becomeFirstResponder


(4) 定義晃動發生時觸發的method motionEnded: withEvent:。


說明:
1. -(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event

當晃動發生時,此method將被呼叫。

2. if(motion == UIEventSubtypeMotionShake)

目前當此method被呼叫時,傳入的motion其實一定是UIEventSubtypeMotionShake,不過為了保險起見,我們還是做個比對,確定真的是晃動。

3.  dogImageView.transform =
          CGAffineTransformMakeRotation(2*M_PI*rotateDegree/360);

利用CGAffineTransformMakeRotation將圖片順時針旋轉。rotateDegree為旋轉的角度,在這個例子裡,每晃動一次,我們即旋轉15度。(一圈是360度。)

執行App:


 為了夢想,盡情地翻滾吧,紅茶!

2012年4月23日 星期一

帥氣的彼得潘簽名

1. 將字型檔加入專案
   加入DFLiuStd-W3.otf

2. 設定Font provided by application
    於其中輸入字型檔的檔名


3.  設定Label的font
利用UIFont的fontWithName:size:設定字型

其中的關鍵在於傳入的fontName。
我們利用fontNamesForFamilyName:列出所有的font,
從中找到DFLiuStd-W3。


執行App:


2012年4月21日 星期六

彼得潘變身蝙蝠俠的重複問題 (The Evils of Duplication)



外在的環境時刻在變,App的開發亦然。整個美術設計或是操作流程一瞬間打掉從練已經是見怪不怪,App程式設計師必須努力去適應的事情。

然而此時程式的寫法就很重要了。比方原本App的魔王是佛地魔,彼得潘變身為哈利波特的程式碼重複出現於多個重要關卡。到時候當規格將魔王改為小丑時,彼得潘必須變身為蝙蝠俠,但若是我們有一處遺漏忘了改正,玩家將看到哈利波特對決小丑的詭異畫面。

Tip: Don't  Repeat Yourself
解決重複問題的方法很簡單,只要學會不要重複。然而就像不說冷笑話,即可解決冷場問題般,都是說來容易,實現超難! 要不說冷笑話,得了解冷笑話的成因。同樣的,接下來我們也來分析常見造成重現象的原因和解法:

1.  Imposed Duplication
人在江湖,身不由己。環境因素使然,不得不重

(1) Multiple representations of information
很多時候,同一個資訊在Server端和App端都需要。以音樂App為例,在Server端定義了表示專輯的table,而App端也同樣需要定義一份。然而,到時候如果專輯table需要新增欄位,由於重複問題,我們需要同時修改兩個地方。

解法:
利用code generator。比方將專輯需要的欄位定義為xml檔。到時候Server端和App端只需讀取此xml檔,然後利用code generator 即可產生新的專輯table,因此每次修改只要更改xml檔一處。

(2) Documentation in code
程式的comment常常是造成重複的凶手! 如果comment鉅細靡遺地說明程式的實作,當程式修改時,comment也需要一併修改。但往往隨著日積月累,程式已經改了不下千回,comment卻還是原封不動,保持在最初的狀態。這樣子的comment,比沒有comment還糟,因為它將讓後繼接手的人誤解,不知該相信comment還是程式碼。


解法:
好的程式碼一目瞭然,其實不太需要comment。如果真的需要寫comment,不寫對不起自己的良心,也請謹記只須表達此段程式的目的,而不用解說實作的細節。

(3) Documentation and code
文件和程式往往保有一定程度的相依性,造成不得不然的重複。

解法:
類似code generator的概念,從文件產生程式。

(4) Language Issues
因為程式語言的特性造成重複。比方C語言裡常常看到同樣的東西必須於.h宣告,.c裡定義。

解法:
利用一些自動化的工具幫忙。比方從.c的定義自動產生.h的宣告。


2.  Inadvertent Duplication
不自覺的重複。此問題往往緣由於不良的程式設計。比方我們定義了Book類別,於其中宣告以下3個member。
(1) price
(2) amount
(3) priceSum
其中priceSum等於price * amount。因此每當定價或銷售量改變時,我們也要記得更新priceSum。惱人的重複問題就這樣靜稍稍產生了。因此,比較好的寫法是將priceSum定義為method,而不是member,如此即不會有忘了更新的問題。


3. Impatient Duplication
人類的懶惰天性使然。以前面提到的彼得潘變身哈利波特為例,若是我們能夠定義成method,於傳入的參數指定變身後的角色,即可解決重複問題。然而往往因為schedule的壓力,或是趕著下班後和女朋友的月球漫步,我們直接將原來的程式碼copy paste。反正功能完全正常,而且還能節省時間提早將App上架,自己,老闆,女朋友,消費者全都開心。可惜,事情往往無法盡如人意。過不了多久,當規格修改,由於重複問題,我們需要徹底清查程式碼每個角落,將彼得潘變身為蝙蝠俠。如果不小心遺漏了,還得花更多的時間找尋和加班。記住,出來混總是要還的,到頭來只會自己,老闆,女朋友,消費者都不開心!

4. Interdeveloper Duplication
因為團隊合作造成的重復。此問題是所有重復問題裡最難解。畢竟自掃門前雪尚且不易了,更何況是團隊成員間的溝通協調。由於缺乏溝通,彼得潘寫了一份變身哈利波特的code,虎克船長也寫了份變身佛地魔的code。

Tip: Make It Easy to Reuse 
建立一個易溝通,彼此常交流,方便Reuse的環境是解決此問題的不二法門。比方我們可以建造一個團隊的知識庫Wiki,從中找尋是否已有同仁寫好變身的程式。變身如此不易,如果已經有人實作,何必再自己打造呢? 況且如果寫得不好,到時候變成科學怪人可就不妙了! 




 


2012年4月18日 星期三

難纏的兔子王 - UIViewContentModeScaleAspectFill的陷阱


彼得潘的畫家友人Jorinde Jankowski (張友鷦) 有天突發奇想,
將結在樹上的Apple換成了可愛的兔子。




不過彼得潘身為蘋果迷,
當然想將兔子換回原先的蘋果。
所謂擒賊先擒王,
樹中間的兔子是我們的首要目標。
接下來彼得潘將利用UIViewContentModeScaleAspectFill,
只顯示圖片的中間區塊,
排除中間的兔子王。


利用UIViewContentModeScaleAspectFill以及將frame設為300*150,
應該可以讓原本577 * 722圖片縮放至300* 375 ( 375 = 722*300/577.0)
並且以中心為準切除上半部和下半部各112.5。
然而程式執行後,
結局卻出乎意料



沒想到,
竟然顯示完整的圖片,
圖片的大小和位置,
完全不是設定的(10, 180, 300, 150),
更可惡的,
彼得潘的眼中釘,中間的兔子王還在。
為了找出問題,
彼得潘再加上一塊半透明的長方形,
此長方形的frame設定為和彼得潘想要的圖片frame一致。




如上圖所示,
彼得潘想要的圖片大小和位置,
其實是圖片的中間區塊。
實現願望其實很簡單,
只要加入以下程式碼即可 

    imageView.clipsToBounds = YES;

clipsToBounds的預設值是NO,
將其設為YES,
即可讓超出範圍的部分被活生生的割除!


若是從Storyboard建立的ImageView,
在將Mode設為Scale To Fill時,
顯示的預覽將是手術成功後的結果。


不過想不到彼得潘又上當了,
由於Clip Subviews沒有勾選,
實際執行時顯示的還是手術前的模樣,
兔子王還是得意地在樹上搖擺。
因此,
記得要勾選Clip Subviews,
它的效果等同於將clipsToBounds設為YES。






2012年4月13日 星期五

飛天豬的category外掛




   小豬有沒有可能像彼得潘一樣翱翔於天際呢?
   只要發揮想像力,
   沒有什麼不可能的!
 
 
   接下來彼得潘將示範如何利用Objective-C獨門的category,
   施加魔法,
   讓Pig物件具有飛翔的能力。
   (感謝藝術家朋友Jorinde Jankowski (張友鷦)提供可愛的飛天豬)

1. 新增category


2. 設定category的名稱以及外掛作用的類別
   
    如下圖所示,
    Category填入Fly,Category on填入Pig,
    到時候category Fly將讓小豬比人類先一步長出翅膀。



    新產生的category檔案包含.h和.m
    檔名Pig+Fly其實正是外掛作用類別和category名稱的結合。




3. 定義category
    定義category的語法其實和定義類別雷同,
    只不過category名稱必須加在類別名稱後的 ( ) 裡。


 
 有一點值得注意的,
  category只能擴充原有物件的能力,
  因此其只能增添新method,
  並不能增加instance variable。
  比方Pig原本沒有hair,
  我們無法利用category讓其多了hair attribute。

4. 測試


為什麼不用繼承?



如果我們定義繼承自Pig的FlyPig類別,
不是也能實現飛天豬嗎?
category的最大好處在於它擴充的是原有物件的能力,
因此只要是Pig物件,
即可飛翔。
但如果採用繼承,
只有FlyPig可以飛翔。
尤其如果是內建的類別,
我們更可以體會category的美妙。
比方我們想讓NSString物件具有顛倒字串的能力,
此時我們只要創建作用於NSString上的Reverse category,
再定義reverseString method
即可讓所有的NSString物件皆具有顛倒是非,
哦,說錯了,
顛倒字串的能力。


Core Data的應用
我們也常利用category擴充NSManagedObject subclass的能力。
因為如果我們直接將新能力定義於NSManagedObject subclass,
每當我們修改表格欄位,
重新產生檔案時,
原來辛苦填寫的新method將消失無蹤







改頭換面的Tab Bar

感謝藝術家朋友Jorinde Jankowski (張友鷦)提供的可愛圖片,
彼得潘接下來將利用這些活靈活現的動物主角表演tab bar的換裝秀。

1. 修改tab bar顏色
    tabBarController.tabBar.tintColor = [UIColor brownColor];



2. 修改tab bar的背景圖片
     tabBarController.tabBar.backgroundImage = [UIImage imageNamed:@"bar.png"];
     image的高度決定了bar的高度。




     
3. 修改被點選tab上icon的顏色
     tabBarController.tabBar.selectedImageTintColor = [UIColor orangeColor];
     未被點選tab上的icon顏色將是tab bar tintColor的相近顏色。



4. 與眾不同的tab
    想要隨心所欲,讓tab擁有獨一無二的外觀?
    很簡單,只要建立UIButton,
    再將此button貼到tabBar上,
    佔據原先tab所佔的空間,
    即可打造天馬行空的外觀,


    不過有一點別忘了,
    當按扭被點選時,
    需設定UITabBarController物件的selectedIndex才能切換至tab對應的頁面。

    



圖檔尺寸和命名

tab bar icon:
     format:  png
     name:  @2x for  high-resolution
     size:    60 * 60 for high-resolution     
                30 * 30 for low-resolution


2012年4月8日 星期日

邀請其它人加入iOS App開發團隊

邀請別人

1. 於iOS Dev Center點選右上方的Member Center


2. 點選左上方的people


3. 點選Invitations



4. 點選Invite Person


5. 填寫被邀請人的資訊

    First Name, Last Name以及Email Address都要和被邀請人的Apple ID一模一樣。



6. 點選右下角的Send Invitation



被邀請


1. 收到邀請的email


2.  點選submit





2012年4月2日 星期一

觸發右鍵選單


1. 啟動 系統偏好設定

2. 點選 觸控式軌跡板



3. 於輔助按鈕,可選擇如何觸發右鍵選單。
    有以下三種方式:

    (1)  以兩根手指輕點 ( 只要輕輕地點即可)

    (2)  按一下軌跡板右下角 (確實按下,有按鍵聲)

    (3)  按一下軌跡板左下角  (確實按下,有按鍵聲)



      最後,還有一招萬靈丹,
      按住control鍵不放,然後點擊軌跡板,一樣能夠觸發右鍵選單。