en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

2.03.2012

UITableView 的分類顯示 Sections

   

在之前的文章UITableView 的資料設定方式一文中,已經示範如何在 UITableView 中設定所要顯示的資料,以及分別顯示這些資料的細節,但是如果資料比數太多時該怎麼辦?你可以參考本篇文章的做法,將資料做分類的處理,並且建立快速索引,讓使用者能以最短的時間找到所需要的資料。


資料分類的概念
動態表格的內容多半是存放在陣列當中方便資料的存取,如果你有好幾類不同比的資料,你可以將這些資料分別存放在不同的陣列裡,最後再使用一個 NSMutableArray 將這些存放不同資料的陣列包起來,之後我們只要針對這個 NSMutableArray 做操作即可。(以下是沿用之前文章的程式碼做修改)
//資料初始化
roleArray = [[NSArray alloc] initWithObjects:@"野蠻人", @"法師", @"弓箭手", @"盜賊", @"德魯伊", @"騎士", nil];
monsterArray = [[NSArray alloc] initWithObjects:@"哥布林戰士", @"哥布林護衛", @"哥布林軍官", @"哥布林王", @"黑暗德魯伊", @"狼人", @"傀儡護衛", @"傀儡領主", @"蜘蛛", @"蝙蝠", nil];

heroicaArray = [[NSMutableArray alloc] initWithObjects:roleArray, monsterArray, nil];


UITableView Sections 的設定
如果要將資料作分類顯示,可以使用以下的內建方法函式,並回傳一個 NSInteger,告訴 UITableViewController 你想將資料分成幾類。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [heroicaArray count];
}

程式碼到此就已經算是完成資料的分類,後續的動作就是要顯示這些分類好的資料,大致的程式碼都和之前的文章差不多,只是操作的物件不同,可以透過方法函式所得到的 section 參數,決定於目前是要處理那一類的資料。
//設定每一類的資料筆數
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [[heroicaArray objectAtIndex:section] count];
}

//設定每一類的資料內容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    //製作可重復利用的表格欄位Cell
    static NSString *CellIdentifier = @"CellIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    //設定欄位的內容與類型
    cell.textLabel.text = [[heroicaArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
    return cell;
}


分類標題與快速索引
分類的標題可以家在分類的開頭或是結尾,同樣是透過方法函式所得到的 section 參數,來確認目前所在的分類。
//設定分類開頭標題
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    switch (section) {
        case 0:
            return @"英雄角色";
            break;

        case 1:
            return @"怪物角色";
            break;

        default:
            return @"";
            break;
    }
}

//設定分類結尾標題
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
}

另外,建立類似電話簿的快速索引,則可以透過下列內建方法函式,回傳一個快速索引的陣列,陣列內容的順序,就是你分類的順序。
//建立快速索引
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    NSArray *index = [[NSArray alloc] initWithObjects:@"英雄", @"怪物", @"武器", @"道具", @"戰利品", @"其他", nil];
    return index;
}


比較好的做法
在上述分類標題與快速索引的部分,使用 switch 與靜態的數值來做設定,其實這不是很恰當的做法,尤其當你的資料筆數非常龐大的時候,比較好的建議是將這些資訊同樣放入陣列裡面,且動態存取它們,來完成設定標題與建立索引陣列。

另外要注意的是,雖然是好幾類不同的資料,但是他們最好還是能擁有相同的屬性,即使該屬性為 nil。例如 A 類型的資料有顏色屬性,但是 B 類型沒有或是不需要,但是仍需為 B 類型的資料保留顏色屬性,即使它們的值都是 nil,這樣的觀念有點類似於多型 Polymorphism,這樣不但可以減少程式碼的撰寫,對於表格內的資料也能保持一致性。






4 則留言:

  1. 我依照了這篇教學文章以及上一篇"UITableView 的資料設定方式"來做這個APPS,完成后在模擬器的MasterView所顯示的資料一切正常,當點選“英雄角色”時,都能跳去相關的DetailViewController;但當點選“怪物角色”時,就不能跳去相關的DetailViewController。例如當我點選“哥布林戰士”時,就會跳去“野蠻人”的DetailViewController,當我點選“哥布林護衛”時,就會跳去“法師”的DetailViewController,如此類推。我認為是上一篇的”detailView“在傳遞參數時的方法需要作出修改才能應用在這篇教學。但我嘗試了很久也未能成功,希望你能給予我一些提示或解決方法,謝謝^^

    回覆刪除
    回覆
    1. Kwai Lam 您好:

      本篇的內容是著重在分類與索引的部份,如果您只是按照上述的程式碼實作,的確會產生這樣的問題。

      回到您問題的陳述,不難發現不管選擇哪一個 Sections 內的 Cell ,都只會對應到同一個陣列內的數值,
      原因很簡單,在前一篇文章「UITableView 的資料設定方式」中,我們只有針對單一陣列的內容去做設定,因為這時我們的資料內容很單純,只有單單一個 roleArray,但是到本篇文章時,我們已經將資料內容擴大,並且使用一個 NSMutableArray 「heroicaArray」來放置每個 Sections 的資料,因此在 heroicaArray 中會有兩個元素,一個是 roleArray,而另一個是 monsterArray。

      解決問題的方法,只要將 Sections 的觀念放在之其那篇文章中即可。


      ex:(取得 Sections 的方法)你可以將它應用在 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 方法函式中:

      NSIndexPath *path = [self.tableView indexPathForSelectedRow];
      NSString *objectName = [[heroicaArray objectAtIndex:path.section] objectAtIndex:path.row];

      [segue.destinationViewController setDetailItem:objectName];

      刪除
    2. 成功了,感謝你詳細的講解!

      刪除
    3. 不用客氣啦!

      刪除