{"id":4549,"date":"2010-10-06T09:58:51","date_gmt":"2010-10-06T07:58:51","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=4549"},"modified":"2010-10-05T16:00:18","modified_gmt":"2010-10-05T14:00:18","slug":"t075-creiamo-form-per-gestire-dataentry-con-uitableview-e-plist","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/t075-creiamo-form-per-gestire-dataentry-con-uitableview-e-plist\/","title":{"rendered":"T#075 \u2013 Creiamo form per gestire DataEntry con UITableView e plist"},"content":{"rendered":"<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form-150x150.png\" alt=\"Esempio form data entry\" title=\"Esempio form data entry\" width=\"150\" height=\"150\" class=\"alignleft size-thumbnail wp-image-4550\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form-150x150.png 150w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form-92x92.png 92w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form-64x64.png 64w\" sizes=\"auto, (max-width: 150px) 100vw, 150px\" \/><\/a> Cocoa Touch \u00e8 un framework completo \u00e8 funzionale ma per quanto riguarda la User Interface, e quindi UIKit, sembra non sia stata posta grande attenzione ad un aspetto: la creazione e gestione di form per il data entry.<br \/>\nCreare form per il data entry \u00e8 un\u2019esigenza comune a molte applicazioni e quando si affronta il problema ci si rende conto che non \u00e8 un\u2019operazione banale come idealmente ci si aspetterebbe. In questo articolo analizzeremo un approccio progettuale che ci permetter\u00e0 di utilizzare una UITableView in modalit\u00e0 \u201cGrouped\u201d per questo scopo. Non \u00e8 ovviamente l\u2019unico modo ma lo ritengo funzionale per varie ragioni: \u00e8 semplice per l\u2019utente, \u00e8 un modello gi\u00e0 usato in varie parti dell\u2019iOS ed \u00e8 pratico per lo sviluppatore.<!--more--><\/p>\n<p>Quello che si vuole ottenere \u00e8 qualcosa di simile alla figura qui sopra, ovvero l\u2019utilizzo della UITableView sulla falsa riga di come viene usata nei Settings di sistema.<\/p>\n<h4>Prerequisiti<\/h4>\n<p>Per non fare un articolo chilometrico do per scontato che il lettore abbia gi\u00e0 una certa dimestichezza con lo sviluppo per iPhone, conosca il sistema MVC e il funzionamento delle UITableView (e relativi delegate e data source).<\/p>\n<p><strong>Iniziamo<\/strong><\/p>\n<p>Alla base di tutto c\u2019\u00e8 la creazione di alcune UITableViewCell custom da inserire nella tabella, avremo vari tipi di cella in funzione del tipo di input che vogliamo acquisire (testo semplice, data, opzioni mutuamente esclusive&#8230;). Ma la sola creazione e aggiunta \u201cby code\u201d delle celle non sopperirebbe al requisito di rapidit\u00e0 che ci siamo posti, per questo introdurremo un meccanismo che utilizza i file plist come struttura della form.<\/p>\n<p>Procediamo per gradi: creiamo innanzitutto una semplice app composta da una UITableView e relativo controller.<\/p>\n<p>Il layout del nostro progetto \u00e8 in questo momento molto semplice come riportato nella figura seguente.<\/p>\n<div id=\"attachment_4551\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/layout-progetto.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-4551\" class=\"size-medium wp-image-4551\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/layout-progetto-300x230.png\" alt=\"layout progetto\" width=\"300\" height=\"230\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/layout-progetto-300x230.png 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/layout-progetto-150x115.png 150w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/layout-progetto.png 334w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p id=\"caption-attachment-4551\" class=\"wp-caption-text\">Layout del progetto<\/p>\n<\/div>\n<p>Dobbiamo configurare la UITableView in modalit\u00e0 \u201cgrouped\u201d da Interface Builder, di default \u00e8 in modalit\u00e0 \u201cplain\u201d che non \u00e8 utile al nostro fine.<\/p>\n<p>Possiamo ora iniziare a creare una semplice cella custom da utilizzare per richiedere in input del testo, l\u2019idea di base \u00e8 realizzare il classico layout con un etichetta a sinistra ed il TextFiled a destra. Creeremo prima una base class dalla quale far derivare tutti i tipi di cella che vogliamo gestire, capiremo man mano il motivo di questa scelta.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface BaseDataEntryCell : UITableViewCell {\r\n\r\n}\r\n\r\n@property (nonatomic, retain) NSString *dataKey;\r\n\r\n-(void) setControlValue:(id)value;\r\n\r\n-(id) getControlValue;\r\n\r\n-(void)postEndEditingNotification;\r\n@end\r\n<\/pre>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@implementation BaseDataEntryCell\r\n\r\n@synthesize dataKey;\r\n\r\n- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {\r\n    if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {\r\n\t\tself.selectionStyle = UITableViewCellSelectionStyleNone;\r\n\t\tself.textLabel.font  = [UIFont boldSystemFontOfSize:14] ;\t\r\n\r\n    }\r\n    return self;\r\n}\r\n\r\n-(void) setControlValue:(id)value\r\n{\r\n\r\n}\r\n\r\n-(id) getControlValue\r\n{\r\n\treturn nil;\r\n}\r\n\r\n-(void)postEndEditingNotification\r\n{\r\n\t[[NSNotificationCenter defaultCenter]\r\n\t postNotificationName:CELL_ENDEDIT_NOTIFICATION_NAME\r\n\t object:[(UITableView *)self.superview indexPathForCell: self]]; \r\n}\r\n\r\n- (void)dealloc {\r\n    [super dealloc];\r\n}\r\n\r\n@end\r\n<\/pre>\n<p>Creata la base class iniziamo ad implementare il primo tipo di cella per il dataentry.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface TextDataEntryCell : BaseDataEntryCell {\r\n}\r\n@property (nonatomic, retain) UITextField *textField;\r\n@end\r\n<\/pre>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@implementation TextDataEntryCell\r\n\r\n@synthesize textField;\r\n\r\n- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {\r\n    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {\r\n\t\t\/\/ Configuro il textfield secondo la necessit\u00e0\r\n\t\tself.textField = [[UITextField alloc] initWithFrame:CGRectZero];\r\n\t\tself.textField.clearsOnBeginEditing = NO;\r\n\t\tself.textField.textAlignment = UITextAlignmentLeft;\r\n\t\tself.textField.returnKeyType = UIReturnKeyDone;\r\n\t\tself.textField.font = [UIFont systemFontOfSize:14];\r\n\t\tself.textField.autocorrectionType = UITextAutocorrectionTypeNo;\r\n\t\tself.textField.autocapitalizationType = UITextAutocapitalizationTypeNone;\r\n\r\n\t\t[self.contentView addSubview:self.textField];\r\n    }\r\n    return self;\r\n}\r\n\r\n- (void)layoutSubviews {\r\n\t[super layoutSubviews];\r\n\r\n\tCGRect labelRect = CGRectMake(self.textLabel.frame.origin.x,\r\n\t\tself.textLabel.frame.origin.y,\r\n\t\tself.contentView.frame.size.width * .35,\r\n\t\tself.textLabel.frame.size.height);\r\n\t[self.textLabel setFrame:labelRect];\r\n\r\n\t\/\/ Rect area del textbox\r\n\tCGRect rect = CGRectMake(self.textLabel.frame.origin.x + self.textLabel.frame.size.width  + LABEL_CONTROL_PADDING,\r\n\t\t\t\t\t\t\t 12.0,\r\n\t\t\t\t\t\t\t self.contentView.frame.size.width-(self.textLabel.frame.size.width + LABEL_CONTROL_PADDING + self.textLabel.frame.origin.x)-RIGHT_PADDING,\r\n\t\t\t\t\t\t\t 25.0);\r\n\r\n\t[textField setFrame:rect];\r\n}\r\n\r\n@end\r\n<\/pre>\n<p>Quel che facciamo \u00e8 semplicemente creare un UITextField e posizionarlo opportunamente, per l\u2019etichetta \u201cricicliamo\u201d la textLabel preesistente nella UITableViewCell.<\/p>\n<p>In questa prima versione la TextDataEntryCell \u00e8 ancora molto semplice e non soddisfa ancora tutte le necessit\u00e0, ma procediamo per gradi e vediamo come usarla nella nostra UITableView.<\/p>\n<p>Come noto le celle vengono configurate nel metodo <em>cellForRowAtIndexPath<\/em> appartenere alla UITableViewDataSource, se volessimo utilizzare la TextDataEntryCell appena creata avremmo un\u2019implementazione tipo quella riportata di seguito.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\r\n\r\n    static NSString *CellIdentifier = @\"Cell\";\r\n\r\n\tBaseDataEntryCell *cell = (BaseDataEntryCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];\r\n\tif (cell == nil) {\r\n\r\n        cell = [[[TextDataEntryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];\r\n    }\r\n\r\n\tcell.textLabel.text = @\"Nome\";\r\n\r\n    return cell;\r\n}\r\n<\/pre>\n<p>Vogliamo per\u00f2 rendere dinamico il tipo di cella che andiamo a creare in funzione del tipo dati da acquisire, al momento abbiamo solo una tipologia di cella per accogliere dati testuali ma nel caso reale avremo celle per le date, e-mail, numero di telefono e cos\u00ec via.<\/p>\n<p>Un aspetto cruciale del ragionamento che stiamo facendo \u00e8 che l\u2019allocazione della cella nel codice precedente potrei anche scriverla cos\u00ec: <\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n[[[NSClassFromString(@\"TextDataEntryCell\") alloc] initWithStyle:....\r\n<\/pre>\n<p>Questo meccanismo \u00e8 molto importante perch\u00e9 ci permette di creare una cella in funzione di una stringa che ne contiene il tipo, questo principio ci permetter\u00e0 di avvantaggiarci dei file plist per la struttura.<\/p>\n<p>Presupponendo di avere gi\u00e0 creato una serie di tipi di celle per testo libero, data ed e-mail il sistema che dovrei usare per strutturare la mia form sarebbe totalmente delegato al codice. Mi troverei a dover scrivere una serie di strutture condizionali (tipo if o switch ) per discriminare riga per riga quale tipo di cella istanziare. Se fosse possibile assolvere allo stesso compito creando un semplice file descrittore avrei un notevole vantaggio sia in termini temporali sia per la manutenibilit\u00e0 del codice prodotto.<\/p>\n<p>I plist (abbreviazione di Property List) sono file che contengono lo stato di oggetti serializzati in XML, hanno il grande vantaggio di essere facilmente gestibili sia da XCode che da tool presenti in Mac OS X. Questo tipo di file \u00e8 molto usato in tutti i sistemi operativi ad oggi prodotti da Apple.<\/p>\n<p>L\u2019idea \u00e8 usare un file plist per creare un array contenente le informazioni necessarie per creare ogni riga, partiamo da un esempio.<\/p>\n<pre lang=\"XML\" line=\"1\" escaped=\"true\">\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!DOCTYPE plist PUBLIC \"-\/\/Apple\/\/DTD PLIST 1.0\/\/EN\" \"http:\/\/www.apple.com\/DTDs\/PropertyList-1.0.dtd\">\r\n<plist version=\"1.0\">\r\n\t<array>\r\n\t\t<dict>\r\n\t\t\t<key>Label<\/key>\r\n\t\t\t<string>Nome<\/string>\r\n\t\t\t<key>CellType<\/key>\r\n\t\t\t<string>TextDataEntryCell<\/string>\r\n\t\t<\/dict>\r\n\t\t<dict>\r\n\t\t\t<key>Label<\/key>\r\n\t\t\t<string>Cognome<\/string>\r\n\t\t\t<key>CellType<\/key>\r\n\t\t\t<string>TextDataEntryCell<\/string>\r\n\t\t<\/dict>\r\n\t\t<dict>\r\n\t\t\t<key>Label<\/key>\r\n\t\t\t<string>Email<\/string>\r\n\t\t\t<key>CellType<\/key>\r\n\t\t\t<string>TextDataEntryCell<\/string>\r\n\t\t<\/dict>\r\n\t\t<dict>\r\n\t\t\t<key>Label<\/key>\r\n\t\t\t<string>Sesso<\/string>\r\n\t\t\t<key>CellType<\/key>\r\n\t\t\t<string>TextDataEntryCell<\/string>\r\n\t\t<\/dict>\r\n\t\t<dict>\r\n\t\t\t<key>Label<\/key>\r\n\t\t\t<string>Data di nascita<\/string>\r\n\t\t\t<key>CellType<\/key>\r\n\t\t\t<string>TextDataEntryCell<\/string>\r\n\t\t<\/dict>\r\n\t<\/array>\r\n<\/plist>\r\n<\/pre>\n<p>Con l\u2019XML sopra sto strutturando una UITableView di 5 righe, per ogni riga uso un Dictionary (<dict><\/dict>) in modo da configurare un certo numero di propriet\u00e0 per la creazione della cella. Nell\u2019esempio ogni cella ha due propriet\u00e0 : Label che definisce l\u2019etichetta e CellType che definisce il tipo della cella stessa.<\/p>\n<p>Creato il file bisogna deserializzarlo in un NSArray, faremo questo nel classico metodo viewDidLoad come segure:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (void)viewDidLoad {\r\n    [super viewDidLoad];\r\n\r\n\tNSString *plisStructure = [[NSBundle mainBundle] pathForResource:@\"table-structure.plist\" ofType:nil];\r\n\r\n    tableStructure = [NSArray arrayWithContentsOfFile:plisStructure];\r\n\r\n}\r\n<\/pre>\n<p>Letto il file andiamo a vedere come modificare cellForRowAtIndexPath in modo da usare l\u2019array tableStructure.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\r\n    static NSString *CellIdentifier = @\"Cell\";\r\n\r\n    NSDictionary *cellData = [tableStructure objectAtIndex:indexPath.row];\r\n    NSString *cellType = [cellData objectForKey:@\"CellType\"];\r\n\r\n\tBaseDataEntryCell *cell = (BaseDataEntryCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];\r\n\tif (cell == nil) {\r\n\r\n        cell = [[[NSClassFromString(cellType) alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];\r\n    }\r\n\r\n\tcell.textLabel.text = [cellData objectForKey:@\"Label\"];\t\r\n\r\n    return cell;\r\n}\r\n<\/pre>\n<p>Come visibile dal codice l\u2019idea \u00e8 molto semplice: estraggo dall\u2019array il Dictionary con le varie propriet\u00e0 e le utilizzo per costruire la cella. La propriet\u00e0 principale \u00e8 CellType che contiene il nome della classe da istanziare ma abbiamo anche Label che definisce l\u2019etichetta per la cella. Il primo passo \u00e8 fatto: siamo in grado di costruire una UITableView semplicemente strutturando un file plist e creando le varie celle in modo dichiarativo.<\/p>\n<p>Arrivati a questo punto siamo in grado di creare la user interface che desideriamo (ammesso ovviamente di costruirsi tutte le tipologie di celle del caso) ma non abbiamo visto come leggere i dati inseriti dall\u2019utente.<\/p>\n<p>Anche in questo caso si possono adottare vari sistemi: uno potrebbe essere quello di definire un delegate conforme ad un nostro protocollo in modo che la cella invochi un metodo quando l\u2019editing \u00e8 completo. Ho preferito adottare una Notification allo stesso scopo perch\u00e9 la sua natura disaccoppiata permette un maggiore dinamismo (anche se pone la necessit\u00e0 di documentazione per l\u2019utilizzatore).<\/p>\n<p>Tornando al sorgente della base class delle celle notiamo un metodo che fin ora abbiamo tralasciato:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(void)postEndEditingNotification\r\n{\r\n\t[[NSNotificationCenter defaultCenter]\r\n\t postNotificationName:CELL_ENDEDIT_NOTIFICATION_NAME\r\n\t object:[(UITableView *)self.superview indexPathForCell: self]]; }\r\n<\/pre>\n<p>Il metodo invia semplicemente una notifica, l\u2019idea \u00e8 far si che quando l\u2019editing di una cella \u00e8 completo questa invii una notifica custom in modo tale che gli observer possano prendere delle azioni.<\/p>\n<p>L\u2019invio della notifica \u00e8 ovviamente diverso a seconda del tipo di cella che stiamo implementando, la TextDataEntryCell dovr\u00e0 inviarla nel momento in cui l\u2019editing del TextField \u00e8 finito. Se avessimo una cella che gestisce il classico check a due stati (selezionato\/non selezionato) l\u2019invio andrebbe fatto al cambio di stato e cos\u00ec via per ogni tipologia.<\/p>\n<p>Concentrandoci sulla nostra TextDataEntryCell sfrutteremo la possibilit\u00e0 dell\u2019UITextField di comunicare l\u2019evento di fine editing mediante il classico pattern del delegate.<\/p>\n<p>Per prima cosa conformiamo la classe al protocollo UITextFieldDelegate modificando la definizione dell\u2019interfaccia in questo modo <\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface TextDataEntryCell : BaseDataEntryCell <UITextFieldDelegate>\r\n<\/pre>\n<p>A livello di implementazione dovremo impostare il delegate del UITextField (self.textField.delegate = self;) ed \u201cintercettare\u201d l\u2019evento di fine editing per postare la nostra notifica.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#pragma mark UITextFieldDelegate\r\n\r\n- (void)textFieldDidEndEditing:(UITextField *)txtField\r\n{\r\n\t[self postEndEditingNotification];\r\n}\r\n<\/pre>\n<p>Resta un ultimo sforzo per arrivare ad una prima implementazione funzionante: intercettare la notifica nel controller principale e gestire il dato. Per intercettare una notifica dobbiamo registrarci come observer, aggiungeremo il pezzo di codice seguente nel metodo viewDidLoad del controller della UITableView.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n         [[NSNotificationCenter defaultCenter]\r\n\t addObserver:self\r\n\t selector:@selector(cellControlDidEndEditing:)\r\n\t name:CELL_ENDEDIT_NOTIFICATION_NAME\r\n\t object:nil];\r\n<\/pre>\n<p>Senza entrare nel dettaglio del funzionamento delle notifiche, per il quale esistono ottimi articoli, diciamo solamente che \u201cagganciamo\u201d un metodo chiamato cellControlDieEndEditing alla ricezione della notifica.<\/p>\n<p>In cellControlDidEndEditing \u00e8 dove andremo a gestire la ricezione del dato inserito, quel che viene fatto in questo metodo dipende ovviamente dal contesto dell\u2019applicazione potremmo dover scrivere il dato in un file, inviarlo ad un servizio web o qualsiasi altra attivit\u00e0. Ai fini dell\u2019esempio lo logghiamo semplicemente nella console di debug.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(void)cellControlDidEndEditing:(NSNotification *)notification\r\n{\r\n\tNSIndexPath *cellIndex = (NSIndexPath *)[notification object];\r\n\tBaseDataEntryCell *cell = (BaseDataEntryCell *)[self.tableView cellForRowAtIndexPath:cellIndex];\r\n\tif(cell != nil)\r\n\t{\r\n\t\tNSLog(@\"L'utente ha digitato %@\",  [cell getControlValue]);\r\n\t}\r\n}\r\n<\/pre>\n<p>Ai pi\u00f9 attenti sar\u00e0 saltato all\u2019occhio un problema: come faccio a discriminare quale cella mi sta notificando la fine dell\u2019editing? Supponiamo di avere una semplice form composta da 4 celle : nome, cognome, indirizzo e citt\u00e0. Ogni cella scatener\u00e0 la notifica di \u201cend editing\u201d ma in questo momento non ho modo di capire quale cella l\u2019ha fatto. Se torniamo ad analizzare la classe base delle celle noteremo una property che fin ora abbiamo trascurato : dataKey. Questa property esiste proprio per gestire il nostro problema, per utilizzarla dobbiamo innanzitutto riprendere il nostro file plist e aggiungere ad ogni riga un property DataKey valorizzata con il discriminante che vogliamo usare.<\/p>\n<pre lang=\"xml\" line=\"1\" escaped=\"true\">\r\n<dict>\r\n\t<key>DataKey<\/key>\r\n\t<string>firstName<\/string>\r\n\r\n\t<key>Label<\/key>\r\n\t<string>Nome<\/string>\r\n\t<key>CellType<\/key>\r\n\t<string>TextDataEntryCell<\/string>\r\n<\/dict>\r\n<\/pre>\n<p>Il metodo cellForRowAtIndex dovr\u00e0 a questo punto leggere dal plist anche la DataKey ed impostarla nell\u2019omonima property della cella, nel seguente modo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\r\n    \/\/ Nota: non va usato un discriminante unico perch\u00e9 abbiamo (potenzialmente) pi\u00f9 tipi di cella\r\n    static NSString *CellIdentifier = @\"Cell\";\r\n\r\n    NSDictionary *cellData = [tableStructure objectAtIndex:indexPath.row];\r\n\tNSString *dataKey = [cellData objectForKey:@\"DataKey\"];\r\n\tNSString *cellType = [cellData objectForKey:@\"CellType\"];\r\n\r\n\tBaseDataEntryCell *cell = (BaseDataEntryCell *)[tableView dequeueReusableCellWithIdentifier:cellType];\r\n\tif (cell == nil) {\r\n\r\n        cell = [[[NSClassFromString(cellType) alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType] autorelease];\r\n    }\r\n\r\n\t\/\/ Impostiamo la datakey della cella\r\n\tcell.dataKey = dataKey;\r\n\tcell.textLabel.text = [cellData objectForKey:@\"Label\"];\r\n\treturn cell;\r\n}\r\n<\/pre>\n<p>A questo punto il giro \u00e8 completo, tornado al nostro cellControlDidEndEditing siamo in grado di dedurre il contesto dell\u2019editing ricevuto dalla dataKey della cella.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(void)cellControlDidEndEditing:(NSNotification *)notification\r\n{\r\n\tNSIndexPath *cellIndex = (NSIndexPath *)[notification object];\r\n\tBaseDataEntryCell *cell = (BaseDataEntryCell *)[self.tableView cellForRowAtIndexPath:cellIndex];\r\n\tif(cell != nil)\r\n\t{\r\n\t\tNSLog(@\"L'utente ha digitato %@ per la datakey %@\",  [cell getControlValue], cell.);\r\n\t}\r\n}\r\n<\/pre>\n<p>Abbiamo realizzato un sistema per creare form in modo quasi visuale, il sistema ha ampi spazi di miglioramento ma gi\u00e0 in questa versione permette di gestire le casistiche tipiche di data entry. <\/p>\n<p>Lascio al lettore l\u2019onere (e l\u2019onore) di costruire tutti i tipi di cella necessari in un\u2019applicazione reale.<\/p>\n<p>In figura uno screenshot del lavoro ultimato:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form.png\" alt=\"Esempio form data entry\" title=\"Esempio form data entry\" width=\"324\" height=\"481\" class=\"aligncenter size-full wp-image-4550\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form.png 324w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form-202x300.png 202w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/DataEntry-form-101x150.png 101w\" sizes=\"auto, (max-width: 324px) 100vw, 324px\" \/><\/a><br \/>\n<\/center><\/p>\n<p style=\"text-align: center;\"><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/TableViewDataEntry.zip\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/05\/download_icon.png\" alt=\"\" width=\"33\" height=\"40\" align=\"middle\" \/><\/a> Se avete problemi con il tutorial, <a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/10\/TableViewDataEntry.zip\">questo \u00e8 il nostro file di progetto.<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cocoa Touch \u00e8 un framework completo \u00e8 funzionale ma per quanto riguarda la User Interface, e quindi&#8230;<\/p>\n","protected":false},"author":175,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[429,431,430,133,224],"class_list":["post-4549","post","type-post","status-publish","format-standard","hentry","category-tutorial-pratici","tag-fabrizio-guglielmino","tag-form-iphone-ipad","tag-plist","tag-uitable","tag-xml-iphone"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/4549","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/175"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=4549"}],"version-history":[{"count":46,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/4549\/revisions"}],"predecessor-version":[{"id":4614,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/4549\/revisions\/4614"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=4549"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=4549"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=4549"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}