2013年9月10日 星期二

iOS 自定義動態資料TableView (customized tableview) + 附加section header使用

此篇環境為iOS 5以上, 使用storyboard.

分為兩個部分,第一部分為基本的tableview,

第二部分加上section header.


Part 1,基本table view




使用table view 分成兩個步驟

一、設定stroy board


  1. 首先拉一個TableView元件到view controller中
  2. 再拉一個table view cell元件到table view 中
  3. 在table view cell中放好想要的ui配置,如下圖

  接下來幾個關鍵步驟!

  4.在table view cell的attribute inspector中Identifier要設定一個名字,之後才能抓到這個cell layout,如下



  5.在每一個table view cell的元件中的attribute inspector中tag欄位,設定一個號碼,之後才可以抓到這個元件,如下


  6.最後在這個table對應到的view controller上面define一個IBOutlet,並且與table相連

至此story board上的設置已經完成



二、在view controller中加code

1. 在header file (.h)檔案中

採用UITableViewDelegate,UITableViewDataSource兩個protocol:

@interface YourViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>

並且定義上面所提到的IBOutlet:

@property (weak, nonatomic) IBOutlet UITableView *mTableView;


和一個存放資料的Array:

NSMutableArray* tableData;

2.在implementation file(.m)檔案中

implement以下幾個dataSource protocol function

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [tableData count]; }
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *simpleTableIdentifier = @"announceTableCell"; //使用在story board設定的identifier才會抓到story board中的table view cell layout UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier]; //如果已經存在則重複使用 if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:simpleTableIdentifier]; } YourDataType *data = [tableData objectAtIndex:indexPath.row]; //利用在story board中設定的tag取得table view cell中的特定component UILabel *titleLabel = (UILabel*)[cell viewWithTag:1]; [titleLabel setText:data.title]; UILabel *contentLable = (UILabel*)[cell viewWithTag:2]; [contentLable setText:data.description]; return cell; }

3.在改變tableData的資料後記得要呼叫

[self.mTableView reloadData];
讓table view reload 顯示最新的資料內容, for example:

//假設執行一個向server 進行http request的function,並且傳回一個array [ServerApiCaller callApiWithSuccess:^(NSArray *result) { tableData = result; [self.mTableView reloadData]; } failure:^(NSError *error, id result) { }];


至此已經完成可以依據server回傳資料改變內容的TableView

Part 2, 加上section header



一、在storyboard中另外加入:

1.加入另外一個table view cell並且設置好想要的section header ui,如下顯示日期黑色那塊.


2.和先前一樣在attribute inspector中設定table view cell 的 identifier 和其中元件的tag


二、在implement file(.m)中加入header 相關的delegate method:

1.設定section header的高度:

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 106; }

2.設定section header的內容:

這裡的tabelData變數與part 1中並不相同,
使用section header時的table data有多種實作方式,這邊採用一個section的array,一個array cell包含了一個section object,其中又包含了這個section中的cell的方式


-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { NSString * const headerID = @"announceTableHeader"; //同一般的cell,依據id取得section header的layout UIView * headerView = [tableView dequeueReusableCellWithIdentifier:headerID]; if(headerView == nil) { headerView = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:headerID]; } //設定layout內的ui內容 //注意:這邊的tableData跟YourDataType和Part 1中的並不相同,一個array cell包含了一個section object,其中又包含了這個section中的cell YourDataType * data = [tableData objectAtIndex:section]; UILabel *weekDay = (UILabel*)[headerView viewWithTag:1]; [weekDay setText:[data getWeekDay]]; UILabel *monthDay = (UILabel*)[headerView viewWithTag:2]; [monthDay setText:[data getDay]]; UILabel *year = (UILabel*)[headerView viewWithTag:3]; [year setText:[data getYear]]; //設定上圖中的"th"位置 UILabel *th = (UILabel*)[headerView viewWithTag:4]; CGSize textSize = [[monthDay text] sizeWithFont:[monthDay font]]; CGFloat strikeWidth = textSize.width; [th setFrame:CGRectMake(monthDay.frame.origin.x + strikeWidth, monthDay.frame.origin.x, monthDay.frame.size.width, monthDay.frame.size.height)]; return headerView; }
part 1 中所實作的delegate method也要有所修改:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { //getListSize拿到這個section下的data cell數量 return [[tableData objectAtIndex:section] getListSize]; }

設定每個cell 內容的方法也要更改成:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *simpleTableIdentifier = @"announceTableCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:simpleTableIdentifier]; } //先從tableData中用indexPath.section拿到section YourDataType *data = [tableData objectAtIndex:indexPath.section]; //再從section中用indexPath.row拿到cell UILabel *titleLabel = (UILabel*)[cell viewWithTag:1]; [titleLabel setText:[data getTitleWithIndex:indexPath.row]]; UILabel *contentLable = (UILabel*)[cell viewWithTag:2]; [contentLable setText:[data getDescriptionWithIndex:indexPath.row]]; return cell; }