Custom UITableViewCell

Le tabelle di default sono orribili. Mai e dico mai presentare un'applicazione con una tabella classica (a meno che non sia la tabella che esce fuori da un campo di ricerca). Facebook ha la sua tabella per le news, Twitter lo stesso, Whatsapp sta tentando di eliminare quella tabella con la lista delle chat... Insomma, [...]

Torna a: Corso creare applicazioni per iOS con il linguaggio Swift > TableView, TabBar e CollectionView
  • Alessandro Scozzaro

    Ciao peppe,
    scusa il disturbo ma ho questa lezione ancora bloccata…mi dice che il quiz è ancora da correggere.
    Grazie mille

    • Ciao Alessandro,
      Ci sono dei problemi con il sistema di risoluzione dei quiz. Vai avanti con l’apprendimento e, non appena lo risolvo, correggerò i quiz di questa lezione.

      Gli altri quiz saranno messi tutti nell’ultima lezione del modulo.

      Scusa per il disagio!

  • Arcy

    Ciao Beppe! ottima guida. Nell’animazione della cella, cassetto a comparsa, ho notato che per un attimo le scritte del cassetto si sovrappongono con quelle della cella. Forse perché il background è trasparente di default? se mettessimo un background bianco non succederebbe?

    • Ciao Arcy,
      Grazie mille per il complimento!

      Il problema dipende dalla renderizzazione della cella quando avviene il begin ed end update. Dato che alla stack non abbiamo messo un vincolo con l’elemento superiore ma solo con la bottom della cella, questa, quando viene ridisegnata viene di default fatta scendere dalla coordinata 0,0.
      Si può risolvere animando l’alpha da 0 a 1 durante l’invocazione dei due metodi di update della riga.

  • Arcy

    Ciao Beppe! come da tuo suggerimento pongo qui il quesito che può essere utile a tutti.
    Le tabelle vengono spesso usate per i form. Alcuni suggeriscono di usare le tabelle con celle statiche. QUest’ultime però necessitano di un TableViewController che a livello stilistico da poche chances :-D
    Quello che vorrei ottenere e una cosa come l’immagine allegata dove la tabella è un form che contiene celle di varia natura e per lo più e stilizzata in modo tale da essere staccata dal bordo e leggermente arrotondata. In chat mi hai detto che questo è possibile NON usando le celle statiche, ma creando celle dinamiche e gestendo la table come oggetto IBOutlet.
    Come posso personalizzare le celle di una tabella dinamica se queste sono di varia natura? e come posso leggerne i dati nel caso che una cella avesse una TextField o una label?
    Grazie mille!

    • Ciao Arcy,

      Veniamo subito al nodo della questione. Forse mi ero spiegato male in chat. Anche con una Static Cell puoi fare esattamente le stesse cose di una dynamic. Il problema è che il frame lo devi modificare da codice se non vuoi fargli riempire tutta la porzione di schermo. Quindi è sicuramente più veloce utilizzare una Dynamic.

      Per l’arrotondamento dei bordi basta ridimensionarla in modo che abbia una distanza dai lati del display (vincoli di distanza o equal height * 0.9) e modificare il layer.cornerRadius:

      https://www.xcoding.it/wp-content/uploads/2016/02/Schermata-2016-02-10-alle-18.07.09.png

      Fin qui credo non dovrebbero esserci grossi problemi.

      Ipotizziamo adesso che la prima cella sia quella che deve prendere dei dati dall’utente. Magari con una textField come fanno quelli dell’app.
      Quindi bisogna aggiungere una TextField e di conseguenza una CustomTableViewCell per gestire la IBAction associata.

      A questo dovresti avere una classe per la gestione della cella con una IBAction con evento DidEndOnExit pronto a gestire il testo inviato.

      Come recupero il testo dalla textField?

      Semplicemente non lo recuperi ma è la textField ad inviarlo ad un oggetto. Oggetto di che tipo?

      Facciamo un passo indietro. Questa pagina sta creando qualcosa che ha più di una singola proprietà. Presumibilmente, quindi, non viene gestito da un singolo dato ma da un oggetto dove all’interno ci sono N proprietà. Il nostro è lo stesso identico caso.

      L’oggetto che deve essere riempito con i dati della tabella sarà sicuramente un singleton. Perché singleton? perché una volta istanziato dal ViewController, o da qualche altra parte, è facilmente recuperabili dall’interno della CustomTableViewCell.

      A questo punto ti linko il progetto con dei commenti su questa parte così mi viene più semplice da spiegare ;)

      LINK DOWNLOAD: http://cl.ly/062z2K3f0Q0l

      Al solito, se fai delle modifiche al progetto e vuoi condividerlo con gli altri, posta sul forum. Se invece ci sono problemi, chiedi pure. Il progetto l’ho commentato nella speranza che sia più chiaro da capire :P

      • Arcy

        Perfetto Beppe, mi pare di aver capito tutto. Possiamo solo chiudere il cerchio di questo argomento chiedendoti se mi spieghi, nel caso non volessi usare le celle dinamiche, come posso ridimensionare l’intera tabella statica? tieni conto che va usato direttamente un UITableViewContainer. Ho provato a fare self.tableView.frame.size.height = 100 per esempio, ma la tabella non si modifica…se vediamo anche questa cosa preparo tutto con degli esempi e mettiamo sul forum (o lo usate come tutorial ;-) così abbiamo entrambe le alternative per personalizzare la tabella.. ;-)

  • Gabriele Brescancin

    ciao Beppe, ci sono dei problemi con il quiz di fine lezione. non funziona la correzione. Grazie. ciao!

    • Ciao Gabriele,
      A breve li riattivo che ci sono stati dei problemi con il sistema.

  • Salvatore G

    Ciao Peppe, sono sempre alle prese col mio esercizio “prenotazione di pizza e bevanda”…e volevo quindi personalizzare la tableViewCell.
    Ho capito che x ottenere il valore da una tableViewCell devo chiamare il metodo/funzione DidSelectRowAtIndexPath ….ottenendo cosi il valore dell’ indice selezionato… e fin qui ci siamo….

    Ma cosa succede se inserisco un bottone all’ interno della mia custom tableViewCell ?
    come faccio ad inserire la pizza selezionata dalla suddetta tabella e metterla in un array usando il bottone creato da me nella mia custom tableViewCell?

    ho provato a documentarmi…ed ho fatto cosi.

    nel metodo cellForRowAtIndexPath ho aggiunto:

    cell.addPizza_button_click.tag = indexPath.row // la riga scelta
    ps. addPizza_button_click è il riferimento della IBOutlet mia customTableViewCell

    cell.addPizza_button_click.addTarget(self, action: “Aggiungi”, forControlEvents: UIControlEvents.TouchUpInside)

    il target si riferisce ad una funzione action, che io ho chiamato Aggiungi….quindi ho proseguito così:

    func Aggiungi( sender: UIButton) {

    let buttonRow = sender.tag // grazie al TAG ottengo l’equivalente dell’indexPath della tableView

    let selectRiga = self.ordine[buttonRow].item // preparo l’indice della variabile ordine che contiene le pizze di tutti che elementi in tabella.
    PS: il .item contiene la pizza corrispondente

    self.selezione.append(selectRiga) // finalmente aggiungo la riga alla variabile delle pizze selezionate

    print(“hai salvato in selezione:(selezione)”) // verifico in consol che sia stato aggiunto l’ elemento dal bottone
    }
    xcode mi solleva una eccezione ……..libc++abi.dylib: terminating with uncaught exception of type NSException (lldb)

    Aiuto, sto diventando scemo…
    Grazie.

    • Ciao Salvatore,
      Mi servono degli screen e vorrei capire a cosa servirebbe il bottone dentro la cella. Se serve a selezionare la cella o comunque la pizza, perché non utilizzi la classica selezione delle celle?

      In alternativa, se proprio vuoi utilizzare il bottone, nella custom class della UITableViewCell che stai utilizzando puoi inserire la IBAction del bottone. Da lì, utilizzando un oggetto Singleton (un qualcosa come Prenotazione o Selezione) inserisci il valore della pizza in questo oggetto per poi ripescarlo dal tuo UIViewController. Se non vuoi usare il Singleton puoi sempre optare per il Delegate tra UIViewController (o TableViewController dov’è la tabella) e la UITableViewCell

      Fammi sapere,
      Giuseppe

  • Salvatore G

    il bottone + inserito nella TableVIewCell mi serve per inserire in contenuto della cella toccata in un array di pizze….che successivamente invierò ad un altro controller… è questo che cerco di capire.

    ho appreso dal corso come farlo in maniera classica… e uso il metodo DidSelectRowAtIndexPath già per l’apertura e chiusura a portafoglio della cella, per poter visualizzare gli ingredienti della pizza.
    mi serve un bottone nella cella che aggiunga effettivamente il contenuto della cella in un array di elementi selezionati.

    per me è fondamentale capire come aggiungere la pizza dal bottone.
    ho provato anche ad inserire la funzione DidSelectRowAtIndexPath all’ interno del bottone…ma niente….
    sto fondendo….
    Aiuto perfavore….capito questo sarò felicissimo …..
    Peppe, Grazie per la tua disponibilità.

    una domanda che non centra niente con quello scritto fino ad ora…hai in progetto di creare un corso/pacchetto intermedio per noi online?
    farai un workshop a Catania?
    Questo corso mi ha entusiasmato, voglio approfondire.

    • Ok adesso è tutto più chiaro!

      Ti preparo un progetto d’esempio che usa il pattern Delegate per far comunicare il bottone con il viewcontroller che gestisce l’array dei selezionati

    • Per quanto riguarda i workshop il prossimo che sto organizzando sarà a Roma.

      Per il pacchetto intermedio sto preparando dei video dove spiego la realizzazione di un’applicazione stile ricettario online condivisibile e gestito dagli utenti (una sorta di social network)

    • Qui trovi il download del progetto d’esempio https://dl.dropboxusercontent.com/u/34966085/testTabellaEbottone.zip

  • Salvatore G

    Grazie Peppe x la celere risposta e la tua disponibilità.
    grazie 1000 :)

    • Non c’è di che Salvo,
      Fammi sapere se va bene oppure no ;)

      Giuseppe Sapienza
      xCoding.it | Sviluppa, Crea e codifica i tuoi sogni!

  • Salvatore G

    non ci sarei sarei mai arrivato senza il tuo aiuto.
    il delegante è il protocollo MyTableViewCellDelegate, contenuto nel MyTableViewCell.swift…giusto ?
    è abbastanza chiaro da leggere…sono sollevato….ho imparato una cosa nuova e preziosa.
    Grazie 1000 Peppe.

  • The Viper

    mi scuso per l’intromissione , ma il sito risulta offline da stamane e non riesco nemmeno a inviare il messaggio per e mail ;) https://uploads.disquscdn.com/images/b2353f902dc103ee2da5da2255b5319109ea40aac3b033e8acf1dc38120177ca.png

    • Ciao Viper,
      l’host sta avendo alcuni problemi relativi ad attacchi ddos e spero a breve dovrebbero risolvere.

  • Lollo82

    Buongiorno Peppe. Sto provando a eserguire l’esercizio che proponi sulle Prototype cell multiple.
    Funziona sino a che non provo ad inserire i dovuti elementi nelle label. Ovvero per ogni oggetto assegna la corretta prototype. Il downcast funziona perchè ho provato a printare con cell is… Ma quando provo ad assegnare il nome dell’animale o della persona alla rispettiva label mi dice che il tipo UITableViewCell non ha tale membro.
    Eppure ho creato tutte le IBoutlet. Infatti se invece di cell = … Scrivo let cell = … me li riconosce, ma ovviamente non funziona perchè si blocca nel return.
    TI posto uno screen di quel punto magari dimentico io qualcosa. https://uploads.disquscdn.com/images/282cd4332623052a1620cb258f0cf6b6239fc0647e5c4612215144ea5c9720c1.png

    • Ciao Lorenzo,

      L’errore deriva dall’utilizzo della variabile cell come UITableViewCell. Infatti il casting che esegui all’interno degli if, nella custom class richiesta, non trasforma il tipo originario della variabile (che rimarrà sempre una UITableViewCell).
      Però, dato che all’interno della variabile cell metterai una subclass della UITableViewCell (la tua custom class), puoi castarla a runtime.

      Cioè potrai fare:
      (cell as! nomeCustomClass).nomeProprietà = valore

      nel tuo caso:
      (cell as! myTableViewCell).name_label.text = persona.name

      Lo stesso principio si applica per l’altro if.

      Alla prossima e buona programmazione,
      Giuseppe Sapienza

  • Niccolò Bonardi

    Ciao Beppe,
    ho un problema con il begin e endUpdates…in pratica se li uso come fai te non ci sono problemi ma ho provato a inserirli in una funzione che ho creato io e mi scombinano l’ordine di tutte le celle! In pratica fanno un mega casino…come mai?
    A me servirebbe variare l’altezza di alcune celle cliccando su un button e quindi nel codice di quel button avevo inserito appunto begin e endUpdates..
    Ci sono modi alternativi?

    • Ciao Niccolò, che intendi per scombinano l’ordine? non mi è mai capitato come problema.

      In caso condividimi il progetto o il codice così vedo subito.

      A presto,
      Giuseppe Sapienza | xCoding.it

      • Niccolò Bonardi

        In pratica io ho una tableView e ho messo da codice un button all’interno delle celle. A questo button ho fatto adTarget e come action ho aggiunto una funzione che voglio utilizzare per espandere la mia cella in altezza (in questo caso solo per le prime 2 celle):

        func changeRowHeight(sender: UIButton) {
        if sender.tag == 0 || sender.tag == 1 {

        self.controlloCells = sender.tag //è un Int opzionale che uso poi per controllare di cambiare altezza solo alle prime 2 celle

        self.myTable.beginUpdates()
        self.myTable.endUpdates()

        }
        }

        per quanto riguarda il metodo heightForRow invece ho:

        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

        if indexPath.row == self.controlloCells {

        self.controlloCells = nil
        return 200

        } else {

        return 80
        }

        Quando però clicco sui button nella prima o nella seconda cella mi sfasa l’ordine degli elementi, nel senso che l’immagine della terza cella va al posto della prima e viceversa o che si scambiano i nomi le varie celle o tutti gli altri elementi si ridistribuiscono in ordine diverso rispetto a quello originale..

Start typing and press Enter to search