2014-10-06 2 views
11

Ho cercato di implementare una funzione nella mia app in modo che quando un utente tocca una cella nella mia vista tabella, la cella si espande verso il basso per rivelare le note. Ho trovato molti esempi di questo in Objective-C, ma devo ancora trovarne uno per Swift.Espandi cella quando viene toccata in Swift

Questo esempio sembra perfetto: Accordion table cell - How to dynamically expand/contract uitableviewcell?

ho avuto un tentativo di tradurre a Swift:

var selectedRowIndex = NSIndexPath() 
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 
    selectedRowIndex = indexPath 
    tableView.beginUpdates() 
    tableView.endUpdates() 
} 

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { 
    if selectedRowIndex == selectedRowIndex.row && indexPath.row == selectedRowIndex.row { 
     return 100 
    } 
    return 70 
} 

Tuttavia questo sembra solo per mandare in crash l'applicazione.

Qualche idea?

Edit:

Ecco il mio codice cellForRowAtIndexPath:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    var cell:CustomTransactionTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTransactionTableViewCell 

    cell.selectionStyle = UITableViewCellSelectionStyle.None 

    if tableView == self.searchDisplayController?.searchResultsTableView { 
     cell.paymentNameLabel.text = (searchResults.objectAtIndex(indexPath.row)) as? String 
     //println(searchResults.objectAtIndex(indexPath.row)) 
     var indexValue = names.indexOfObject(searchResults.objectAtIndex(indexPath.row)) 
     cell.costLabel.text = (values.objectAtIndex(indexValue)) as? String 
     cell.dateLabel.text = (dates.objectAtIndex(indexValue)) as? String 

     if images.objectAtIndex(indexValue) as NSObject == 0 { 
      cell.paymentArrowImage.hidden = false 
      cell.creditArrowImage.hidden = true 
     } else if images.objectAtIndex(indexValue) as NSObject == 1 { 
      cell.creditArrowImage.hidden = false 
      cell.paymentArrowImage.hidden = true 
     } 
    } else { 
     cell.paymentNameLabel.text = (names.objectAtIndex(indexPath.row)) as? String 
     cell.costLabel.text = (values.objectAtIndex(indexPath.row)) as? String 
     cell.dateLabel.text = (dates.objectAtIndex(indexPath.row)) as? String 

     if images.objectAtIndex(indexPath.row) as NSObject == 0 { 
      cell.paymentArrowImage.hidden = false 
      cell.creditArrowImage.hidden = true 
     } else if images.objectAtIndex(indexPath.row) as NSObject == 1 { 
      cell.creditArrowImage.hidden = false 
      cell.paymentArrowImage.hidden = true 
     } 
    } 
    return cell 
} 

Qui ci sono le impostazioni di uscita:

enter image description here

+2

Questa parte non ha senso per me: 'se selectedRowIndex == selectedRowIndex. fila'? – zisoft

+0

Provare a creare 'NSIndexPath' bit differentyl' var selectedRow = NSIndexPath (forRow: 0, inSection: 0) ' – Kirsteins

+0

@zisoft Bene l'esempio che stavo basando su off ha' selectedRowIndex && indexPath.row == selectedRowIndex.row' ma se Provo ad usarlo per ottenere l'errore: Digitare NSIndexPath non è conforme al protocollo 'Tipo booleano' – user3746428

risposta

4

Il primo confronto nella vostra istruzione if non può mai essere vero perché stai confrontando un IndexPath con un numero intero. Dovresti anche inizializzare la variabile selectedRowIndex con un valore di riga che non può essere nella tabella, come -1, quindi nulla verrà espanso quando la tabella viene caricata per la prima volta.

var selectedRowIndex: NSIndexPath = NSIndexPath(forRow: -1, inSection: 0) 

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { 
    if indexPath.row == selectedRowIndex.row { 
     return 100 
    } 
    return 70 
} 
+0

Grazie per il tuo aiuto e la spiegazione. La cella sembra non espandersi se toccata. Aggiornerò la mia domanda con il mio codice cellForRowAtIndex in modo da poter vedere se c'è un problema ovvio. – user3746428

+0

@ user3746428, non vedo nulla nel tuo metodo cellForRowAtIndexPath che farebbe sì che la cella non si espanda. Metti un log in appena sopra "return 100" e vedi se quella clausola viene mai eseguita. Altrimenti, inserisci un log in didSelectRowAtIndexPath per assicurarti che venga eseguito. – rdelmar

+0

Stranamente, il codice esegue e funziona se eseguo una ricerca e poi tocchi uno dei risultati, ma non viene eseguito se tocchio semplicemente una delle celle normalmente. – user3746428

14

Mi ci sono volute un bel po 'di ore per farlo funzionare. Di seguito è come ho risolto.

PS: il problema con il codice di @ rdelmar è che presume che tu abbia solo uno section nella tabella, quindi sta solo confrontando lo indexPath.row. Se hai più di una sezione (o se vuoi già rendere conto per espandere il codice più tardi) dovresti confrontare l'intero indice, in questo modo:

1) Hai bisogno di una variabile per dire quale riga è selezionata. Vedo che l'hai già fatto, ma dovrai restituire la variabile a uno stato "nulla selezionato" consistente (per quando l'utente chiude tutte le celle). Credo che il modo migliore per farlo è tramite un optional:

var selectedIndexPath: NSIndexPath? = nil 

2) È necessario identificare quando l'utente seleziona una cella. didSelectRowAtIndexPath è la scelta più ovvia. È necessario tenere conto di tre possibili esiti:

  1. l'utente sta toccando una cella e un'altra cella si espande
  2. l'utente sta toccando una cella e nessuna cella è espanso
  3. l'utente sta toccando una cella che è già espanso

per ciascun caso controlliamo se il selectedIndexPath è uguale a zero (nessuna cella espansa), pari alla indexPath della fila filettato (stessa cella già espanso) o diverso dal indexPath (un'altra cella è espansa). Sistemiamo di conseguenza il parametro selezionatoIndexPath. Questa variabile verrà utilizzata per verificare la riga giusta Altezza per ogni riga. Hai menzionato nei commenti che didSelectRowAtIndexPath "non sembra essere chiamato". Stai usando un println() e controlli la console per vedere se è stata chiamata? Ne ho incluso uno nel codice qui sotto.

PS: questo non funziona utilizzando tableView.rowHeight perché, a quanto pare, rowHeight viene controllato una sola volta da Swift prima di aggiornare TUTTE le righe in tableView.

Ultimo ma non meno importante, io uso reloadRowsAtIndexPath per ricaricare solo le righe necessarie. Ma, anche perché so che ridisegnerà la tabella, ritrasmetterà quando necessario e anzi animerà le modifiche. Notare la [indexPath] è tra parentesi poiché questo metodo richiede un array di NSIndexPath:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 
     println("didSelectRowAtIndexPath was called") 
     var cell = tableView.cellForRowAtIndexPath(indexPath) as! MyCustomTableViewCell 
     switch selectedIndexPath { 
     case nil: 
      selectedIndexPath = indexPath 
     default: 
      if selectedIndexPath! == indexPath { 
       selectedIndexPath = nil 
      } else { 
       selectedIndexPath = indexPath 
      } 
     } 
     tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) 
} 

3) Terzo e ultimo passo, Swift deve sapere quando passare ogni valore alla altezza della cella. Facciamo un controllo simile qui, con se/else. So che si può reso il codice molto più breve, ma sto scrivendo tutto in modo che altre persone possano capire facilmente, troppo:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { 
     let smallHeight: CGFloat = 70.0 
     let expandedHeight: CGFloat = 100.0 
     let ip = indexPath 
     if selectedIndexPath != nil { 
      if ip == selectedIndexPath! { 
       return expandedHeight 
      } else { 
       return smallHeight 
      } 
     } else { 
      return smallHeight 
     } 
    } 

Ora, alcune note sul codice che potrebbe essere la causa dei vostri problemi, se quanto sopra non lo risolve:

var cell:CustomTransactionTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTransactionTableViewCell 

non so se questo è il problema, ma self non dovrebbe essere necessario, dal momento che probabilmente stai mettendo questo codice nel tuo (Custom) TableViewController. Inoltre, invece di specificare il tipo di variabile, puoi fidarti dell'inferenza di Swift se forzatamente fai il cast forzato della cella dalla dequota. Quella forza casting è il as! nel seguente codice:

var cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier" forIndexPath: indexPath) as! CustomTransactionTableViewCell 

Tuttavia, è assolutamente necessario impostare tale identificatore. Vai allo storyboard, seleziona la tabellaView che contiene la cella di cui hai bisogno, per la sottoclasse di TableViewCell di cui hai bisogno (probabilmente CustomTransactionTableViewCell, nel tuo caso). Ora seleziona la cella in TableView (verifica di aver selezionato l'elemento giusto. È preferibile aprire la struttura del documento tramite Editor> Mostra struttura del documento). Con la cella selezionata, vai a Impostazioni Attributi a destra e digita il nome Identifier.

È inoltre possibile provare a commentare il cell.selectionStyle = UITableViewCellSelectionStyle.None per verificare se questo blocca la selezione in alcun modo (in questo modo le celle cambieranno colore quando toccate se vengono selezionate).

Buona fortuna, amico.

+0

Questo non causare problemi con le celle che utilizzano tableView.rowHeight = UITableViewAutomaticDimension? – cyril

+0

I * credo * che rowHeight sovrascrive l'UITableViewAutomaticDimension, ma non ne sono sicuro. Ma la mia risposta sopra presuppone che tu abbia altezze fisse per le righe (sia negli stati espanso che in quello contratto), quindi l'UITableViewAutomaticDimension non dovrebbe essere usato qui. –

+1

Ho provato il tuo metodo con UITableViewAutomaticDimension e il ritorno alla dimensione normale funziona bene ma non l'espansione. Ti aggiornerò quando lo scoprirò – cyril

0

Dopo aver ottenuto il percorso indice in didSelectRowAtIndexPath basta ricaricare il cellulare con seguente metodo reloadCellsAtIndexpath e in heightForRowAtIndexPathMethod controllo seguente condizione

if selectedIndexPath != nil && selectedIndexPath == indexPath {  
     return yourExpandedCellHieght 
} 
1

suggerisco così lving questo con vincolo di layout altezza modyfing

class ExpandableCell: UITableViewCell { 

@IBOutlet weak var img: UIImageView! 


@IBOutlet weak var imgHeightConstraint: NSLayoutConstraint! 


var isExpanded:Bool = false 
    { 
    didSet 
    { 
     if !isExpanded { 
      self.imgHeightConstraint.constant = 0.0 

     } else { 
      self.imgHeightConstraint.constant = 128.0 
     } 
    } 
} 

} 

Poi, all'interno ViewController:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 

@IBOutlet weak var tableView: UITableView! 
override func viewDidLoad() { 
    super.viewDidLoad() 
    self.tableView.delegate = self 
    self.tableView.dataSource = self 
    self.tableView.estimatedRowHeight = 2.0 
    self.tableView.rowHeight = UITableViewAutomaticDimension 
    self.tableView.tableFooterView = UIView() 
} 


// TableView DataSource methods 
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    return 3 
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell:ExpandableCell = tableView.dequeueReusableCell(withIdentifier: "ExpandableCell") as! ExpandableCell 
    cell.img.image = UIImage(named: indexPath.row.description) 
    cell.isExpanded = false 
    return cell 
} 

// TableView Delegate methods 
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 

    guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell 
     else { return } 

     UIView.animate(withDuration: 0.3, animations: { 
      tableView.beginUpdates() 
      cell.isExpanded = !cell.isExpanded 
      tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.top, animated: true) 
      tableView.endUpdates() 

     }) 

    } 




func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 
    guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell 
     else { return } 
    UIView.animate(withDuration: 0.3, animations: { 
     tableView.beginUpdates() 
     cell.isExpanded = false 
     tableView.endUpdates() 
    }) 
} 
} 

esercitazione completa disponibile here