2011年2月23日 星期三

[More iPhone SDK 心得] Chap4 The Devil in the Detail View

主畫面
Table-Based Detail View
這章的標題很有趣,當按下 Hero cell 想要編輯時,就是 detail view 登場的時候,剛開始我還搞不懂 detail view 的意思。 一開始先討論 detail view 設計的方法, [Table-Based vs. Nib-Based],Human Interface Guidelines 並沒有對此做任何假設,這裡會使用 Table-Based 的方式。

Note:  >  英文叫做 disclosure indicator

而 Detail View 設計的困難在於其對應關係,例如我按下 Name 這個 cell 、或是按下 Birthdate cell,會有不同的 Editing SubController 產生,要如何設計其對應關係, that's a challenge。

下面的 code 是一般的做法,這種 nested switch 的方式造成 maintain 上的困難,本章是結合 paired array 和 nested array 的方式來處理對應關係,有彈性的多。


enum HeroEditControllerSections {
HeroEditControllerSectionName = 0,
HeroEditControllerSectionGeneral,
HeroEditControllerSectionCount
};
enum HeroEditControllerNameSection {
HeroEditControllerNameRow = 0,
HeroEditControllerNameSectionCount
};
enum HeroEditControllerGeneralSection {
HeroEditControllerGeneralSectionSecretIdentityRow,
HeroEditControllerGeneralSectionBirthdateRow,
HeroEditControllerGeneralSectionSexRow,
HeroEditControllerGeneralSectionCount
};
//Then, in every method where you are provided with an index path, you can take the
//appropriate action based on the row and section represented by the index path, using
//switch statements, like this:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
switch (section) {
case HeroEditControllerSectionName:
switch (row)
{
case HeroEditControllerNameRow :
// Create a controller to edit name
// and push it on the stack
...
break;
default:
break;
}
break;
case HeroEditControllerSectionGeneral:
switch (row) {
case HeroEditControllerGeneralSectionSecretIdentityRow:
// Create a controller to edit secret identity
// and push it on the stack
...
CHAPTER 4: The Devil in the Detail View 87
break;
case HeroEditControllerGeneralSectionBirthdateRow:
// Create a controller to edit birthdate and
// push it on the stack
...
break;
case HeroEditControllerGeneralSectionSexRow:
// Create a controller to edit sex and push it
// on the stack
...
break;
default:
break;
}
break;
default:
break;
}
}


// combine nested and paired array
- (void)viewDidLoad {
    sectionNames = [[NSArray alloc] initWithObjects:
                    [NSNull null],
                    NSLocalizedString(@"General", @"General"),
                    nil];
    rowLabels = [[NSArray alloc] initWithObjects:
 
                 // Section 1
                 [NSArray arrayWithObjects:NSLocalizedString(@"Name", @"Name"), nil],
 
                 // Section 2
                 [NSArray arrayWithObjects:NSLocalizedString(@"Identity", @"Identity"),
                  NSLocalizedString(@"Birthdate", @"Birthdate"),
                  NSLocalizedString(@"Sex", @"Sex"),
                  nil],
                 
                 // Sentinel
                 nil];
    rowKeys = [[NSArray alloc] initWithObjects:
               
               // Section 1
               [NSArray arrayWithObjects:@"name", nil],
   
               // Section 2
               [NSArray arrayWithObjects:@"secretIdentity", @"birthdate", @"sex", nil],
               
               // Sentinel
               nil];
    
rowControllers = [[NSArray alloc] initWithObjects:
 
                      // Section 1
                      [NSArray arrayWithObject:@"ManagedObjectStringEditor"],
 
                      // Section 2
                      [NSArray arrayWithObjects:@"ManagedObjectStringEditor"
                       @"ManagedObjectDateEditor",
                       @"ManagedObjectSingleSelectionListEditor", nil],
 
                      // Sentinel
                      nil];
    rowArguments = [[NSArray alloc] initWithObjects:
                    
                    // Section 1
                    [NSArray arrayWithObject:[NSNull null]],
                    
                    // Section 2,
                    [NSArray arrayWithObjects:[NSNull null], 
                     [NSNull null], 
                     [NSDictionary dictionaryWithObject:[NSArray 
                                                         arrayWithObjects:@"Male", @"Female", nil
                                                 forKey:@"list"], 
                     nil],
                    
                    // Sentinel
                    nil];
    
    
    [super viewDidLoad];
}


這裡有用到 [NSNull null]] 到的技巧,第一次看不是很明白,主要是用於 unname section 的情況??

接下來是設計真正的 editing subcontroller 的部份,下面兩張圖分別是 Birthdate 和 Name 欄位的editing subcontroller,這裡用了相同的父類別 (終於用到繼承了 XD) 來提取相同的部份。






對於 attribute 的字串處理 ,用到了 category 的技巧,如下 code

@protocol HeroValueDisplay
- (NSString *)heroValueDisplay;
@end
@interface NSString (HeroValueDisplay)
- (NSString *)heroValueDisplay;
@end
@interface NSDate (HeroValueDisplay)
- (NSString *)heroValueDisplay;
@end
@interface NSNumber (HeroValueDisplay)
- (NSString *)heroValueDisplay;
@end
@interface NSDecimalNumber (HeroValueDisplay)
- (NSString *)heroValueDisplay;
@end


其他要注意的是

  • loadview 的使用時機 (在 datapicker 中有用到)
  • UIButton 和 Bar Button 之間的不同
  • NSClassFromString 的使用技巧 (like Java's Introspection)

這章很多東西 = =























                                            

hi



沒有留言:

張貼留言