亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

從零開始學ios開發(十六):NavigationControllersandTableViews(下)

2019-11-14 20:19:39
字體:
來源:轉載
供稿:網友

終于進行到下了,這是關于Navigation Controllers和Table Views的最后一個例子,稍微復雜了一點,但也僅僅是復雜而已,難度不大,我們開始吧。

如果沒有上一篇的代碼,可以從這里下載Nav_2

1)第六個subtableview:An Editable Detail Pane
打開你iphone上的通訊錄,首先看見的是你通訊錄中所有的聯系人列表,點選一個聯系人,就會切換到聯系人的詳細頁面,再點擊右上角的編輯按鈕,就可以對聯系人的內容進行編輯。我們的這個例子與之有點相似之處,首先也是一個列表,這個列表中列舉了歷任的美國總統(他們的名字和任期),然后點擊其中的一個總統名字,view切換到該總統的詳細頁面,我們可以對該總統的信息進行編輯保存。

好了,開始我們的例子,與以往不同的是,這次我們需要先創建一個類,叫做BIDPResident,這個類記錄了每位美國總統的信息,名字啊,任職起止年,來自哪個政黨等信息。選中Project navigator中的Nav文件夾,單擊鼠標右鍵,選擇“New File...”,在彈出的窗口中,左邊選擇Cocoa Touch,右邊選擇Objective-C class,點擊Next按鈕,在下一個窗口中將class命名為BIDPresident,Subclass of命名為NSObject,點擊Next按鈕,完成創建。


打開BIDPresident.h,添加如下代碼

復制代碼
#import <Foundation/Foundation.h>#define kPresidentNumberKey @"President"#define kPresidentNameKey   @"Name"#define kPreisdentFromKey   @"FromYear"#define kPresidentToKey     @"ToYear"#define kPresidentPartyKey  @"Party"@interface BIDPresident : NSObject <NSCoding>@property int number;@property (nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *fromYear;@property (nonatomic, copy) NSString *toYear;@property (nonatomic, copy) NSString *party;@end
復制代碼

我們首先定義了5個常量,這5個常量會在標識總統信息的時候用到,看后面的代碼就明白了,我們一般在編程的時候都會用到常量,這里沒什么區別。接著我們用到了一個新的家伙<NSCoding>協議,這個東西的作用是將一個對象寫入文件或者從文件中創建一個對象(the NSCoding protocol is what allows this object to be written to and created from files.)在這里你僅僅需要知道這些就可以了,在以后的章節中,會對NSCoding進行詳細的了解。

好了,我們接著打開BIDPresident.m,添加如下代碼

復制代碼
#import "BIDPresident.h"@implementation BIDPresident@synthesize number;@synthesize name;@synthesize fromYear;@synthesize toYear;@synthesize party;#pragma mark -#pragma mark NSCoding- (void)encodeWithCoder:(NSCoder *)coder{    [coder encodeInt:self.number forKey:kPresidentNumberKey];    [coder encodeObject:self.name forKey:kPresidentNameKey];    [coder encodeObject:self.fromYear forKey:kPreisdentFromKey];    [coder encodeObject:self.toYear forKey:kPresidentToKey];    [coder encodeObject:self.party forKey:kPresidentPartyKey];}- (id)initWithCoder:(NSCoder *)coder{    if(self = [super init])    {        number = [coder decodeIntForKey:kPresidentNumberKey];        name = [coder decodeObjectForKey:kPresidentNameKey];        fromYear = [coder decodeObjectForKey:kPreisdentFromKey];        toYear = [coder decodeObjectForKey:kPresidentToKey];        party = [coder decodeObjectForKey:kPresidentPartyKey];    }    return self;}
復制代碼

encodeWithCoder和initWithCoder是2個NSCoding的協議方法,encodeWithCoder用于將我們的對象進行編碼,initWithCoder用于將我們的對象進行解碼。這2個方法的作用是從plist中讀取數據,并轉換成BIDPresident的對象。

下載President.plist.zip,解壓后,將President.plist拖入到項目中

下面開始創建controller,我們需要創建2個controller,一個用于展示presidents列表,另一個是展示總統信息的詳細頁面。選中Project navigator中的Nav文件夾,單擊鼠標右鍵,選擇“New File...”,在彈出的窗口中,左邊選擇Cocoa Touch,右邊選擇Objective-C class,點擊Next按鈕,在下一個窗口中將class命名為BIDPresidentsViewController,Subclass of命名為BIDSecondLevelViewController,點擊Next按鈕,完成創建。使用同樣的方法再創建一個,class命名為BIDPresidentDetailController,Subclass of命名為UITableViewController。

打開BIDPresidentsViewController.h,添加如下代碼

復制代碼
#import "BIDSecondLevelViewController.h"@interface BIDPresidentsViewController : BIDSecondLevelViewController@property (strong, nonatomic) NSMutableArray *list;@end
復制代碼

這個list用于保存Presidents的列表

打開BIDPresidentsViewController.m,添加如下代碼

復制代碼
#import "BIDPresidentsViewController.h"#import "BIDPresidentDetailController.h"#import "BIDPresident.h"@implementation BIDPresidentsViewController@synthesize list;- (void)viewDidLoad{    [super viewDidLoad];    NSString *path = [[NSBundle mainBundle] pathForResource:@"Presidents" ofType:@"plist"];        NSData *data;    NSKeyedUnarchiver *unarchiver;        data = [[NSData alloc] initWithContentsOfFile:path];    unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];    NSMutableArray *array = [unarchiver decodeObjectForKey:@"Presidents"];    self.list = array;    [unarchiver finishDecoding];}- (void)viewWillAppear:(BOOL)animated{    [super viewWillAppear:animated];    [self.tableView reloadData];}
復制代碼

上面這段代碼中,要對viewDidLoad稍微解釋一下:
首先,通過下面的代碼找到Presidents.plist:
NSString *path = [[NSBundlemainBundle] pathForResource:@"Presidents"ofType:@"plist"];

接著,我們聲明了一個NSData對象,一個NSKeyedUnarchiver對象,NSData是一個保存數據的類,其保存數據的格式是二進制的(應該是二進制的吧,byte類型),然后NSData可以將其內容轉換為其他的類型,例如NSString。NSKeyedUnarchiver類從字節流中讀取數據(與NSKeyedUnarchiver類對應的有一個NSKeyedArchiver類,它的作用是將對象寫入字節流)。
NSData *data;
NSKeyedUnarchiver *unarchiver;

接著初始化data中的數據,Presidents.plist中的數據,然后將data中的數據賦給unarchiver
data = [[NSDataalloc] initWithContentsOfFile:path];
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

接著對unarchiver中的內容進行解碼,并賦給新創建的NSMutableArray
NSMutableArray *array = [unarchiver decodeObjectForKey:@"Presidents"];

最后將array賦值給list,并終止解碼過程
self.list = array;
[unarchiver finishDecoding];

這段viewDidLoad代碼如果是第一次接觸的話,確實有點搞腦子,幾個對象是第一次遇到,如果實在看不懂也不要太在意,只要知道這個過程是干嘛的,只要知道我們從plist中讀取了Presidents的對象,然后把它賦值給了list就可以了。

viewWillAppear貌似也是我們第一次使用到吧,這個方法是在每次view即將出現之前被調用到的(在viewDidLoad之后被調用),當我們修改了某一個president的信息后,那么這個president在列表中的顯示也將發生變化,我們所采用的方法不是去判斷這個president出現在列表的哪個位置,而是不管三七二十一,整個的重新載入列表,這是一個比較偷懶且簡單易實現的方法。

繼續添加代碼

復制代碼
#pragma mark -#pragma mark Table Data Source Methods- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return [list count];}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    static NSString *PresidentListCellIdentifier = @"PresidentListCellIdentifier";        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PresidentListCellIdentifier];    if(cell == nil)    {        cell = [[UITableViewCell alloc]                initWithStyle:UITableViewCellStyleSubtitle                reuseIdentifier:PresidentListCellIdentifier];    }    NSUInteger row = [indexPath row];    BIDPresident *thePres = [self.list objectAtIndex:row];    cell.textLabel.text = thePres.name;    cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ - %@", thePres.fromYear, thePres.toYear];    return cell;}
復制代碼

在tableView:cellForRowAtIndexPath方法中,首先要注意的是,cell的style是UITableViewCellStyleSubtitle。其次我們每次都對cell的textLabel和detailTextLabel重新賦值,這個是因為我們可能會對某些president的信息進行修改,這樣重新取回的cell中保存的還是舊數據,所以要更新一下。

繼續添加代碼

復制代碼
#pragma mark -#pragma mark Table Delegate Methods- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    NSUInteger row = [indexPath row];    BIDPresident *prez = [self.list objectAtIndex:row];        BIDPresidentDetailController *childController = [[BIDPresidentDetailController alloc] initWithStyle:UITableViewStyleGrouped];        childController.title = prez.name;    childController.president = prez;        [self.navigationController pushViewController:childController animated:YES];}
復制代碼

當我們點選一個cell后,將創建一個BIDPresident的對象,里面包含了選中的president的詳細信息,然后創建一個BIDPresidentDetailController的對象childController,并設置其title屬性(可以在navigation bar上面顯示)和其需要展示的president,BIDPresidentDetailController對象的style為UITableViewStyleGrouped。navigationController的pushViewController方法之前也用到過,將新的childController push進棧并顯示childController。

另外,這里應該會有一個錯誤:childController.president = prez,因為到目前位置BIDPresidentDetailController中什么都沒有,更別說president,不過沒關系,先留在這里,我們馬上就要去實現BIDPresidentDetailController了。

保存好文件后,我們開始編輯BIDPresidentDetailController,打開BIDPresidentDetailController.h,添加如下代碼

復制代碼
#import <UIKit/UIKit.h>@class BIDPresident;#define kNumberOfEditableRows   4#define kNameRowIndex           0#define kFromYearRowIndex       1#define kToYearRowIndex         2#define kPartyIndex             3#define kLabelTag               4096@interface BIDPresidentDetailController : UITableViewController <UITextFieldDelegate>@property (strong, nonatomic) BIDPresident *president;@property (strong, nonatomic) NSArray *fieldLabels;@property (strong, nonatomic) NSMutableDictionary *tempValues;@property (strong, nonatomic) UITextField *currentTextField;- (IBAction)cancel:(id)sender;- (IBAction)save:(id)sender;- (IBAction)textFieldDone:(id)sender;@end
復制代碼

在BIDPresidentDetailController中定義了一個style為grouped的table,且這個table有4行,分別記錄了當前president的名字,擔任總統的開始年份,擔任總統的結束年份和來自哪個政黨。上面的代碼中,前5個常量分別表示了這些信息:
#define kNumberOfEditableRows  4 // 有4行可編輯的行
#define kNameRowIndex                0 // president名字
#define kFromYearRowIndex    1 // 開始年份
#define kToYearRowIndex      2 // 結束年份
#define kPartyIndex                  3 // 政黨

最后一個常量kLabelTag在之后的code中會有解釋,它的作用是取回cell中的UILabel,看之后的代碼就會明白。

在這里我們引入了一個新的協議<UITextFieldDelegate>。

*president:指向當前的總統,這個就是我們上面提到的那個錯誤,現在把那個錯誤彌補了。
*fieldLabels:是一個Array,里面保存了與4個常量對應的label,kNameRowIndex對應fieldLabels中index為0的label,kFromYearRowIndex對應fieldLabels中index為1的label,以此類推,具體如何實現還是看之后的代碼吧,這樣比較直觀。
*tempValues:是一個mutable dictionary,它將臨時保存用戶修改過的值,因為當一個值被修改后(例如任職的開始年份),我們并不喜歡它直接反映到最終的list中,因為一旦用于點擊了Cancel按鈕,所有的修改都將作廢,因此我們先保存在tempValues中,如果之后用戶點擊的是Save按鈕,那么才會將值保存到list中。
*currentTextField:當用戶點擊一個BIDPresidentDetailController的text field時,currentTextField將指向那個text field。我們必須擁有一個currentTextField的原因有點復雜,當我們完成對一個text field修改的時候,我們可能會跳轉到另一個text field,此時UITextField的delegate方法textFieldDidEndEditing方法將被觸發,textFieldDidEndEditing有當前的textfield參數傳人,可以知道是哪個textfield在編輯,我們可以保存最新值到tempValues中,但是當我們點擊Save按鈕的時候,BIDPresidentDetailController將會出棧并顯示BIDPresidentsViewController,那么此時我們沒有機會得到是那個textfield在編輯,因此我們也沒有辦法保存最新的值到tempValues中,基于這種情況,我們定義了currentTextField,這樣就可以知道最后編輯的那個textfield,也可以獲得其值,這樣就可以保存最新的值到tempValues中去了。

說了一大段了理論,大家是不是看的云里霧里的,我也說的云里霧里的,還是看具體的實現吧,這樣可以撥開云霧見月明。

打開BIDPresidentDetailController.m,添加如下代碼

復制代碼
#import "BIDPresidentDetailController.h"#import "BIDPresident.h"@implementation BIDPresidentDetailController@synthesize president;@synthesize fieldLabels;@synthesize tempValues;@synthesize currentTextField;- (IBAction)cancel:(id)sender{    [self.navigationController popViewControllerAnimated:YES];}- (IBAction)save:(id)sender{    if (currentTextField != nil) {        NSNumber *tagAsNum = [NSNumber numberWithInt:currentTextField.tag];        [tempValues setObject:currentTextField.text forKey:tagAsNum];    }    for (NSNumber *key in [tempValues allKeys]) {        switch ([key intValue]) {            case kNameRowIndex:                president.name = [tempValues objectForKey:key];                break;            case kFromYearRowIndex:                president.fromYear = [tempValues objectForKey:key];                break;            case kToYearRowIndex:                president.toYear = [tempValues objectForKey:key];                break;            case kPartyIndex:                president.party = [tempValues objectForKey:key];                break;        }    }    [self.navigationController popViewControllerAnimated:YES];        NSArray *allControllers = self.navigationController.viewControllers;    UITableViewController *parent = [allControllers lastObject];    [parent.tableView reloadData];}- (IBAction)textFieldDone:(id)sender{    [sender resignFirstResponder];}
復制代碼

第一個實現的是cancel action方法,當用戶點擊Cancel按鈕時,會調用該方法。當cancel action被觸發后,當前的view會出棧(BIDPresidentDetailController出棧),之前的一個view會顯示出來(BIDPresidentsViewController會到棧的頂端顯示)。

第二個實現的是save action方法,當用戶點擊Save按鈕時,會調用該方法。save action的目的是保存用戶對于信息的修改,當save action被觸發后,位于tempValues中的值將被寫入到president中,但是有一種情況是例外的,如果用戶正在編輯一個textField,當編輯完后,立刻點擊Save按鈕(此時textField還是獲得焦點的,虛擬鍵盤也還在),那么當前正在被編輯的textField的內容還沒有保存到tempValues中,如果直接保存數據,當前textField中的內容將無法保存,因此為了解決這個問題,我們引入了之前所說的currentTextField,在save action中,我們首先判斷currentTextField是否為nil,如果不是,則說明當前有textField正在被編輯,我們就通過currentTextField來獲得當前編輯的textField的內容,并把值保存進tempValues中。
if (currentTextField != nil) {
        NSNumber *tagAsNum = [NSNumber numberWithInt:currentTextField.tag];
        [tempValues setObject:currentTextField.text forKey:tagAsNum];
}

之后的快速遍歷tempValues中的每一個值,將其保存到president中。tempValues的類型是NSMutableDictionary,它的key是行號,即每個textField所處的在第幾行的行號,通過行號獲得tempValues中對應的值,并賦給president。
for (NSNumber *key in [tempValues allKeys]) {
    switch ([key intValue]) {
        case kNameRowIndex:
            president.name = [tempValues objectForKey:key];
            break;
            ......
        default:
            break;
    }
}

之后就是當前的view出棧,父view提升為第一個view顯示
[self.navigationControllerpopViewControllerAnimated:YES];

我們還需要做一件事情,因為我們可能對信息進行了修改,因此必須重新load父view中的數據(BIDPresidentsViewController中的數據)。代碼中首先列舉出所有navigation中的viewController,其實也就只有一個,因此可以用lastObject獲得,然后重新載入數據即可。
NSArray *allControllers = self.navigationController.viewControllers;
UITableViewController *parent = [allControllers lastObject]; // 只有一個controller,如果換成objectAtIndex:0也是對的
[parent.tableView reloadData];

第三個實現的是textFieldDone action方法,當完成對一個textField內容修改后,點擊虛擬鍵盤上面的Done按鈕,使虛擬鍵盤消失。

繼續添加如下代碼

復制代碼
#pragma mark -- (void)viewDidLoad{    [super viewDidLoad];    NSArray *array = [[NSArray alloc] initWithObjects:@"Name:", @"From:", @"To:", @"Party:", nil];    self.fieldLabels = array;        UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]                                     initWithTitle:@"Cancel"                                     style:UIBarButtonItemStylePlain                                     target:self                                     action:@selector(cancel:)];    self.navigationItem.leftBarButtonItem = cancelButton;        UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]                                   initWithTitle:@"Save"                                   style:UIBarButtonItemStyleDone                                   target:self                                   action:@selector(save:)];    self.navigationItem.rightBarButtonItem = saveButton;        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];    self.tempValues = dict;}
復制代碼

首先創建4個string,并賦給fieldLabels。接著創建了2個按鈕,cancelButton和saveButton。cancelButton中定義了它調用的action為cancel,并且它放置的位置為navigationbar的左邊。同樣saveButton中定義了它調用的action為save,并且它放置的位置為navigationbar的右邊。最后我們創建了一個NSMutableDictionary對象,并使tempValues指向這個對象。

接著添加代碼

復制代碼
#pragma mark -#pragma mark Table Data Source Methods- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return kNumberOfEditableRows;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    static NSString *PresidentCellIdentifier = @"PresidentCellIdentifier";        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PresidentCellIdentifier];    if (cell == nil) {        cell = [[UITableViewCell alloc]                initWithStyle:UITableViewCellStyleDefault                reuseIdentifier:PresidentCellIdentifier];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 75, 25)]; label.textAlignment = UITextAlignmentRight; label.tag = kLabelTag; label.font = [UIFont boldSystemFontOfSize:14]; [cell.contentView addSubview:label]; UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(90, 12, 200, 25)]; textField.clearsOnBeginEditing = NO; [textField setDelegate:self]; textField.returnKeyType = UIReturnKeyDone; [textField addTarget:self action:@selector(textFieldDone:) forControlEvents:UIControlEventEditingDidEndOnExit]; [cell.contentView addSubview:textField]; } NSUInteger row = [indexPath row]; UILabel *label = (UILabel *)[cell viewWithTag:kLabelTag]; UITextField *textField = nil; for (UIView *oneView in cell.contentView.subviews) { if([oneView isMemberOfClass:[UITextField class]]) textField = (UITextField *)oneView; } label.text = [fieldLabels objectAtIndex:row]; NSNumber *rowAsNum = [NSNumber numberWithInt:row]; switch (row) { case kNameRowIndex: if ([[tempValues allKeys] containsObject:rowAsNum]) textField.text = [tempValues objectForKey:rowAsNum]; else textField.text = president.name; break; case kFromYearRowIndex: if ([[tempValues allKeys] containsObject:rowAsNum]) textField.text = [tempValues objectForKey:rowAsNum]; else textField.text = president.fromYear; break; case kToYearRowIndex: if ([[tempValues allKeys] containsObject:rowAsNum]) textField.text = [tempValues objectForKey:rowAsNum]; else textField.text = president.toYear; break; case kPartyIndex: if ([[tempValues allKeys] containsObject:rowAsNum]) textField.text = [tempValues objectForKey:rowAsNum]; else textField.text = president.party; break; default: break; } if (currentTextField == textField) { currentTextField = nil; } textField.tag = row; return cell;}
復制代碼

tableView:numberOfRowsInSection就不解釋了,和之前的一樣,重點看一下tableView:cellForRowAtIndexPath。

首先,我們再明確一次這個方法被調用的時機,定table view中的cell要出現時,會調用該方法來創建或者取回cell,也就是說cell是一個一個單獨創建的或者取回的,他們不是一下子創建完,必須一個一個創建,好了記住這一點后再看代碼就會比較容易理解。

代碼一開始還是和以前的例子一樣定義identifier,然后試著取回cell,如果不行就創建cell
static NSString *PresidentCellIdentifier = @"PresidentCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PresidentCellIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:PresidentCellIdentifier];
這些就一筆帶過了,之后的代碼才是重點。

在創建cell的同時,還創建了一個UILabel和UITextField。創建UILabel時,我們指定了它的位置,文字是右對齊的,tag是kLabelTag(可以利用這個kLabelTag取回cell中的label),設置了字體,最后將label添加到cell中。
UILabel *label = [[UILabelalloc] initWithFrame:CGRectMake(10, 10, 75, 25)];
label.textAlignment = UITextAlignmentRight;
label.tag = kLabelTag;
label.font = [UIFont boldSystemFontOfSize:14];
[cell.contentView addSubview:label];

創建UITextField時,指定了它的位置,設置其獲得焦點后原有的內容不消失,設置self作為它的delegate(textField是在BIDPresidentDetailController上創建的,BIDPresidentDetailController也是它的delegate文件,它的協議方法都在這個文件中實現),當點擊textField時,會有虛擬鍵盤彈出,將虛擬鍵盤的Return鍵設置成Done鍵,接著為textField的Did End On Exit事件制定action為textFieldDone(addTarget是說明textFieldDone在哪里,用self就是說在BIDPresidentDetailController中),最后將textField添加到cell中。
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(90, 12, 200, 25)];
textField.clearsOnBeginEditing = NO;
[textField setDelegate:self];
textField.returnKeyType = UIReturnKeyDone;
[textField addTarget:self action:@selector(textFieldDone:) forControlEvents:UIControlEventEditingDidEndOnExit];
[cell.contentView addSubview:textField]

注意,在創建textField的時候,我們并沒有為其添加tag,我們使用遍歷cell中所用subviews的方式來找到textField的。接著看下面的代碼,通過indexPath我們可以知道當前的cell是table view中的第幾個
NSUInteger row = [indexPath row];

接著就是得到當前cell中的label和textField,為他們賦值。因為cell中只有一個label的tag為kLabelTag,基于這點,我們可以用過tag來找到label
UILabel *label = (UILabel *)[cell viewWithTag:kLabelTag];
在cell中也只有一個textField,所以只要遍歷cell中包含的所有view,并判斷view的類型是不是UITextField,就可以找到textField
UITextField *textField = nil;
for (UIView *oneView in cell.contentView.subviews) {
    if([oneView isMemberOfClass:[UITextField class]])
        textField = (UITextField *)oneView;
}

找到了label和textField就為他們賦值,label的賦值相對簡單,我們已經知道是第幾個cell,那么就可以在fieldLabels中找到對應的值賦給label
label.text = [fieldLabels objectAtIndex:row];

textField相對來說比較復雜些,我們首先要判斷當前的內容是不是被修改過,如果修改過,那么我們要從tempValues中取值,如果沒有修改過,則從president對象中取值(之后的代碼會有對tempValues的賦值,看來就明白了,這里稍微想一想原理,應該可以理解)
if ([[tempValues allKeys] containsObject:rowAsNum])
    textField.text = [tempValues objectForKey:rowAsNum];
else
    textField.text = president.name;

接著是判斷當前的textField是否是正在編輯的textField(currentTextField),如果是就將currentTextField設為nil,因為textField的內容已經發生了變化,里面包含的都是最新的值,所有currentTextField就不需要了。
if (currentTextField == textField) {
    currentTextField = nil;
}

最后設置textField的tag為row,并返回cell
textField.tag = row;
return cell;

繼續添加代碼

復制代碼
#pragma mark -#pragma mark Table Delegate Methods- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath{    return nil;}#pragma mark Text Field Delegate Methods- (void)textFieldDidBeginEditing:(UITextField *)textField{    self.currentTextField = textField;}- (void)textFieldDidEndEditing:(UITextField *)textField{    NSNumber *tagAsNum = [NSNumber numberWithInt:textField.tag];    [tempValues setObject:textField.text forKey:tagAsNum];}
復制代碼

第一個是tableview的delegate方法,返回nil表示不能選擇table view中的cell。

之后的2個都是textField的delegate方法,第一個textFieldDidBeginEditing,當textField變成first responder時(獲得焦點,彈出虛擬鍵盤時)會觸發這個方法,在這個方法中,我們將currentTextField指向了當前的textField,這個就能夠保證我們在點擊Save按鈕時,可以得到正確的值。

第二個是textFieldDidEndEditing方法,當textField失去焦點或者點選了虛擬鍵盤上的Done按鈕就會觸發該方法,在這個方法中,首先獲得textField的tag,然后為tempValues賦值,值是當前textField的text,key是textField的tag。

至此BIDPresidentDetailController中所有的代碼都添加完成了,大家看懂了嗎?沒看懂的話多看幾遍,然后加上自己的理解,應該能夠搞明白的,相信自己,相信奇跡吧!

最后打開BIDFirstLevelController.m,添加如下代碼

復制代碼
#import "BIDFirstLevelController.h"#import "BIDSecondLevelViewController.h"#import "BIDDisclosureButtonController.h"#import "BIDCheckListController.h"#import "BIDRowControlsController.h"#import "BIDMoveMeController.h"#import "BIDDeleteMeController.h"#import "BIDPresidentsViewController.h"@implementation BIDFirstLevelController@synthesize controllers;- (void)viewDidLoad {    [super viewDidLoad];    self.title = @"First Level";    NSMutableArray *array = [[NSMutableArray alloc] init];            // Disclosure Button    BIDDisclosureButtonController *disclosureButtonController = [[BIDDisclosureButtonController alloc]                                                                 initWithStyle:UITableViewStylePlain];    disclosureButtonController.title = @"Disclosure Buttons";    disclosureButtonController.rowImage = [UIImage imageNamed:@"disclosureButtonControllerIcon.png"];    [array addObject:disclosureButtonController];        // Checklist    BIDCheckListController *checkListController = [[BIDCheckListController alloc]                                                   initWithStyle:UITableViewStylePlain];    checkListController.title = @"Check One";    checkListController.rowImage = [UIImage imageNamed:@"checkmarkControllerIcon.png"];    [array addObject:checkListController];            // Row Control    BIDRowControlsController *rowControlsController = [[BIDRowControlsController alloc]                                                       initWithStyle:UITableViewStylePlain];    rowControlsController.title = @"Row Control";    rowControlsController.rowImage = [UIImage imageNamed:@"rowControlsIcon.png"];    [array addObject:rowControlsController];        // Move Control    BIDMoveMeController *moveMeController = [[BIDMoveMeController alloc]                                             initWithStyle:UITableViewStylePlain];    moveMeController.title = @"Move Me";    moveMeController.rowImage = [UIImage imageNamed:@"moveMeIcon.png"];    [array addObject:moveMeController];        // Delete Me    BIDDeleteMeController *deleteMeController = [[BIDDeleteMeController alloc]                                                 initWithStyle:UITableViewStylePlain];    deleteMeController.title = @"Delete Me";    deleteMeController.rowImage = [UIImage imageNamed:@"deleteMeIcon.png"];    [array addObject:deleteMeController];        // BIDPresident View/Edit    BIDPresidentsViewController *presidentsViewController = [[BIDPresidentsViewController alloc]                                                             initWithStyle:UITableViewStylePlain];    presidentsViewController.title = @"Detail Edit";    presidentsViewController.rowImage = [UIImage imageNamed:@"detailEditIcon.png"];    [array addObject:presidentsViewController];        self.controllers = array;}
復制代碼

好了,終于可以編譯運行了,效果如下

選擇最后的Detail Edit,切換到BIDPresidentsViewController

隨便選擇一個總統的名字,切換到BIDPresidentDetailController

隨便選擇一個textField,會出現虛擬鍵盤,且會顯示Done按鈕

隨便編輯一下內容,然后點擊Done按鈕,鍵盤消失

點擊Save按鈕保存修改,或者點擊Cancel按鈕,放棄修改,這里我點擊的是Save按鈕

 

ok,大致功能就是這些,但是還是有可以改進的地方,如果你打開你iphone上的通訊錄,選擇一條記錄進行編輯,彈出的虛擬鍵盤上顯示的是return按鈕,點擊return按鈕,光標會移動到下一個textField中,這個是怎么實現的呢?其實沒那么困難,下面就來實現一下。

打開BIDPresidentDetailController.m,在tableView:cellForRowAtIndexPath方法中,將下面的這句話刪除

復制代碼
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    static NSString *PresidentCellIdentifier = @"PresidentCellIdentifier";        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PresidentCellIdentifier];    if (cell == nil) {        cell = [[UITableViewCell alloc]                initWithStyle:UITableViewCellStyleDefault                reuseIdentifier:PresidentCellIdentifier];                UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 75, 25)];        label.textAlignment = UITextAlignmentRight;        label.tag = kLabelTag;        label.font = [UIFont boldSystemFontOfSize:14];        [cell.contentView addSubview:label];                UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(90, 12, 200, 25)];        textField.clearsOnBeginEditing = NO;        [textField setDelegate:self];        textField.returnKeyType = UIReturnKeyDone;        [textField addTarget:self action:@selector(textFieldDone:) forControlEvents:UIControlEventEditingDidEndOnExit];        [cell.contentView addSubview:textField];    }    NSUInteger row = [indexPath row];
......
復制代碼

這樣虛擬鍵盤就不會顯示Done按鈕了,會顯示默認的return按鈕。然后找到textFieldDone方法,進行如下修改

復制代碼
- (IBAction)textFieldDone:(id)sender{    [sender resignFirstResponder];    UITableViewCell *cell = (UITableViewCell *)[[sender superview] superview];    UITableView *table = (UITableView *)[cell superview];    NSIndexPath *textFieldIndexPath = [table indexPathForCell:cell];    NSUInteger row = [textFieldIndexPath row];    row++;    if (row >= kNumberOfEditableRows) {        row = 0;    }    NSIndexPath *newPath = [NSIndexPath indexPathForRow:row inSection:0];    UITableViewCell *nextCell = [self.tableView cellForRowAtIndexPath:newPath];    UITextField *nextField = nil;    for (UIView *oneView in nextCell.contentView.subviews) {        if([oneView isMemberOfClass:[UITextField class]])            nextField = (UITextField *)oneView;    }    [nextField becomeFirstResponder];}
復制代碼

有一點需要明確,cell本身是不知道自己處在哪一行的,textFieldDone也沒有indexPath參數傳進來告知現在是哪一行,因此我們要另辟蹊徑。令人欣慰的是,table view知道當前是哪個cell正在響應,因此我們需要先得到table view。觸發textFieldDone的是textField,它的superview是contentView,contextView的superview是UITableViewCell,cell的superview是table view,好了,就這么找到table view了
UITableViewCell *cell = (UITableViewCell *)[[sender superview] superview];
UITableView *table = (UITableView *)[cell superview];

通過table view的indexPathForCell方法,可以得到indexPath,有了indexPath就可以知道當前的cell是哪一行的,最最關鍵的東西得到后,之后的代碼就很簡單了,大家應該可以很容易的理解。再次編譯code,并選擇一個preisdent進行編輯

這次出現的return按鈕,試著點擊return按鈕,光標會在4個textField之間循環跳躍,很是活潑。

好了,這個例子終于結束了,很不容易。

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩电影中文字幕在线| 国产精品美乳一区二区免费| 美女av一区二区| 精品久久久一区| 国产欧美一区二区| 欧美性xxxx极品高清hd直播| 久久99国产精品自在自在app| 色综合伊人色综合网| 欧美二区乱c黑人| 日韩av在线精品| 7m精品福利视频导航| 欧美日韩成人黄色| 国产一区玩具在线观看| 国产亚洲精品久久久久久| 亚洲最大在线视频| 亚洲视频综合网| 亚洲图片欧美日产| 国内精品久久久久久| 久久久久久久电影一区| 亚洲自拍偷拍一区| 国产精品最新在线观看| 成人亚洲欧美一区二区三区| 欧美成人免费播放| 国产综合视频在线观看| 午夜欧美大片免费观看| www.久久撸.com| 久久夜色精品国产| 国产精品夜色7777狼人| 超碰精品一区二区三区乱码| 在线播放日韩精品| 亚洲乱亚洲乱妇无码| 欧美超级乱淫片喷水| 亚洲精品一区二三区不卡| 蜜臀久久99精品久久久久久宅男| 亚洲黄色片网站| 69久久夜色精品国产7777| 日韩精品电影网| 国产大片精品免费永久看nba| 97久久超碰福利国产精品…| 成人免费视频a| 欧美午夜精品久久久久久人妖| 日韩精品在线观看网站| 日韩在线免费av| 色一区av在线| 日韩av在线免播放器| 另类专区欧美制服同性| 亚洲精品中文字幕女同| 亚洲欧美激情四射在线日| 中文字幕亚洲欧美一区二区三区| 欧美极品美女视频网站在线观看免费| 最近2019中文字幕第三页视频| 爱福利视频一区| 亚洲free性xxxx护士白浆| 日本乱人伦a精品| 亚洲白拍色综合图区| 国语对白做受69| 日韩av色在线| 欧美激情高清视频| 国产成人综合久久| 国产精品成人一区二区三区吃奶| 2025国产精品视频| 久久av中文字幕| 粗暴蹂躏中文一区二区三区| 亚洲永久在线观看| 日韩美女福利视频| 伊人一区二区三区久久精品| 青青久久aⅴ北条麻妃| 欧美成人一二三| 国产综合色香蕉精品| 久久久久久九九九| 韩国三级电影久久久久久| 成人在线国产精品| 日韩精品视频三区| 国产精品人成电影| 国产精品一区专区欧美日韩| 国产这里只有精品| 日韩av电影手机在线| 在线看日韩欧美| 欧美日韩亚洲成人| 欧美激情精品久久久久久免费印度| 欧美猛交ⅹxxx乱大交视频| 国产精品男女猛烈高潮激情| 777午夜精品福利在线观看| 国产精品第二页| 欧美成年人视频| 久久精品国产v日韩v亚洲| 97国产精品视频人人做人人爱| 日韩在线观看免费av| 欧美日韩人人澡狠狠躁视频| 18久久久久久| 成人美女av在线直播| 精品小视频在线| 国产精品毛片a∨一区二区三区|国| 国产精品久久久久一区二区| 日本久久亚洲电影| 欧美成人自拍视频| 日韩av男人的天堂| 免费91麻豆精品国产自产在线观看| 亚洲少妇激情视频| 国产精品三级久久久久久电影| 欧美人与性动交| 日韩一区二区三区国产| 色哟哟网站入口亚洲精品| 91丝袜美腿美女视频网站| 欧美成年人网站| 欧美美最猛性xxxxxx| 热久久美女精品天天吊色| 永久555www成人免费| 日韩国产精品亚洲а∨天堂免| 国产精品男女猛烈高潮激情| 成人欧美在线视频| 在线观看日韩欧美| 日韩欧美主播在线| 国产成人精品久久二区二区| 欧美日韩国产精品专区| 日本乱人伦a精品| 中文字幕亚洲欧美一区二区三区| 欧美高清无遮挡| 自拍偷拍免费精品| 亚洲香蕉在线观看| 精品欧美激情精品一区| 国产欧美日韩亚洲精品| 欧美电影院免费观看| 久久香蕉国产线看观看av| 最好看的2019的中文字幕视频| 亚洲精品中文字幕av| 尤物yw午夜国产精品视频| 国产精品视频网| 国内精品国产三级国产在线专| 精品亚洲男同gayvideo网站| 久久久精品欧美| 欧美成人三级视频网站| 欧美性视频在线| 日韩视频免费在线观看| 欧美日韩国产二区| 国产精品三级久久久久久电影| 亚洲性av网站| 91精品久久久久久久久中文字幕| 国产欧美精品日韩| 亚洲成人精品视频| 亚洲精品wwww| 精品视频偷偷看在线观看| 国产亚洲精品日韩| 日韩av观看网址| 亚洲欧洲av一区二区| 国产日韩在线看片| 91国偷自产一区二区三区的观看方式| 国产一区在线播放| 一区二区三区四区在线观看视频| 国产精品香蕉国产| 色www亚洲国产张柏芝| 91视频国产一区| 日韩精品视频在线观看免费| 91精品国产高清久久久久久久久| 中文在线资源观看视频网站免费不卡| 国产精品免费久久久久影院| 一级做a爰片久久毛片美女图片| 91麻豆桃色免费看| 国产精品免费一区二区三区都可以| 日韩精品免费在线观看| 国产精品嫩草影院久久久| 久久精品一区中文字幕| 国产精品1区2区在线观看| 久久久久久久影院|