創建一個iOS應用程序
在表面看,創建一個iOS應用程序和創建一個Mac OS X應用程序很像。使用相同的工具和許多相同的基礎庫。盡管很像,還是有些顯著的不同。iPhone不是桌面計算機,有不同的預期,需要完全不同設計思路。需要從iOS的能力獲得優勢,還要避開那些對移動環境無關和不切實際的特性。iPhone和iPod touch的小尺寸屏幕意味著,你的應用程序用戶界面應該合理組織,并總是關注于用戶最需要的信息。
iOS運行用戶跟iPhone和iPod touch的設備,用桌面程序無法實現的交互方式來交互。多點觸摸(Multi-Touch)是一種革命性的接收事件的新方法,報告每一個獨立的手指對屏幕的觸摸,并使處理多手指手勢和其他的復雜輸入變得非常簡單。此外,內建的硬件特性,例如加速度傳感器,雖然也在一些桌面系統使用,但是在iOS中使用的更佳廣泛,可以跟蹤屏幕的當前方向并相應的調整你的內容。了解如何在你的程序中使用這些特性,有助于你聚焦適于用戶的設計方式。
了解iPhone程序設計的最好辦法就是看例子。本文針對例程MoveMe進行介紹。下面的例子展示iOS程序的典型性為,包括:
◆初始化程序
◆顯示窗口
◆描繪定制內容
◆處理觸摸事件
◆進行動畫
圖 1 展現了應用程序的接口。觸摸Welcome按鈕會引發一個動畫,按鈕會跳動并把自己的中心移動到你的指下。手指在屏幕上移動,按鈕就會跟著你的手指移動。把你的手指從屏幕上拿開,引發另一個動畫,按鈕跳回它的原始位置。在按鈕以外的任何位置雙擊屏幕會改變按鈕歡迎詞的語言。
圖1 MoveMe程序的窗口
閱讀本文的其他部分前,你應該下載例子(MoveMe),這樣你可以直接參照源代碼學習。你應該已經閱讀了iPhone開發者中心下面的指南文章,對iOS和開發需要的工具和語言有了基本的了解:
如果你對Objective-C編程語言不熟悉,你應該閱讀學習Objective-C:入門手冊讓自己對Objective-C的基本語法有所了解。#p#
查看MoveMe例子工程
下載MoveMe例程,你可以得到構建和運行程序需要的源代碼和支持文件。你應該使用Xcode應用程序(位于 /Developer/Applications)管理iOS項目。每個Xcode項目窗口包含一個工作空間用來組織代碼和資源文件,編譯和組裝程序的構建規則,編輯和調試代碼的工具。
圖 2現實MoveMe程序的Xcode項目窗口。要打開這個項目,首先把它復制到本地硬盤,然后雙擊MoveMe.xcodeproj文件即可。(你可以在可以在Xcode選擇菜單File > Open然后選擇文件。)項目包含了多個Objective-C源代碼文件(擴展名為.m),一些圖像文件和其他資源,以及構建程序包的預定義目標。
圖2 MoveMe項目窗口
在iOS,Xcode項目的最終目標是程序包,一個特性類型的目錄,放置程序的二進制執行文件以及資源文件。iOS包是一個相對平面的目錄結構,包含的大多數文件都在包目錄的頂層。然而,包文件也可以包含子目錄去存儲字符串的本地化版本以及其他的語言相關的資源文件。本文不需要你了解程序的確切結構,但是你感興趣的話可以在iOS編程指南程序包章節找到這些信息。
構建MoveMe程序
要構建MoveMe程序并在模擬器里運行它,要找如下的步驟去做:
1. 在Xcode中打開MoveMe.xcodeproj文件
2. 在項目工具欄,確保Active SDK菜單中選中了simulator選項。(如果Active SDK菜單在工具欄中沒有出現,選擇菜單Project > Set Active SDK > Simulator。)
3. 從菜單選擇Build > Build and Go (Run),或者簡單的點擊工具欄上的Build and Go按鈕。
當程序構建完成,Xcode把它裝入到模擬器中然后開始運行它。使用鼠標,你可以點擊Welcome按鈕,并把它拖到屏幕的任意位置,查看程序的行為(是否符合需求)。如果你把一個設備設置為開發所用,你可以構建程序并讓它在設備上運行。#p#
簡述內存管理
iOS主要是一個面向對象系統,所以你分配的大多數內存都是以Objective-C對象的形式存在的。iOS中的對象使用引用計數機制來確定是否可以安全的釋放對象占用的內存。當你創建一個對象,它的引用計數從1開始。客戶獲得對象后可以選擇保留它,這樣就要把他的引用技術加1。如果客戶保留一個對象,那么它也必須在不再需要的時候釋放這個對象。釋放一個對象就會讓它的引用技術減1。當一個對象的引用計數等于0,系統就會自動的回收對象占用的內存。
注意:iOS并不支持Mac OS X v10.5以及更新版本中的垃圾回收型內存管理機制。如果你需要分配普通的內存塊,沒有跟一個對象想關聯的內存塊,你可以使用標準的malloc庫調用。任何使用malloc函數分配的內存,你都有責任在用完后,調用free函數釋放。系統不會幫助你釋放基于malloc的內存塊。
無論你想如何分配內存,在iOS中管理全部內存使用都比在Mac OS X中更加的重要。雖然iOS有一個虛擬內存系統,但是他不使用交換文件。這就是說如果需要的話,代碼頁可以被覆蓋,但是同時應用程序的數據必須適應內存的尺寸。系統監控著空閑內存的大小,并試圖給程序需要的全部內存。但是當內存使用情況太危急的時候,系統可能會終止你的應用程序。但是這個選項僅僅系統是為了確保系統有足夠的內存執行最重要的操作,例如接電話,的最后一招。
更多關于在iOS下分配內存的信息,參看Cocoa基礎指南。更多關于如何改善程序內存使用的信息,參看iOS編程指南的管理你的內存使用章節。
初始化MoveMe程序
就像每個基于C的程序一樣,每個iPhone程序的初始入口點也是main函數。好消息是,當你使用Xcode的iPhone模板創建新項目的時候,你不需要自己寫這個函數。項目模板包含了一個帶有啟動一個程序所有需要代碼的這個函數的版本。
列表1展示了MoveMe程序的main函數。這個main函數位于項目中的main.m文件內。你創建的所有程序都會有一個跟這個一樣的main函數。這個函數執行兩個關鍵任務。首先,它為程序創建一個最高級的自動釋放的內存池,這個內存池可以回收使用autorelease函數釋放的 Objective-C對象占用的內存。其次,它調用UIApplicationMain函數創建MoveMe程序的關鍵對象,初始化這些對象,開始事件處理循環。直到程序退出這個函數才會返回。
列表1 使用提供的main函數
- int main(int argc, char *argv[])
- {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, nil);
- [pool release];
- return retVal;
- }
定義程序的委托對象
你的項目中最重要的架構細節之一就是定義一個程序委托對象,從你提供的類實例化。MoveMe項目中的程序委托類的接口聲明在 MoveMeAppDelegate.h文件內,實現定義在MoveMeAppDelegate.m文件內。一旦你把這些文件加入到項目中,你可以使用界面構建器指明這個類作為程序的委托對象。界面構建器是一個可視化工具,用來創建和管理窗口的視圖,設置視圖之間的層級,配置每個視圖的選項,在視圖和程序的其他對象間建立聯系。因為它是一個可視化工具,你可以通過在窗口上拖動組件完成這些任務。操作的結果是你的界面的交互版本,你可以快速修改,隨時看到改變。界面構建器把你的用戶界面保存為一個nib文件,這是你的程序對象圖形的存檔。
要啟動界面構建器,并查看程序委托對象的定義,雙擊Xcode項目窗口中的文件組面板中的MainWindow.xib文件。MainWindow.xib 是一個nib文件,包含了程序的窗口,定義了程序中幾個重要對象之間的關系,包括程序的委托對象。要了解程序委托關系是如何建立的,點擊nib文件文檔窗口(標題為MainWindow.xib)中的File's Owner圖標,顯示查看器窗口(選擇菜單Tools > Inspector),然后點擊查看器窗口的連接頁。
如圖3所示,查看器現實File's Owner對象(表示nib文件中的應用程序)有一個委托出口連接到MoveMeAppDelegate對象。
圖3 程序委托
程序委托對象與標準的UIApplication對象串聯工作,對程序狀態改變做出反應。程序對象做大多數的繁重工作,但是委托也負責幾項關鍵的行為,如下:
◆設定程序窗口,初始化用戶界面
◆執行定制數據引擎所需的格外初始化任務
◆打開與程序定制的URL模式相關聯的內容
◆對設備方向的變化做出響應
◆處理內存不足的警告
◆處理系統要求程序退出的請求
啟動期間,對委托對象最緊急的任務就是設定和展現程序窗口,詳情在“創建程序窗口”中描述。委托對象還應該為了程序可以立即使用而執行需要的任務,例如從之前的一個狀態恢復程序,或者創建需要的對象。當程序退出時,委托對象需要執行有序的程序關閉操作,并保存下次啟動循環所需要的信息。#p#
創建程序窗口
每個程序都需要創建窗口覆蓋整個屏幕然后在窗口內放置內容。iOS的圖形程序不會和其他的程序一起運行。實際上除了核心和一些底層系統守護進程,程序啟動后就獨占系統了。甚至,你的程序可能最多需要一個窗口,一個 UIWindow類的實例。你希望改變用戶界面的時候,你只需要改變窗口上顯示的視圖即可。
窗口提供了用戶界面的描繪空間,但是視圖對象提供實際的內容。視圖對象是UIView類的實例,描繪內容,并響應對內容的交互行為。iOS定義了標準視圖去表現表格,按鈕,文本框以及其他類型的交互控件。你可以把這些視圖都加入到窗口,或者你可以用繼承UIView定義一個定制視圖,實現一些定制描繪和事件響應。MoveMe程序定義了兩個視圖,由MoveMeView和PlacardView兩個類實現,顯示用戶界面,處理用戶交互事件。
程序啟動期間,目標是創建程序窗口并盡快的顯示一些初始內容。窗口從MainWindow.xib文件中展開。當程序達到啟動完成狀態,準備就緒可以處理事件的時候,UIApplication對象向委托對象發出applicationDidFinishLaunching:消息。這個消息暗示委托對象把內容放入窗口,并執行其他程序所需的初始化操作。
在MoveMe程序中,委托在applicationDidFinishLaunching:消息中做如下工作:
◆創建一個視圖控制器對象,管理窗口中的內容視圖。
◆使用MoveMeView類的實例初始化試圖控制器,這個實例保存在MoveMeView.xib文件內,作為背景視圖并填滿整個窗口邊框。
◆把視圖控制器作為子視圖加入窗口。
◆顯示窗口。
列表2展示了MoveMe程序的applicationDidFinishLaunching:方法,該方法定義在程序委托對象的實現文件MoveMeAppDelegate.m中。這個方法創建了窗口的主內容視圖,并使窗口可見。展現窗口讓系統了解到,程序已經準備就緒可以處理事件了。
列表2 創建內容視圖
- - (void)applicationDidFinishLaunching:(UIApplication *)application
- {
- // Set up the view controller
- UIViewController *aViewController = [[UIViewController alloc]
- initWithNibName:@"MoveMeView" bundle:[NSBundle mainBundle]];
- self.viewController = aViewController;
- [aViewController release];
- // Add the view controller's view as a subview of the window
- UIView *controllersView = [viewController view];
- [window addSubview:controllersView];
- [window makeKeyAndVisible];
- }
注意:你可以利用applicationDidFinishLaunching:方法執行設定程序用戶界面以外的其他任務。許多程序利用它初始化需要的數據結構,讀取用戶參數,或者返回到上次程序退出時的狀態。
雖然前面的代碼創建了窗口的背景視圖,并顯示了窗口,但是在前面的代碼中,你無法找到創建PlacardView類顯示Welcome按鈕的代碼。這個行為由MoveMeView類的 setUpPlacardView方法來處理,這個方法在MoveMeView對象從nib文件中展開的時候會被調用。setUpPlacardView 方法在列表3中展示。這個視圖的初始化部分包括了PlacardView對象的創建。因為MoveMeView類提供了整個程序的背景,所以它把 PlacardView對象加為子視圖。兩個視圖見的關系不僅令 Welcome按鈕顯示在程序的背景之上,而且讓MoveMeView類可以處理針對按鈕的事件。
列表3 創建placard視圖
- - (void)setUpPlacardView
- {
- // Create the placard view -- it calculates its own frame based on its image
- PlacardView *aPlacardView = [[PlacardView alloc] init];
- self.placardView = aPlacardView;
- [aPlacardView release];
- placardView.center = self.center;
- [self addSubview:placardView];
- }
#p#
描繪Welcome按鈕
UIKit提供的標準視圖無需修改就可以描繪很多類型的簡單內容。例如,你可以用UIImageView類顯示圖像,用UILabel類顯示文本信息。 MoveMe程序的MoveMeView類也得益于UIView對象的基本屬性,具體是backgroundColor屬性,用顏色填滿視圖。這個屬性可以在視圖對象的初始化方法中被設置。這里,這個屬性被設置于MoveMeView.xib文件中,使用界面構建器的查看器窗口的屬性頁的顏色選擇器選擇顏色。當你需要動態描繪內容,你必須使用在UIKit里的一些更高級的描繪特性,或者你應該使用Quartz或者OpenGL ES。
MoveMe 程序的PlacardView類描繪Welcome按鈕并管理按鈕在屏幕上面的位置。雖然PlacardView類可以用嵌入UIImageView和 UILabel對象的方法描繪內容,但是這里明確的描繪內容,來顯示全部的流程。所以,這個類實現了drawRect:方法,該方法是用來定制描繪的位置。
drawRect:方法被調用時,描繪環境已經配置就緒了。需要做的只是指定描繪命令描繪定制內容。在PalcardView類中,內容包括一個背景圖像(保存在Placard.png資源文件內)和一個定制字符串,文本可以動態改變。要描繪內容,該類需要執行下面的步驟:
◆在視圖的原點描繪背景圖片。(因為視圖大小已經調整到符合圖片尺寸,這個步驟提供了完整的按鈕北京。)
◆計算welcome字符串的位置,令其可以顯示在按鈕中間。(因為字符串的尺寸可以改變,所以位置每次都需要根據當前的字符串尺寸計算。)
◆設定描繪顏色為黑色。
◆用黑色描繪字符串,位置略有偏移。
◆設定描繪顏色為白色。
◆在正確的位置用白色描繪字符串。
列表4展示 PlacardView類的drawRect方法。成員變量placardImage包含一個帶有按鈕背景圖片的UIImage對象,成員變量 currentDisplayString是一個NSString對象包含了welcome字符串。描繪圖像后,這個方法計算字符串相對視圖的位置。字符串的尺寸是已知的,在字符串裝入的時候計算好并保存在成員變量textSize中。字符串會被描繪兩次,一次用黑色一次用白色,使用NSString的drawAtPoint:forWidth:withFont:fontSize:lineBreakMode:baselineAdjustment:方法。
列表4 描繪Welcome按鈕
- - (void)drawRect:(CGRect)rect
- {
- // Draw the placard at 0, 0
- [placardImage drawAtPoint:(CGPointMake(0.0, 0.0))];
- /*
- Draw the current display string.
- This could be done using a UILabel, but this serves to illustrate
- the UIKit extensions to NSString. The text is drawn center of the
- view twice - first slightly offset in black, then in white -- to give
- an embossed appearance. The size of the font and text are calculated
- in setupNextDisplayString.
- */
- // Find point at which to draw the string so it will be in the center of the view
- CGFloat x = self.bounds.size.width/2 - textSize.width/2;
- CGFloat y = self.bounds.size.height/2 - textSize.height/2;
- CGPoint point;
- // Get the font of the appropriate size
- UIFont *font = [UIFont systemFontOfSize:fontSize];
- [[UIColor blackColor] set];
- point = CGPointMake(x, y + 0.5);
- [currentDisplayString drawAtPoint:point
- forWidth:(self.bounds.size.width-STRING_INDENT)
- withFont:font
- fontSize:fontSize
- lineBreakMode:UILineBreakModeMiddleTruncation
- baselineAdjustment:UIBaselineAdjustmentAlignBaselines];
- [[UIColor whiteColor] set];
- point = CGPointMake(x, y);
- [currentDisplayString drawAtPoint:point
- forWidth:(self.bounds.size.width-STRING_INDENT)
- withFont:font
- fontSize:fontSize
- lineBreakMode:UILineBreakModeMiddleTruncation
- baselineAdjustment:UIBaselineAdjustmentAlignBaselines];
- }
當你需要描繪比圖像和字符串更復雜的內容的時候,你可以選擇Quartz或者OpenGL ES。Quartz和UIKit協作,處理描繪矢量路徑,圖像,斜線,PDF和其他你想動態創建的復雜內容。因為Quartz和UIKit基于相同的描繪環境,你可以直接在視圖的drawRect:方法中調用Quartz函數,甚至可以在UIKit類中混用Quartz。
OpenGL ES是Quartz和UIKit之外的選擇,可以讓你用類似Mac OS X中OpenGL技術的函數來渲染2D和3D內容。跟Quartz和UIKit不同的是,你不能用視圖的drawRect:方法來進行描繪。仍舊是使用視圖,但是主要是用視圖對象作為OpenGL ES代碼的描繪空間。按照什么頻率更新描繪空間,使用什么對象,取決于你自己的選擇。
#p#
處理觸摸事件
iOS的多點觸摸接口令你的程序可以識別和響應多個手指觸摸設備產生的不同事件。響應多手指觸摸的能力帶來了強大的能力,但是也帶來跟傳統的基于鼠標的事件處理系統操作的明顯不同。每次手指觸摸設備的表面,觸摸傳感器產生一個觸摸事件。每次手指移動,額外的觸摸事件產生表明手指的新位置。一旦手指離開設備表面,系統分發另外一個觸摸事件表明這一點。
因為同時可能有多個手指觸摸設備,這就產生了利用這些事件來識別復雜用戶手勢的可能性。系統對識別一些常用手勢(例如,雙擊)提供了幫助,但是你可以檢測到更復雜的手勢。當事件系統產生了一個新的觸摸事件,它包含了每個手指當前的狀態,包括觸摸或者剛離開設備的表面。因為每個事件對象包括所有的活動觸摸事件,你可以用新的事件監控每個手指的動作。你可以在事件行為之間跟蹤每個手指的移動來檢測手勢,你可以用來跟程序中的內容交互。例如,如果事件表明用戶正在實施手指分開和手指并攏手勢,而且下面的視圖支持放大,所以你可以用這些時間改變當前的縮放級別。
圖4 用觸摸事件檢測手勢
系統把事件分發給程序的響應者對象,它是UIResponder類的實例。在iPhone程序中,程序的視圖往往是你的定制響應者對象。MoveMe程序實現兩個視圖類,但是實際上只有MoveMeView類響應事件消息。該類通過覆蓋 UIResponder的下列方法,檢測在Welcome按鈕內外的輕點。
◆ (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
◆ (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
◆ (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
為了簡化事件處理行為,MoveMe程序僅跟蹤接觸設備表面的第一次手指的事件。這可以利用UIView類對此的支持,此類在默認情況下禁用多點觸摸事件。對于不需要跟蹤多個手指事件的程序,這個特性很方便。當多點觸摸事件被禁用的時候,系統僅分發跟第一個接觸設備的手指的消息。與其他的觸摸相關的事件不會被分發給視圖。如果希望得到其他觸摸的信息,你可以利用UIView類的 setMultipleTouchEnabled:方法,打開多點觸摸支持。
作為事件處理部分,MoveMeView類執行下面的操作:
◆當觸摸第一次發生,檢查事件發生的位置。
◆雙擊Welcome按鈕外的位置,更新按鈕顯示的字符串。
◆單擊按鈕,把按鈕的中心移到手指之下,并引發一個動畫放大按鈕。
◆其他的觸摸都忽略。
◆如果手指在按鈕之內移動,按鈕的位置就會更新以匹配手指。
◆如果手指在按鈕內,然后離開設備的表面,按鈕以動畫的形式回到他的原始位置。
列表5展示MoveMeView類的 touchesBegan:withEvent:方法。手指首次觸摸設備的時候系統調用這個方法。方法獲得所有的觸摸,然后抽出唯一的觸摸以及被觸摸的物體。UITouch對象中的信息可以說明觸摸發生在哪個視圖(MoveMeView或者PlacardView)上,以及與之相關的觸碰數量。如果觸摸是按鈕外的雙擊,touchesBegan:withEvent:方法調用setupNextDisplayString方法改變按鈕的歡迎字符串。如果事件發生在Welcome按鈕內,使用animateFirstTouchAtPoint:方法放大按鈕,并把按鈕中心移動到觸摸發生的位置上。其他觸摸相關的事件都被忽略了。
列表5 處理初步的觸摸事件
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- {
- // We only support single touches, so anyObject
- // retrieves just that touch from touches
- UITouch *touch = [touches anyObject];
- // Only move the placard view if the touch was in the placard view
- if ([touch view] != placardView)
- {
- // In case of a double tap outside the placard view,
- // update the placard's display string
- if ([touch tapCount] == 2)
- {
- [placardView setupNextDisplayString];
- }
- return;
- }
- // Animate the first touch
- CGPoint touchPoint = [touch locationInView:self];
- [self animateFirstTouchAtPoint:touchPoint];
- }
列表6展示MoveMeView類的touchesMoved:withEvent:方法。系統在響應手指已經觸摸設備,而且從原始位置移開的時候調用這個方法。MoveMe程序僅跟蹤發生在Welcome按鈕上的移動。這個方法檢查觸摸事件的位置,并用之調整Placard對象的中點。視圖的系統會引發在新位置的自動重繪。
列表6 響應觸摸的移動
- - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
- // If the touch was in the placardView, move the placardView
- // to its location
- if ([touch view] == placardView)
- {
- CGPoint location = [touch locationInView:self];
- placardView.center = location;
- return;
- }
- }
當用戶的手指離開屏幕,MoveMe用動畫的形式把按鈕移動回它的原始位置,程序窗口的中點。列表7展示了開始動畫的touchesEnded:withEvent:方法。
列表7 釋放Welcome按鈕
- - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
- // If the touch was in the placardView, bounce it back to the center
- if ([touch view] == placardView)
- {
- // Disable user interaction so subsequent touches
- // don't interfere with animation
- self.userInteractionEnabled = NO;
- [self animatePlacardViewToCenter];
- return;
- }
- }
為了簡化程序的事件處理流程,touchesEnded:withEvent:方法在按鈕以動畫方式返回原始位置的過程中暫時禁用了新的觸摸事件。如果不這么做的話,每個事件處理方法中都需要判斷按鈕是不是正在動畫中,如果是就要終止動畫。在按鈕返回屏幕中心的時候短時間禁用用戶交互,簡化了事件處理代碼,無非額外的邏輯。當按鈕達到他的原始位置,MoveMeView類的 animationDidStop:finished:方法重新啟用用戶交互,這樣事件循環又可以繼續下去。#p#
按鈕動畫移動
在iPhone程序中,動畫起了非常重要的作用。動畫廣泛用于給用戶相關信息以及立即響應。例如,當用戶在程序中瀏覽層級數據,iPhone不是僅僅直接用其他的屏幕代替當前的屏幕,而是讓屏幕以動畫的方式就位。移動的方向用戶走向層級的高層還是低層,并給用戶可視化的信息這里有新的信息。
因為動畫很重要,所以UIKit的類中已經內建了對它的支持。MoveMe程序獲益于這種支持,動畫改變Welcome按鈕的外觀。當用戶第一次觸摸按鈕,程序引發一個動畫讓按鈕的尺寸變大。
當用戶放開這個按鈕,另外的動畫產生讓按鈕回到原始位置。創建這樣動畫的步驟如下:
◆調用你想動畫的視圖的beginAnimations:context:方法。
◆設置動畫的屬性。
◆調用commitAnimations方法開始動畫。
列表8展示Welcome按鈕第一次被觸摸的時候令它動畫的代碼。這個方法設置了動畫的持續時間,并對按鈕實施放大操作。當動畫結束,動畫的機制調用委托的 growAnimationDidStop:finished:context:方法,通過令按鈕輕微晃動的方式結束動畫,并把placard視圖移動到觸點。
列表8 讓Welcome產生動畫
- - (void)animateFirstTouchAtPoint:(CGPoint)touchPoint
- {
- #define GROW_ANIMATION_DURATION_SECONDS 0.15
- NSValue *touchPointValue = [[NSValue valueWithCGPoint:touchPoint] retain];
- [UIView beginAnimations:nil context:touchPointValue];
- [UIView setAnimationDuration:GROW_ANIMATION_DURATION_SECONDS];
- [UIView setAnimationDelegate:self];
- [UIView setAnimationDidStopSelector: @selector(growAnimationDidStop:finished:context:)];
- CGAffineTransform transform = CGAffineTransformMakeScale(1.2, 1.2);
- placardView.transform = transform;
- [UIView commitAnimations];
- }
- - (void)growAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
- {
- #define MOVE_ANIMATION_DURATION_SECONDS 0.15
- [UIView beginAnimations:nil context:NULL];
- [UIView setAnimationDuration:MOVE_ANIMATION_DURATION_SECONDS];
- placardView.transform = CGAffineTransformMakeScale(1.1, 1.1);
- // Move the placard view under the touch
- NSValue *touchPointValue = (NSValue *)context;
- placardView.center = [touchPointValue CGPointValue];
- [touchPointValue release];
- [UIView commitAnimations];
- }
#p#
完成程序
前面的章節,你已經看到MoveMe程序如何初始化,展現用戶界面和響應事件。除了這些概念以外,在構建程序并把它裝載到設備之前,你還需要了解一些小細節。最后需要考慮的是你的程序的信息屬性列表(info.plist)。這是程序用來跟系統通信的一個XML文件。Xcode 為你的程序創建一個默認版本,并把程序的初始化配置信息放在其中。你可以拓展這個信息,為系統提供所需的關于程序的額外細節信息。例如,你使用這個文件通信,關于程序的版本,支持的定制URL模式,啟動鏡像,默認的視覺狀態和系統狀態欄的風格。
列表9展示了MoveMe程序的info.plist文件的內容。該文件指明可執行文件的名字,顯示在用戶主屏幕的圖像文件,以及在系統中唯一確定程序的字符串。因為MoveMe程序是一個全屏程序,也就是說,他不顯示狀態欄,所以它包含了鍵UIStatusBarHidden并把值設為true。把這個鍵設置為true令系統得知在啟動或者程序運行期間,不要顯示程序狀態欄。雖然MoveMe程序也可以用編程的手段配置相同的行為,但是那樣的話行為只能在程序已經啟動后起作用,看起來有點莫名其妙的。
列表9 Info.plist文件的內容
- CFBundleDevelopmentRegion
- en
- CFBundleDisplayName
- ${PRODUCT_NAME}
- CFBundleExecutable
- ${EXECUTABLE_NAME}
- CFBundleIconFile
- Icon.png
- CFBundleIdentifier
- com.yourcompany.${PRODUCT_NAME:identifier}
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- ${PRODUCT_NAME}
- CFBundlePackageType
- APPL
- CFBundleSignature
- ????
- CFBundleVersion
- 1.0
- UIStatusBarHidden
- NSMainNibFile
- MainWindow
注意:你可以使用文本編輯器編輯info.plist文件的內容,顯示為Info.plist文件內容對應的XML內容,或者使用屬性列表編輯器,顯示為鍵值對的表格。Xcode也提供了信息窗口訪問程序目標的某些屬性。要展現這個窗口,選擇程序目標然后選擇菜單File > Get Info。屬性表包含一些info.plist文件中的屬性。
現在你已經了解了創建自己的iPhone程序需要的全部基礎信息。下面的步驟是學習更多的iOS特性。程序應該利用iOS內建的特性創建直觀而且令人愉悅的用戶體驗。這些特性有些在“深入探討你的程序”,但是關于完全的特性列表以及如何使用它們的信息,參看iOS編程指南。
深入談討你的程序
這里有一些用戶可以獲得的iPhone和iPod touch的特性。有些特性跟硬件相關,例如根據設備的方向自動調整視圖。有些是跟軟件相關的,例如所有的內建iPhone應用程序都使用同一個聯系人列表。因為下面描述的這些特性跟基本的用戶體驗有關,所以你應該在原始設計就考慮這些特性是否適用于你的程序。
用加速度傳感器跟蹤方向和移動
iPhone和iPod touch的加速度傳感器可以為系統和你的程序提供輸入。一個加速度傳感器測量在一個方向上速度的變化。iPhone和iPod touch都有三個加速度傳感器用來測量3D空間的每個主要軸向上的變化,允許你檢測任何方向的移動。
圖5 加速度傳感器軸向
也許你不認為測量加速度變化有啥用處,但是實際上這個信息可以有很大的用處。重力的作用總是把物體拉向地面。這個力的作用造成即使設備是靜止不動的,仍舊可以測量到朝向地面的加速度。通過跟蹤加速度傳感器記錄的加速度,以及加速度的大小,你可以精確的檢測到設備在3D空間內的方向。你可以把方向作為程序的輸入。
系統使用加速度傳感器監控設備當前的方向,并在方向改變的時候通知程序。如果程序的用戶界面支持橫豎兩種模式,那么你應該把視圖控制器加入到你的設計中。UIViewController類提供了,旋轉用戶界面以及根據方向改變自動調整視圖位置的機制。
如果你想直接訪問原始的加速度傳感器數據,你可以使用UIKit內的共享對象UIAccelerometer。加速度傳感器在一個可以配置的時間間隔下定時報告當前的加速度值。你還可以使用這些數據來檢測設備方向或者檢測其他的瞬間移動,例如用戶前后搖晃設備。你可以把這個信息作為游戲或者其他程序的輸入。想要找到如何配置UIAccelerometer對象,以及接收加速度傳感器事件的例子,參看iOS編程指南的訪問加速度傳感器事件章節。
訪問用戶通訊錄
用戶的通訊錄列表是所有系統程序共享的重要資源。電話,郵件和短信息程序都用它來識別用戶要聯系的聯系人,并使基本交互變得容易,例如打電話,發郵件,或者短信息。你自己的程序也可以為類似的目的訪問用戶的通訊錄信息,或者其他程序需要的相關信息。
圖6 訪問用戶通訊錄
iOS提供對用戶通訊錄的直接訪問,以及通過一個標準的選擇接口進行的間接訪問。使用直接訪問,你可以直接用通訊錄數據庫獲得聯系人信息。你可以用另外的方式來展現這些聯系信息,或者根據程序的需求過濾它們。如果你不需要一個定制接口,iOS也提供了一系列系統接口選擇和創建聯系人。把這些接口加入你的程序需要一些額外的工作,但是可以令你的程序的外觀看起來更像系統的一部分。
獲得用戶當前的位置
運行iOS的設備是為用戶頻繁使用而設計的。你所設計的程序需要把這一點納入考慮之中。互聯網和Web讓人們可以在任何地方經營自己的事業,所以可以追蹤用戶當前位置就變成了必備的用戶體驗了。畢竟,我們不能為一個在洛杉磯口渴的人,列出紐約的咖啡店。這就是Core Location框架的用處。
Core Location框架追蹤手機機站和Wi-Fi無線熱點的信號,然后使用他們三角定位出用戶當前的位置(此文檔比較早期,現在已經包括直接獲取GPS信號了)。你可以僅用該框架獲取一個固定的初始位置,也可以在用戶位置發生變化的時候得到通知。利用這些信息,你可以過濾要提供給用戶的信息,或者做一些其他的事情。
播放音頻和視頻
iOS通過Core Audio和OpenAL框架支持你的程序中的音頻特性,并使用Media Player框架提供視頻回放的能力。你可以用它播放簡單的聲音效果,或者多個聲道的音頻,混音,把它們限制在某個音域內,甚至可以使用iPhone的振動功能。如果你是一個游戲開發者,而且你的代碼中已經使用了OpenAL的特性,你可以直接在iOS中繼續使用這些代碼在你的游戲中定位和回放你的音頻。
Media Player框架是用來回放全屏視頻文件。這個框架支持多種標準的電影文件格式,你可以控制回放的環境,包括是否顯示用戶控制器,以及視頻內容的長寬比。游戲開發者可以利用這個框架播放過場影片或者預先渲染好的內容,而基于媒體的程序也可以用這個框架回放電影文件。
圖7 播放視頻
使用內置相機
iPhone的相機程序允許用戶拍攝照片,然后保存在照片庫中,和其他從電腦上傳來的照片放在一起。雖然iPod touch沒有照相機,但是它也有一個用來保存用戶上傳照片的照片庫。iOS用UIKit框架中的UIImagePickerController類來支持這些特性。
圖8 iPhone的相機
UIImagePickerController類為你的程序提供了相機和照片庫的訪問接口。包括照相和照片程序的很多程序,使用這些標準的系統接口。當你顯示一個圖片選擇界面,選擇控制器UIImagePickerController實現所有所需的用戶交互細節,然后返回一個結果圖像到你的應用程序。
【編輯推薦】