objective-c過濾HTML標籤

  1. // 過濾HTML標籤
  2. - (NSString *)flattenHTML:(NSString *)html {

  3.     NSScanner *theScanner;
  4.     NSString *text = nil;

  5.     theScanner = [NSScanner scannerWithString:html];

  6.     while ([theScanner isAtEnd] == NO) {
  7.         // find start of tag
  8.         [theScanner scanUpToString:@"<" intoString:NULL] ;
  9.         // find end of tag
  10.         [theScanner scanUpToString:@">" intoString:&text] ;
  11.         // replace the found tag with a space
  12.         //(you can filter multi-spaces out later if you wish)
  13.         html = [html stringByReplacingOccurrencesOfString:
  14.                 [ NSString stringWithFormat:@"%@>", text]
  15.                                                withString:@” “];
  16.     } // while //

  17.     return html;
  18. }

UITableView下拉刷新

以前看到facebook,人人和其他的一些應用,很多都使用了下拉列表,列表就重新加載數據的應用.感覺用戶體驗很好,但是一直沒有時間找相應的解決方法,今天終於有時間整理一下代碼:

下圖是實現的效果圖,當用戶下拉列表的時候,就出現向上的箭頭.

 

實現的開源框架是:https://github.com/enormego/EGOTableViewPullRefresh

實現過程很簡單,下載相應的demo,然後拷貝資源文件和EGORefreshTableHeaderView.h和EGORefreshTableHeaderView.m到相依的工程中.在table所在的類中添加如下代碼:

  1. #import <UIKit/UIKit.h>
  2. #import “EGORefreshTableHeaderView.h”
  3. @interface iphone_navtaViewController : UIViewController
  4. <UITableViewDelegate,UITableViewDataSource,EGORefreshTableHeaderDelegate>{
  5.     BOOL isflage;
  6.     IBOutlet UITableView *myTableView;
  7.     EGORefreshTableHeaderView *_refreshHeaderView; 
  8.     BOOL _reloading; 
  9. }
  10. @property (nonatomic,retain) UITableView *myTableView;
  11. - (void)reloadTableViewDataSource; 
  12. - (void)doneLoadingTableViewData; 
  13. @end

 

實現方法:

  1. #import “iphone_navtaViewController.h”
  2. @implementation iphone_navtaViewController
  3. @synthesize myTableView;
  4. - (void)viewDidLoad {
  5.     [super viewDidLoad];
  6.     if (_refreshHeaderView == nil) { 
  7.        
  8. EGORefreshTableHeaderView *view1 = [[EGORefreshTableHeaderView alloc] 
  9. initWithFrame:CGRectMake(0.0f, 10.0f – 
  10. self.myTableView.bounds.size.height, self.myTableView.frame.size.width, 
  11. self.view.bounds.size.height)]; 
  12.         view1.delegate = self; 
  13.         [self.myTableView addSubview:view1]; 
  14.         _refreshHeaderView = view1; 
  15.         [view1 release]; 
  16.     } 
  17.     [_refreshHeaderView refreshLastUpdatedDate]; 
  18. }
  19. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  20.     return (interfaceOrientation == UIInterfaceOrientationPortrait);
  21. }
  22. - (void)didReceiveMemoryWarning {
  23.     [super didReceiveMemoryWarning];
  24. }
  25. - (void)viewDidUnload {
  26.     self.myTableView=nil;
  27.     _refreshHeaderView=nil;
  28. }
  29. - (void)dealloc {
  30.     _refreshHeaderView=nil;
  31.     [self.myTableView release];
  32.     [super dealloc];
  33. }
  34. #pragma mark –
  35. #pragma mark onClick
  36. -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
  37. {
  38.       isflage=!isflage;
  39.     [super.navigationController setNavigationBarHidden:isflage animated:TRUE];
  40.     [super.navigationController setToolbarHidden:isflage animated:TRUE];
  41. }
  42. #pragma mark –
  43. #pragma mark UITableView
  44. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  45.     return 1;
  46. }
  47. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  48.     return 3;
  49. }
  50. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  51.     UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"tag"];
  52.     if (cell==nil) {
  53.         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
  54.                                        reuseIdentifier:@”tag”] autorelease];
  55.     }
  56.     //表格設計
  57.     return cell;
  58. }
  59. -(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  60. {
  61.     return 100;
  62. }
  63. #pragma mark – 
  64. #pragma mark Data Source Loading / Reloading Methods
  65. - (void)reloadTableViewDataSource{ 
  66.     NSLog(@”==開始加載數據”); 
  67.     _reloading = YES; 
  68. }
  69. - (void)doneLoadingTableViewData{ 
  70.     NSLog(@”===加載完數據”); 
  71.     _reloading = NO; 
  72.     [_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:self.myTableView]; 
  73. #pragma mark – 
  74. #pragma mark UIScrollViewDelegate Methods 
  75. - (void)scrollViewDidScroll:(UIScrollView *)scrollView{    
  76.     [_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView]; 
  77. }
  78. - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ 
  79.     [_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView]; 
  80. #pragma mark – 
  81. #pragma mark EGORefreshTableHeaderDelegate Methods 
  82. - (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView*)view{
  83.     [self reloadTableViewDataSource]; 
  84.     [self performSelector:@selector(doneLoadingTableViewData) withObject:nil afterDelay:3.0]; 
  85. }
  86. - (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView*)view{
  87.     return _reloading; 
  88. - (NSDate*)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView*)view{
  89.     return [NSDate date];     
  90. @end

 

 

一個最重要的步驟,就是要添加QuartzCore.framework組建。

然後運行即可。

 

 

[轉自:http://1058813598.diandian.com/post/2011-11-09/6622195]

UITableView滑動刪除

  1. // 是否允許刪除該行
  2. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
  3.     return YES;
  4. }
  5. // 處理滑動事件
  6. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
  7.     if (editingStyle == UITableViewCellEditingStyleDelete) {
  8.          [self.data removeObjectAtIndex:indexPath.row];
  9.          [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
  10.      }
  11.      else if (editingStyle == UITableViewCellEditingStyleInsert) {

  12.      }
  13.  }
  14. // 修改刪除按鈕文字
  15. - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath{
  16.         return @”X”;
  17. }

IOS5設置標籤欄背景

適應於5.0及以上版本,5.0以下可使用類別重寫drawRect方法實現。
  1. UINavigationBar *navBar = [myNavController navigationBar];
  2. if ([navBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)])
  3. {
  4.     [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"brnlthr_nav.jpg"] forBarMetrics:UIBarMetricsDefault];
  5. }

TableView詳細解釋

-、建立 UITableView

  1.  DataTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 420)];
  2.  [DataTable setDelegate:self];
  3.  [DataTable setDataSource:self];
  4.  [self.view addSubview:DataTable];
  5.  [DataTable release];
二、UITableView各Method說明
  1. //Section總數
  2. - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
  3.     return TitleData;
  4. }
  1. // Section Titles
  2. //每個section顯示的標題
  3. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
  4.     return @”";
  5. }
  1. //指定有多少個分區(Section),默認爲1
  2. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  3.     return 4;
  4. }
  1. //指定每個分區中有多少行,默認爲1
  2. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
  3.     return 100;
  4. }
  1. //繪製Cell
  2. -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  3.     static NSString *CellTableIdentifier = @”CellTableIdentifier”;
  4.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellTableIdentifier];
  5.     if (cell == nil) {
  6.         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: SimpleTableIdentifier] autorelease];
  7.      }
  8.     cell.imageView.image=image;//未選cell時的圖片
  9.     cell.imageView.highlightedImage=highlightImage;//選中cell後的圖片
  10.     cell.text=//…..
  11.     return cell;
  12. }
  1. //行縮進
  2. -(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath{
  3.  NSUInteger row = [indexPath row];
  4.     return row;
  5. }
  1. //改變行的高度
  2. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
  3.     return 40;
  4. }
//定位
  1. [TopicsTable setContentOffset:CGPointMake(0, promiseNum * 44 + Chapter * 20)];
  2. //返回當前所選cell
  3. NSIndexPath *ip = [NSIndexPath indexPathForRow:row inSection:section];
  4. [TopicsTable selectRowAtIndexPath:ip animated:YES scrollPosition:UITableViewScrollPositionNone];
  5.  [tableView setSeparatorStyle:UITableViewCellSelectionStyleNone];

  6. //選中Cell響應事件
  7. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
  8.     [tableView deselectRowAtIndexPath:indexPath animated:YES]; //選中後的反顯顏色即刻消失
  9. }
  1. //判斷選中的行(阻止選中第一行)
  2. -(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
  3. {
  4.     NSUInteger row = [indexPath row];
  5.     if (row == 0)
  6.         return nil;

  7.     return indexPath;
  8. }
  1. //划動cell是否出現del按鈕
  2. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
  3.     return YES;
  4. }
  1. //編輯狀態
  2. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
  3. forRowAtIndexPath:(NSIndexPath *)indexPath{

  1. [topicsTable setContentSize:CGSizeMake(0,controller.promiseNum * 44)];
  1. //右側添加一個索引表
  2. - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
  3.     //
  4. }
  1. //返回Section標題內容
  2. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
  3. }
  1. //自定義划動時del按鈕內容
  2. - (NSString *)tableView:(UITableView *)tableView
  3. titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
  4. //跳到指的row or section
  5. [tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:NO];

三、在UITableViewCell上建立UILable多行顯示
  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  2.     static NSString *CellIdentifier = @”Cell”;
  3.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  4.     if (cell == nil) {
  5.         cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
  6.    UILabel *Datalabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 320, 44)];
  7.    [Datalabel setTag:100];
  8.    Datalabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  9.    [cell.contentView addSubview:Datalabel];
  10.     [Datalabel release];
  11.  }
  12.  UILabel *Datalabel = (UILabel *)[cell.contentView viewWithTag:100];
  13.  [Datalabel setFont:[UIFont boldSystemFontOfSize:18]];
  14.  Datalabel.text = [data.DataArray objectAtIndex:indexPath.row];
  15.  cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  16.     return cell;
  17. }
  1. //選中cell時的顏色
  2. typedef enum {
  3.     UITableViewCellSelectionStyleNone,
  4.     UITableViewCellSelectionStyleBlue,
  5.     UITableViewCellSelectionStyleGray
  6. } UITableViewCellSelectionStyle
  7. //cell右邊按鈕格式
  8. typedef enum {
  9.     UITableViewCellAccessoryNone,                   // don’t show any accessory view
  10.     UITableViewCellAccessoryDisclosureIndicator,    // regular chevron. doesn’t track
  11.     UITableViewCellAccessoryDetailDisclosureButton, // blue button w/ chevron. tracks
  12.     UITableViewCellAccessoryCheckmark               // checkmark. doesn’t track
  13. } UITableViewCellAccessoryType
  14. //是否加換行線
  15. typedef enum {
  16.     UITableViewCellSeparatorStyleNone,
  17.     UITableViewCellSeparatorStyleSingleLine
  18. } UITableViewCellSeparatorStyle//改變換行線顏色
  19. tableView.separatorColor = [UIColor blueColor];

爲 iOS 應用組件添加圓角

OS 系統自帶的 View 組件都是正方形的,看起來都太生硬,有時候我需要變成圓角形式

具體的實現是使用QuartzCore庫,下面我具體的描述一下實現過程:
  • 首先創建一個項目,名字叫:ipad_webwiew
  • 利用Interface Builder添加一個UIWebView,然後和相應的代碼相關聯
  • 添加QuartzCore.framework

代碼實現:

頭文件:

  1. #import <UIKit/UIKit.h>
  2. #import <QuartzCore/QuartzCore.h>

  3. @interface ipad_webwiewViewController : UIViewController
  4.  {
  5.     IBOutlet UIWebView *myWebView;
  6.     UIView *myView;
  7. }
  8. @property (nonatomic,retain) UIWebView *myWebView;

  9. @end

代碼實現:

  1. - (void)viewDidLoad {
  2.     [super viewDidLoad];
  3.     //給圖層添加背景圖片:
  4.     //myView.layer.contents = (id)[UIImage imageNamed:@"view_BG.png"].CGImage;
  5.     //將圖層的邊框設置爲圓腳
  6.     myWebView.layer.cornerRadius = 8;
  7.     myWebView.layer.masksToBounds = YES;
  8.     //給圖層添加一個有色邊框
  9.     myWebView.layer.borderWidth = 5;
  10.     myWebView.layer.borderColor = [[UIColor colorWithRed:0.52 green:0.09 blue:0.07 alpha:1] CGColor];
  11. }

解決the identity used to sign the executabnle is no longer valid錯誤

使用XCODE做真機調試的時候,報了個

 

the identity used to sign the executabnle is no longer valid
please verify that your device’s clock is properly set, and that your signing certificiate is not expired.
(0xE8008018).

 

錯誤,原因是越獄的手機沒有安裝 appSync ,使用CYDIA搜索並安裝appSync即可。

 

[轉]創建一個iPhone應用程序

在表面看,創建一個iPhone應用程序和創建一個Mac OS X應用程序很像。使用相同的工具和許多相同的基礎庫。儘管很像,還是有些顯著的不同。iPhone不是桌面計算機,有不同的預期,需要完全不同設計思路。需要從iPhone OS的能力獲得優勢,還要避開那些對移動環境無關和不切實際的特性。iPhone和iPod touch的小尺寸屏幕意味着,你的應用程序用戶界面應該合理組織,並總是關注於用戶最需要的信息。

iPhone OS運行用戶跟iPhone和iPod touch的設備,用桌面程序無法實現的交互方式來交互。多點觸摸(Multi-Touch)是一種革命性的接收事件的新方法,報告每一個獨立的手指對屏幕的觸摸,並使處理多手指手勢和其他的複雜輸入變得非常簡單。此外,內建的硬件特性,例如加速度傳感器,雖然也在一些桌面系統使用,但是在iPhone OS中使用的更佳廣泛,可以跟蹤屏幕的當前方向並相應的調整你的內容。瞭解如何在你的程序中使用這些特性,有助於你聚焦適於用戶的設計方式。

瞭解iPhone程序設計的最好辦法就是看例子。本文針對例程MoveMe進行介紹。下面的例子展示iPhone程序的典型性爲,包括:

  • 初始化程序
  • 顯示窗口
  • 描繪定製內容
  • 處理觸摸事件
  • 進行動畫

圖 1 展現了應用程序的接口。觸摸Welcome按鈕會引發一個動畫,按鈕會跳動並把自己的中心移動到你的指下。手指在屏幕上移動,按鈕就會跟着你的手指移動。把你的手指從屏幕上拿開,引發另一個動畫,按鈕跳回它的原始位置。在按鈕以外的任何位置雙擊屏幕會改變按鈕歡迎詞的語言。

圖1 MoveMe程序的窗口

閱讀本文的其他部分前,你應該下載例子(MoveMe),這樣你可以直接參照源代碼學習。你應該已經閱讀了iPhone開發者中心下面的指南文章,對iPhone OS和開發需要的工具和語言有了基本的瞭解:

  • iPhone OS概述
  • iPhone開發工具

如果你對Objective-C編程語言不熟悉,你應該閱讀學習Objective-C:入門手冊讓自己對Objective-C的基本語法有所瞭解。

查看MoveMe例子工程

下載MoveMe例程,你可以得到構建和運行程序需要的源代碼和支持文件。你應該使用Xcode應用程序(位於 /Developer/Applications)管理iPhone OS項目。每個Xcode項目窗口包含一個工作空間用來組織代碼和資源文件,編譯和組裝程序的構建規則,編輯和調試代碼的工具。

圖 2現實MoveMe程序的Xcode項目窗口。要打開這個項目,首先把它複製到本地硬盤,然後雙擊MoveMe.xcodeproj文件即可。(你可以在可以在Xcode選擇菜單File > Open然後選擇文件。)項目包含了多個Objective-C源代碼文件(擴展名爲.m),一些圖像文件和其他資源,以及構建程序包的預定義目標。

圖2 MoveMe項目窗口

在iPhone OS,Xcode項目的最終目標是程序包,一個特性類型的目錄,放置程序的二進制執行文件以及資源文件。iPhone OS包是一個相對平面的目錄結構,包含的大多數文件都在包目錄的頂層。然而,包文件也可以包含子目錄去存儲字符串的本地化版本以及其他的語言相關的資源文件。本文不需要你瞭解程序的確切結構,但是你感興趣的話可以在iPhone OS編程指南程序包章節找到這些信息。

構建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按鈕,並把它拖到屏幕的任意位置,查看程序的行爲(是否符合需求)。如果你把一個設備設置爲開發所用,你可以構建程序並讓它在設備上運行。瞭解如何配置設備爲開發所用以及如何裝入程序的信息,可以參看iPhone OS編程指南中的開發環境章節。

簡述內存管理

iPhone OS主要是一個面向對象系統,所以你分配的大多數內存都是以Objective-C對象的形式存在的。iPhone OS中的對象使用引用計數機制來確定是否可以安全的釋放對象佔用的內存。當你創建一個對象,它的引用計數從1開始。客戶獲得對象後可以選擇保留它,這樣就要把他的引用技術加1。如果客戶保留一個對象,那麼它也必須在不再需要的時候釋放這個對象。釋放一個對象就會讓它的引用技術減1。當一個對象的引用計數等於0,系統就會自動的回收對象佔用的內存。

注意:iPhone OS並不支持Mac OS X v10.5以及更新版本中的垃圾回收型內存管理機制。如果你需要分配普通的內存塊,沒有跟一個對象想關聯的內存塊,你可以使用標準的malloc庫調用。任何使用malloc函數分配的內存,你都有責任在用完後,調用free函數釋放。系統不會幫助你釋放基於malloc的內存塊。

無論你想如何分配內存,在iPhone OS中管理全部內存使用都比在Mac OS X中更加的重要。雖然iPhone OS有一個虛擬內存系統,但是他不使用交換文件。這就是說如果需要的話,代碼頁可以被覆蓋,但是同時應用程序的數據必須適應內存的尺寸。系統監控着空閒內存的大小,並試圖給程序需要的全部內存。但是當內存使用情況太危急的時候,系統可能會終止你的應用程序。但是這個選項僅僅系統是爲了確保系統有足夠的內存執行最重要的操作,例如接電話,的最後一招。

更多關於在iPhone OS下分配內存的信息,參看Cocoa基礎指南。更多關於如何改善程序內存使用的信息,參看iPhone OS編程指南的管理你的內存使用章節。

初始化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模式相關聯的內容
  • 對設備方向的變化做出響應
  • 處理內存不足的警告
  • 處理系統要求程序退出的請求

啓動期間,對委託對象最緊急的任務就是設定和展現程序窗口,詳情在“創建程序窗口”中描述。委託對象還應該爲了程序可以立即使用而執行需要的任務,例如從之前的一個狀態恢復程序,或者創建需要的對象。當程序退出時,委託對象需要執行有序的程序關閉操作,並保存下次啓動循環所需要的信息。

想了解iPhone程序的基礎架構和生命週期循環,參看iPhone OS編程指南的核心程序架構章節。

創建程序窗口

每個程序都需要創建窗口覆蓋整個屏幕然後在窗口內放置內容。iPhone OS的圖形程序不會和其他的程序一起運行。實際上除了核心和一些底層系統守護進程,程序啓動後就獨佔系統了。甚至,你的程序可能最多需要一個窗口,一個 UIWindow類的實例。你希望改變用戶界面的時候,你只需要改變窗口上顯示的視圖即可。

窗口提供了用戶界面的描繪空間,但是視圖對象提供實際的內容。視圖對象是UIView類的實例,描繪內容,並響應對內容的交互行爲。iPhone OS定義了標準視圖去表現表格,按鈕,文本框以及其他類型的交互控件。你可以把這些視圖都加入到窗口,或者你可以用繼承UIView定義一個定製視圖,實現一些定製描繪和事件響應。MoveMe程序定義了兩個視圖,由MoveMeView和PlacardView兩個類實現,顯示用戶界面,處理用戶交互事件。

程序啓動期間,目標是創建程序窗口並儘快的顯示一些初始內容。窗口從MainWindow.xib文件中展開。當程序達到啓動完成狀態,準備就緒可以處理事件的時候,UIApplication對象向委託對象發出applicationDidFinishLaunching:消息。這個消息暗示委託對象把內容放入窗口,並執行其他程序所需的初始化操作。

在MoveMe程序中,委託在applicationDidFinishLaunching:消息中做如下工作:

  1. 創建一個視圖控制器對象,管理窗口中的內容視圖。
  2. 使用MoveMeView類的實例初始化試圖控制器,這個實例保存在MoveMeView.xib文件內,作爲背景視圖並填滿整個窗口邊框。
  3. 把視圖控制器作爲子視圖加入窗口。
  4. 顯示窗口。

列表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];
}

想了解關於創建窗口和視圖的更詳細信息,參看iPhone OS編程指南的窗口和視圖章節。

描繪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資源文件內)和一個定製字符串,文本可以動態改變。要描繪內容,該類需要執行下面的步驟:

  1. 在視圖的原點描繪背景圖片。(因爲視圖大小已經調整到符合圖片尺寸,這個步驟提供了完整的按鈕北京。)
  2. 計算welcome字符串的位置,令其可以顯示在按鈕中間。(因爲字符串的尺寸可以改變,所以位置每次都需要根據當前的字符串尺寸計算。)
  3. 設定描繪顏色爲黑色。
  4. 用黑色描繪字符串,位置略有偏移。
  5. 設定描繪顏色爲白色。
  6. 在正確的位置用白色描繪字符串。

列表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代碼的描繪空間。按照什麼頻率更新描繪空間,使用什麼對象,取決於你自己的選擇。

對於每種描繪技術和如何使用他們的細節信息,參看iPhone OS編程指南的圖形和描繪。

處理觸摸事件

iPhone OS的多點觸摸接口令你的程序可以識別和響應多個手指觸摸設備產生的不同事件。響應多手指觸摸的能力帶來了強大的能力,但是也帶來跟傳統的基於鼠標的事件處理系統操作的明顯不同。每次手指觸摸設備的表面,觸摸傳感器產生一個觸摸事件。每次手指移動,額外的觸摸事件產生表明手指的新位置。一旦手指離開設備表面,系統分發另外一個觸摸事件表明這一點。

因爲同時可能有多個手指觸摸設備,這就產生了利用這些事件來識別複雜用戶手勢的可能性。系統對識別一些常用手勢(例如,雙擊)提供了幫助,但是你可以檢測到更復雜的手勢。當事件系統產生了一個新的觸摸事件,它包含了每個手指當前的狀態,包括觸摸或者剛離開設備的表面。因爲每個事件對象包括所有的活動觸摸事件,你可以用新的事件監控每個手指的動作。你可以在事件行爲之間跟蹤每個手指的移動來檢測手勢,你可以用來跟程序中的內容交互。例如,如果事件表明用戶正在實施手指分開和手指併攏手勢,而且下面的視圖支持放大,所以你可以用這些時間改變當前的縮放級別。

圖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類執行下面的操作:

  1. 當觸摸第一次發生,檢查事件發生的位置。
    • 雙擊Welcome按鈕外的位置,更新按鈕顯示的字符串。
    • 單擊按鈕,把按鈕的中心移到手指之下,並引發一個動畫放大按鈕。
    • 其他的觸摸都忽略。
  2. 如果手指在按鈕之內移動,按鈕的位置就會更新以匹配手指。
  3. 如果手指在按鈕內,然後離開設備的表面,按鈕以動畫的形式回到他的原始位置。

列表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:方法重新啓用用戶交互,這樣事件循環又可以繼續下去。

更多關於在iPhone OS下處理事件的信息,參看iPhone OS編程指南的事件處理章節。

按鈕動畫移動

在iPhone程序中,動畫起了非常重要的作用。動畫廣泛用於給用戶相關信息以及立即響應。例如,當用戶在程序中瀏覽層級數據,iPhone不是僅僅直接用其他的屏幕代替當前的屏幕,而是讓屏幕以動畫的方式就位。移動的方向用戶走向層級的高層還是低層,並給用戶可視化的信息這裏有新的信息。

因爲動畫很重要,所以UIKit的類中已經內建了對它的支持。MoveMe程序獲益於這種支持,動畫改變Welcome按鈕的外觀。當用戶第一次觸摸按鈕,程序引發一個動畫讓按鈕的尺寸變大。

當用戶放開這個按鈕,另外的動畫產生讓按鈕回到原始位置。創建這樣動畫的步驟如下:

  1. 調用你想動畫的視圖的beginAnimations:context:方法。
  2. 設置動畫的屬性。
  3. 調用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];
}

更多關於使用基於視圖的內建動畫的信息,參看iPhone OS編程指南的動畫視圖章節。更多關係核心動畫的信息,參看iPhone OS編程指南的應用核心動畫效果章節。

完成程序

前面的章節,你已經看到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文件中的屬性。

想了解更多關於程序的info.plist文件的信息,參看iPhone OS編程指南的屬性列表章節。

現在你已經瞭解了創建自己的iPhone程序需要的全部基礎信息。下面的步驟是學習更多的iPhone OS特性。程序應該利用iPhone OS內建的特性創建直觀而且令人愉悅的用戶體驗。這些特性有些在“深入探討你的程序”,但是關於完全的特性列表以及如何使用它們的信息,參看iPhone OS編程指南。

深入談討你的程序

這裏有一些用戶可以獲得的iPhone和iPod touch的特性。有些特性跟硬件相關,例如根據設備的方向自動調整視圖。有些是跟軟件相關的,例如所有的內建iPhone應用程序都使用同一個聯繫人列表。因爲下面描述的這些特性跟基本的用戶體驗有關,所以你應該在原始設計就考慮這些特性是否適用於你的程序。

用加速度傳感器跟蹤方向和移動

iPhone和iPod touch的加速度傳感器可以爲系統和你的程序提供輸入。一個加速度傳感器測量在一個方向上速度的變化。iPhone和iPod touch都有三個加速度傳感器用來測量3D空間的每個主要軸向上的變化,允許你檢測任何方向的移動。

圖5 加速度傳感器軸向

也許你不認爲測量加速度變化有啥用處,但是實際上這個信息可以有很大的用處。重力的作用總是把物體拉向地面。這個力的作用造成即使設備是靜止不動的,仍舊可以測量到朝向地面的加速度。通過跟蹤加速度傳感器記錄的加速度,以及加速度的大小,你可以精確的檢測到設備在3D空間內的方向。你可以把方向作爲程序的輸入。

系統使用加速度傳感器監控設備當前的方向,並在方向改變的時候通知程序。如果程序的用戶界面支持橫豎兩種模式,那麼你應該把視圖控制器加入到你的設計中。UIViewController類提供了,旋轉用戶界面以及根據方向改變自動調整視圖位置的機制。

如果你想直接訪問原始的加速度傳感器數據,你可以使用UIKit內的共享對象UIAccelerometer。加速度傳感器在一個可以配置的時間間隔下定時報告當前的加速度值。你還可以使用這些數據來檢測設備方向或者檢測其他的瞬間移動,例如用戶前後搖晃設備。你可以把這個信息作爲遊戲或者其他程序的輸入。想要找到如何配置UIAccelerometer對象,以及接收加速度傳感器事件的例子,參看iPhone OS編程指南的訪問加速度傳感器事件章節。

訪問用戶通訊錄

用戶的通訊錄列表是所有系統程序共享的重要資源。電話,郵件和短信息程序都用它來識別用戶要聯繫的聯繫人,並使基本交互變得容易,例如打電話,發郵件,或者短信息。你自己的程序也可以爲類似的目的訪問用戶的通訊錄信息,或者其他程序需要的相關信息。

圖6 訪問用戶通訊錄

iPhone OS提供對用戶通訊錄的直接訪問,以及通過一個標準的選擇接口進行的間接訪問。使用直接訪問,你可以直接用通訊錄數據庫獲得聯繫人信息。你可以用另外的方式來展現這些聯繫信息,或者根據程序的需求過濾它們。如果你不需要一個定製接口,iPhone OS也提供了一系列系統接口選擇和創建聯繫人。把這些接口加入你的程序需要一些額外的工作,但是可以令你的程序的外觀看起來更像系統的一部分。

訪問用戶的聯繫信息需要地址簿和地址簿用戶界面框架。更多關於這些框架,參看地址簿用戶界面框架參考手冊和地址簿框架參考手冊。

獲得用戶當前的位置

運行iPhone OS的設備是爲用戶頻繁使用而設計的。你所設計的程序需要把這一點納入考慮之中。互聯網和Web讓人們可以在任何地方經營自己的事業,所以可以追蹤用戶當前位置就變成了必備的用戶體驗了。畢竟,我們不能爲一個在洛杉磯口渴的人,列出紐約的咖啡店。這就是Core Location框架的用處。

Core Location框架追蹤手機機站和Wi-Fi無線熱點的信號,然後使用他們三角定位出用戶當前的位置(此文檔比較早期,現在已經包括直接獲取GPS信號了)。你可以僅用該框架獲取一個固定的初始位置,也可以在用戶位置發生變化的時候得到通知。利用這些信息,你可以過濾要提供給用戶的信息,或者做一些其他的事情。

在程序中獲取位置信息數據的例子,參見iPhone OS編程指南的“獲得用戶當前位置”章節。

播放音頻和視頻

iPhone OS通過Core Audio和OpenAL框架支持你的程序中的音頻特性,並使用Media Player框架提供視頻回放的能力。你可以用它播放簡單的聲音效果,或者多個聲道的音頻,混音,把它們限制在某個音域內,甚至可以使用iPhone的振動功能。如果你是一個遊戲開發者,而且你的代碼中已經使用了OpenAL的特性,你可以直接在iPhone OS中繼續使用這些代碼在你的遊戲中定位和回放你的音頻。

Media Player框架是用來回放全屏視頻文件。這個框架支持多種標準的電影文件格式,你可以控制回放的環境,包括是否顯示用戶控制器,以及視頻內容的長寬比。遊戲開發者可以利用這個框架播放過場影片或者預先渲染好的內容,而基於媒體的程序也可以用這個框架回放電影文件。

圖7 播放視頻

更多關於iPhone OS中的多媒體技術的信息,參見iPhone OS編程指南的“音頻和視頻技術”章節。

使用內置相機

iPhone的相機程序允許用戶拍攝照片,然後保存在照片庫中,和其他從電腦上傳來的照片放在一起。雖然iPod touch沒有照相機,但是它也有一個用來保存用戶上傳照片的照片庫。iPhone OS用UIKit框架中的UIImagePickerController類來支持這些特性。

圖8 iPhone的相機

UIImagePickerController類爲你的程序提供了相機和照片庫的訪問接口。包括照相和照片程序的很多程序,使用這些標準的系統接口。當你顯示一個圖片選擇界面,選擇控制器UIImagePickerController實現所有所需的用戶交互細節,然後返回一個結果圖像到你的應用程序。

更多關於照片選擇器接口的信息,參見iPhone OS程序開發指南中的“用照相機照相”以及“從照片庫中選擇照片”章節。

[轉]iOS開發工具

要開發iOS的應用程序,你需要一臺安裝有Xcode工具和Mac OS X的電腦。Xcode是Apple提供的開發工具集,提供項目管理,代碼編輯,創建執行程序,代碼級調試,代碼庫管理,性能調節等等功能。這個工具集的核心就是Xcode程序,提供了基本的源代碼開發環境。但是Xcode不是你使用的唯一個工具,下面的章節向你介紹創建iPhone應用程序需要的工具。

Xcode

你開發經驗的焦點是Xcode程序。Xcode是一個集成開發環境(IDE),提供所有的工具,令你可以創建和管理你的iPhone項目和源代碼,構建你的代碼成爲可執行文件,在iPhone模擬器或者真實設備上運行和調試你的代碼。

創建一個新的iPhone應用程序,從在Xcode創建一個新的項目開始。一個項目,管理與你的程序所有相關的信息,包括源代碼,構建設置,以及需要放在一起的規則。每個Xcode項目的核心就是項目窗口,如圖一所示。這個窗口提供了快速訪問你的程序所有關鍵元素的途徑。文件和文件組列表是你管理項目中文件的地方,包括你的源代碼文件,以及從這些源代碼文件創建的構建目標。工具欄提供訪問常用工具和命令的途徑,細節面板提供一個對你項目的工作可配置的空間。項目窗口的其他部分提供了項目的其他信息。

圖1 一個Xcode項目窗口

Xcode包含一個先進的文本編輯器,支持許多特性,例如代碼完成,語法高亮,代碼摺疊,以及行內的錯誤警告提示等等。Xcode的構建系統提供了默認的設置,另外還提供令你隨心設置環境的能力。如果你需要文檔,研究助手可以提供內容相關的文檔,Xcode文檔窗口讓你可以瀏覽和查找信息。

當你在Xcode中構建應用程序,你可以選擇爲iPhone模擬器創建,也可以選擇爲iPhone設備創建。模擬器提供了測試應用程序的本地環境,可以確保你的程序行爲完全符合你的需求。當你對程序的基本行爲已經滿意後,你可以告訴Xcode構建它,然後在連接到你的計算機上的iPhone和iPod touch上運行它。在設備上面運行程序提供了終極測試環境,而且Xcode可以把內建調試器植入運行在設備上的代碼中。

圖2 從Xcode運行項目

更多關於在iOS上構建項目的更多信息,參看iOS開發者指南中的開發環境章節。

界面構建器(Interface Builder)

界面構建器是用來可視化裝配應用程序用戶界面的工具。使用界面構建器,你可以用拖拽預置組件的方式裝配你的程序窗口。組件包括標準的系統控件,例如選擇器,文本框,按鈕,還包括用來展現程序提供的視圖的定製視圖。當你把組件放置在窗口上,你可以通過拖拽來移動它們,利用查看器修改他們的屬性,以及在這些對象和你的代碼之間建立聯繫。當界面達到你的要求時,你可以保存這些內容爲nib文件,這是一種定製的資源文件格式。在界面構建器中創建的nib文件,包括UIKit框架在運行期間在應用程序中重建相同對象所需要的全部信息。裝入nib文件會創建保存在文件中的所有對象的運行期間版本,嚴格按照界面構建器中的配置。程序還使用你指定的連接信息在新創建的對象和程序中已存的對象間建立聯繫。這些連接爲你的代碼提供了nib文件對象的指針,以及提供了在代碼中對象和用戶動作通訊需要的信息。

總的來說,界面構建器爲你創建程序用戶界面節約了大量的時間。界面構建器節省了創建,配置和定位構成用戶界面的對象所需要的定製代碼。因爲它是一個可視化編輯器,你可以看到與運行期間完全相同的界面。

Instruments

爲了確保你的軟件有最好的用戶體驗,Instruments環境可以幫你分析你的iPhone應用程序在模擬器或者設備上面運行的性能表現。Instruments可以從運行的應用程序中獲取數據,並可以把數據展現爲叫做時間線的圖形。你可以獲取關於程序的內存用量,磁盤活動,網絡活動以及圖形性能等數據。時間線視圖可以一個接一個的顯示所有這些不同類型的信息,讓你可以把程序的所有行爲聯繫起來(分析),而不僅僅是(一次僅觀察)某個領域內的行爲。需要了解更多信息,你可以查看Instruments獲取的細節例子。

圖3 使用Instruments調節你的應用程序

除了時間線視圖,Instruments還提供了其他的工具幫助分析基於時間的程序的行爲。例如,Instruments窗口可以讓你保存多次運行的數據,這樣你就可以發現程序的性能是得到了提升,還是需要繼續努力。你可以保存這些運行的數據在Instruments文檔內,並在任何時間打開他們。

如何在iPhone程序上使用Instruments的更詳盡的信息,請參考iOS編程指南的開發環境章節。爲了獲得如何使用Instruments的通用信息,請參考Instruments用戶指南。

[轉]Objective-C入門

Objective-C是一種簡單的計算機語言,設計爲可以支持真正的面向對象編程。Objective-C通過提供類定義,方法以及屬性的語法,還有其他可以提高類的動態擴展能力的結構等,擴展了標準的ANSI C語言。類的語法和設計主要是基於Smalltalk,最早的面向對象編程語言之一。

如果你以前使用過其他面向對象編程語言,那麼下面的信息可以幫助你學習Objective-C的基本語法。許多傳統的面向對象概念,例如封裝,繼承以及多態,在Objective-C中都有所體現。這裏有一些重要的不同,但是這些不同在這文章會表現出來,而且如果你需要還有更多詳細的信息存在。

如果你從來沒有使用任何編程語言編過程序,那麼你至少需要在開始之前,對相關概念進行一些基礎的瞭解。對象的使用和對象對象架構是iPhone程序設計的基礎,理解他們如何交互對創建你的程序非常重要。想了解面向對象概念的,請參看使用Objective-C進行面向對象編程。此外,參看Cocoa基礎指南可以獲得Cocoa中的面向對象設計模式的信息。

想了解更多Objective-C語言和語法的細節介紹,請參看Objective-C 2.0編程語言。

Objective-C: C的超集

Objective-C是ANSI版本C編程語言的超集,支持C的基本語法。在C代碼中,你定義頭文件和源代碼文件,從代碼實現細節分離公共聲明。Objective-C頭文件使用的文件名列在表1中。

表1 Objective-C代碼的文件擴展名

擴展名 內容類型
.h 頭文件。頭文件包含類,類型,函數和常數的聲明。
.m 源代碼文件。這是典型的源代碼文件擴展名,可以包含Objective-C和C代碼。
.mm 源代碼文件。帶有這種擴展名的源代碼文件,除了可以包含Objective-C和C代碼以外還可以包含C++代碼。僅在你的Objective-C代碼中確實需要使用C++類或者特性的時候才用這種擴展名。

當你需要在源代碼中包含頭文件的時候,你可以使用標準的#include編譯選項,但是Objective-C提供了更好的方法。#import選項和#include選項完全相同,只是它可以確保相同的文件只會被包含一次。Objective-C的例子和文檔都傾向於使用#import,你的代碼也應該是這樣的。

字符串

作爲C語言的超集,Objective-C支持C語言字符串方面的約定。也就是說,單個字符被單引號包括,字符串被雙引號包括。然而,大多數Objective-C通常不使用C語言風格的字符串。反之,大多數框架把字符串傳遞給NSString對象。NSString類提供了字符串的類包裝,包含了所有你期望的優點,包括對保存任意長度字符串的內建內存管理機制,支持Unicode,printf風格的格式化工具,等等。因爲這種字符串使用的非常頻繁,Objective-C提供了一個助記符可以方便地從常量值創建NSString對象。要使用這個助記符,你需要做的全部事情,是在普通的雙引號字符串前放置一個@符號,如下面的例子所示:

NSString*  myString = @"My String\n";
NSString*  anotherString = [NSString stringWithFormat:@"%d %s", 1, @"String"];

// 從一個C語言字符串創建Objective-C字符串
NSString*  fromCString = [NSString stringWithCString:"A C string" encoding:NSASCIIStringEncoding];

如同所有其他的面嚮對象語言,類是Objective-C用來封裝數據,以及操作數據的行爲的基礎結構。對象就是類的運行期間實例,它包含了類聲明的實例變量自己的內存拷貝,以及類成員的指針。Objective-C的類規格說明包含了兩個部分:接口和實現。接口部分包含了類聲明和實例變量的定義,以及類相關的方法。實現部分包含了類方法的實際代碼。圖1展現了聲明一個叫做MyClass的類的語法,這個類繼承自NSObject基礎類。類聲明總是由@interface編譯選項開始,由@end編譯選項結束。類名之後的(用冒號分隔的)是父類的名字。類的實例(或者成員)變量聲明在被大括號包含的代碼塊中。實例變量塊後面就是類聲明的方法的列表。每個實例變量和方法聲明都以分號結尾。

圖1 類聲明

列表1展現了前面例子中MyClass類的實現。類同與類聲明,類實現的位置也由兩個編譯選項確定,@implementation和@end。這些選項給編譯器提供了要將方法和對應類聯繫起來,所需的範圍信息。因此方法的定義和接口中對應的聲明是匹配的,只是多了個代碼塊而已。

列表1 類實現

@implementation MyClass

- (id)initWithString:(NSString *) aName
{
    if (self = [super init]) {
        count count = 0;
        data = nil;
        name = [aName copy];
        return self;
    }
}

+ (MyClass *)createMyClassWithString: (NSString *) aName
{
    return [[[self alloc] initWithString:aName] autorelease];
}
@end

注意: 雖然前面的類只聲明瞭方法,但是類可以聲明屬性。想了解更多關於屬性的信息,參看“屬性”。

 

當用變量保存對象的時候,始終應該使用指針類型。Objective-C對變量包含的對象支持強弱兩種類型。強類型指針的變量類型聲明包含了類名。弱類型指針使用id作爲對象的類型。弱類型指針常用於類的集合,在集合中對象精確的類型可以是未知的。如果你用過強類型語言,你也許覺得使用弱類型變量可能會帶來問題,但是他們實際上給了Objective-C程序巨大的靈活性,而且使它更強大。

下面的例子裏,展示了MyClass類的強類型和弱類型聲明變量:

MyClass*  myObject1;    // Strong typing
id        myObject2;    // Weak typing

 

方法

Objective-C中的類可以聲明兩種類型的方法:實例方法和類方法。實例方法就是一個方法,它在類的一個具體實例的範圍內執行。也就是說,在你調用一個實例方法前,你必須首先創建類的一個實例。而類方法,比較起來,也就是說,不需要你創建一個實例。

方法聲明包括方法類型標識符,返回值類型,一個或多個方法標識關鍵字,參數類型和名信息。圖2展示insertObject:atIndex:實例方法的聲明。聲明由一個減號(-)開始,這表明這是一個實例方法。方法實際的名字(insertObject:atIndex:)是所有方法標識關鍵的級聯,包含了冒號。冒號表明了參數的出現。如果方法沒有參數,你可以省略第一個(也是唯一的)方法標識關鍵字後面的冒號。本例中,這個方法有兩個參數。

圖2 方法聲明語法

當你想調用一個方法,你傳遞消息到對應的對象。這裏消息就是方法標識符,以及傳遞給方法的參數信息。發送給對象的所有消息都會動態分發,這樣有利於實現Objective-C類的多態行爲。也就是說,如果子類定義了跟父類的具有相同標識符的方法,那麼子類首先收到消息,然後可以有選擇的把消息轉發(也可以不轉發)給他的父類。

消息被中括號( [ 和 ] )包括。中括號中間,接收消息的對象在左邊,消息(包括消息需要的任何參數)在右邊。例如,給myArray變量傳遞消息insertObject:atIndex:消息,你需要使用如下的語法:

[myArray insertObject:anObj atIndex:0];

爲了避免聲明過多的本地變量保存臨時結果,Objective-C允許你使用嵌套消息。每個嵌套消息的返回值可以作爲其他消息的參數或者目標。例如,你可以用任何獲取這種值的消息來代替前面例子裏面的任何變量。所以,如果你有另外一個對象叫做myAppObject擁有方法,可以訪問數組對象,以及插入對象到一個數組,你可以把前面的例子寫成如下的樣子:

[[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];

雖然前面的例子都是傳遞消息給某個類的實例,但是你也可以傳遞消息給類本身。當給類發消息,你指定的方法必須被定義爲類方法,而不是實例方法。你可以認爲類方法跟C++類裏面的靜態成員有點像(但是不是完全相同的)。

 

類方法的典型用途是用做創建新的類實例的工廠方法,或者是訪問類相關的共享信息的途徑。類方法聲明的語法跟實例方法的幾乎完全一樣,只有一點小差別。與實例方法使用減號作爲方法類型標識符不同,類方法使用加號( + )。

下面的例子演示了一個類方法如何作爲類的工廠方法。在這裏,arrayWithCapacity是NSMutableArray類的類方法,爲類的新實例分配內容並初始化,然後返回給你。

NSMutableArray*   myArray = nil;    // nil is essentially the same as NULL

// Create a new array and assign it to the myArray variable.
myArray = [NSMutableArray arrayWithCapacity:0];

屬性

屬性是用來代替聲明存取方法的便捷方式。屬性不會在你的類聲明中創建一個新的實例變量。他們僅僅是定義方法訪問已有的實例變量的速記方式而已。暴露實例變量的類,可以使用屬性記號代替getter和setter語法。類還可以使用屬性暴露一些“虛擬”的實例變量,他們是部分數據動態計算的結果,而不是確實保存在實例變量內的。

實際上可以說,屬性節約了你必須要些的大量多餘的代碼。因爲大多數存取方法都是用類似的方式實現的,屬性避免了爲類暴露的每個實例變量提供不同的getter和setter的需求。取而代之的是,你用屬性聲明指定你希望的行爲,然後在編譯期間合成基於聲明的實際的getter和setter方法。

屬性聲明應該放在類接口的方法聲明那裏。基本的定義使用@property編譯選項,緊跟着類型信息和屬性的名字。你還可以用定製選項對屬性進行配置,這決定了存取方法的行爲。下面的例子展示了一些簡單的屬性聲明:

@property BOOL flag;
@property (copy) NSString* nameObject;  // Copy the object during assignment.
@property (readonly) UIView* rootView;  // Create only a getter method.

使用屬性另外的好處就是你可以在代碼中訪問他們的時候,使用點語法,如下面的例子所示:

myObject.flag = YES;
CGRect   viewFrame = myObject.rootView.frame;

雖然前面例子裏面的對象和屬性名是故意這麼取的,他們還是展現了屬性的靈活性。點語法實際上隱藏了對應的方法調用。每個可讀的屬性由一個與屬性同名的方法支持。每個可寫屬性由一個叫做“set屬性名”的額外方法來支持,屬性名的第一個字母要大寫。(這些方法是屬性的實際實現方式,也是你可以聲明一個沒有任何實例變量支持的屬性聲明的原因。)如果用方法來代替前面代碼中的屬性,你就要下下面的代碼:

[myObject setFlag:YES];
CGRect   viewFrame = [[myObject rootView] frame];

要了解更多關於如何聲明屬性的信息,參看Objective-C 2.0編程語言的屬性一章。

 

協議和代理

協議聲明瞭可以被任何類實現的方法。協議不是那些類本身。他們僅是定義一個接口,其他的對象去負責實現。你實現了協議裏面的方法,就叫做符合協議。

在iPhone OS中協議常用來實現委託對象。委託對象就是一個對象以其他的對象的模式行事。瞭解協議,委託和對象最好的辦法就是看一個例子。

UIApplication類實現了一個程序需要的行爲。如果想接收程序當前狀態的簡單消息,並不需要強制你創建UIApplication的一個子類,反之UIApplication類通過調用委託對象的指定方法來分發這些通知消息。實現UIApplicationDelegate方法的對象都可以接受這樣的通知,並進行響應的反應。

協議的聲明跟類接口的聲明很像,只是協議沒有父類,而且他們不會定義任何實例變量。下面的例子展示了一個有一個方法的協議聲明:

@protocol MyProtocol
		- (void)myProtocolMethod;
@end

在大多數委託協議情況下,使用某種協議僅僅是簡單的實現協議定義的方法而已。 有些協議要求你明確的表明你支持這種協議,協議可以指定必須或者可選的方法。在你深入開發的過程中,你應該花點時間學習協議以及他們的用途,請閱讀Objective-C 2.0編程語言的協議章節。

更多的信息

前面的信息是爲了讓你對Objective-C語言的基礎所有了解。本文提到的語言特性,你可以在閱讀完整文檔的時候找到。但是這個語言不僅僅有這些特性,所以最好請仔細閱讀文檔Objective-C 2.0編程語言


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章