2014-12-20 9 views
23

Sto lavorando su una vista tabella con una cella personalizzata che è stata creata a livello di codice, senza utilizzo di IB. Ho dato un'occhiata a Google e ho chiesto informazioni su NSChat, ma non riesco ancora a farlo funzionare. Visualizza solo la vista tabella predefinita. Grazie in anticipo!Utilizzo di una sottoclasse UITableViewCell creata in modo programmatico in Swift

EventCell.swift

import UIKit 

class EventCell: UITableViewCell { 

    var eventName: UILabel = UILabel() 
    var eventCity: UILabel = UILabel() 
    var eventTime: UILabel = UILabel() 

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 
     super.init(style: style, reuseIdentifier: reuseIdentifier) 

     self.contentView.addSubview(eventName) 
     self.contentView.addSubview(eventCity) 
     self.contentView.addSubview(eventTime) 
    } 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 

    override func layoutSubviews() { 
     super.layoutSubviews() 

     eventName = UILabel(frame: CGRectMake(20, 10, self.bounds.size.width - 40, 25)) 
     eventCity = UILabel(frame: CGRectMake(0, 0, 0, 0)) 
     eventTime = UILabel(frame: CGRectMake(0, 0, 0, 0)) 

    } 

} 

ViewController.swift

class ViewController: UITableViewController, UITableViewDelegate { 

    var events: Dictionary<String, [String]> = ["0": ["Monroe Family", "La Cañada", "8:30"]] 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     tableView.dataSource = self; 
     tableView.delegate = self; 

     tableView.registerClass(EventCell.self, forCellReuseIdentifier: "EventCell") 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 

    } 

    override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { 
     return UITableViewAutomaticDimension; 
    } 

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return events.count 
    } 

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 

     var cellIdendifier: String = "EventCell" 

     var cell: EventCell = tableView.dequeueReusableCellWithIdentifier(cellIdendifier, forIndexPath: indexPath) as EventCell 
     cell = EventCell(style: .Default, reuseIdentifier: cellIdendifier) 

     if let i = events[String(indexPath.row)] { 
      cell.eventName.text = i[0] 
      cell.eventCity.text = i[1] 
      cell.eventTime.text = i[2] 
     } 

     cell.sizeToFit() 

     return cell 


    } 
} 
+0

https://github.com/ericcgu/TableViewCellWithAutoLayoutiOS8 – ericgu

+0

lasciare cellIdendifier: String = "EventCell" poiché cellIdentifier non è mutato. – Das

+0

let cella: EventCell = tableView.dequeueReusableCellWithIdentifier (cellIdendifier, forIndexPath: indexPath) as! EventCell poiché la cella non viene mutata e forzata con il downcast! – Das

risposta

21

Ok prima un paio di osservazioni importanti.

Prima, si crea inutilmente la propria nuova cella ogni volta che la vista tabella richiede una cella invece di riutilizzare le celle precedenti. È necessario rimuovere questa riga:

cell = EventCell(style: .Default, reuseIdentifier: cellIdendifier) 

Questo è inutile perché dequeue creerà automaticamente nuove cellule, se necessario in base alla classe di aver registrato per l'identificatore dato.

Secondo, non si dovrebbero usare i limiti dello schermo principale quando si stende il codice. Questo si romperà se la tua vista tabella non è la larghezza completa. Invece, è possibile utilizzare self.bounds in modo che sia sempre relativo alla cella stessa.

Terzo, non si dovrebbe chiamare setNeedsLayout o layoutIfNeeded perché se si chiama quel metodo, è già tutto disposto di nuovo.

Quarto, è necessario registrare la classe di celle di visualizzazione tabella prima di impostare l'origine dati di visualizzazione tabella nel caso in cui UITableView inizi ogni richiesta di informazioni dall'origine dati quando viene impostata l'origine dati.

Quinto, due delle vostre sottoview hanno una dimensione di 0,0 in modo che non si presentino comunque.

Se questo codice è effettivamente in esecuzione senza arresti anomali, si stanno creando EventCells perché si sta eseguendo una trasmissione forzata dal risultato dello dequeueReusableCellWithIdentifier:forIndexPath. Ciò significa che hai semplicemente un problema di layout/display/dati.

+0

In questo caso, self.bound sarebbe uguale a self.contentView.bounds? – ppalancica

3

Il tuo codice particolare non funziona perché sta creando nuovi UILabels in EventCell.layoutSubviews. Solo gli UILabel iniziali vengono aggiunti all'albero delle viste e le loro dimensioni sono probabilmente pari a 0.

Immagino che intendiate aggiornare i loro frame, ad es. aggiornare il metodo della classe EventCell:

override func layoutSubviews() { 
    super.layoutSubviews() 

    eventName.frame = CGRectMake(20, 10, self.bounds.size.width - 40, 25) 
    eventCity.frame = CGRectMake(<actual size>) 
    eventTime.frame = CGRectMake(<actual size>) 
} 
2

È possibile utilizzare esemplificazione pigro in rapida per evitare di dover cadere in layoutSubviews/vista danza ciclo di vita.

class EventCell: UITableViewCell { 
    lazy public var lblName = { 
    return UILabel (frame: CGRectMake(10, 0, self.bounds.size.width , 40)) 
    }() 

lazy public var lblCity = { 
    return UILabel (frame: CGRectMake(10, 40, self.bounds.size.width , 20)) 
    }() 
lazy public var lblTime = { 
    return UILabel (frame: CGRectMake(self.bounds.size.width - 80, 10, , 25)) 
    }() 


    override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 
    super.init(style: style, reuseIdentifier: reuseIdentifier) 

    self.contentView.addSubview(lblName) 
    self.contentView.addSubview(lblCity) 
    self.contentView.addSubview(lblTime) 
    } 

} 
23

Ho avuto la stessa domanda. Ho applicato il consiglio nella risposta di @ drewag, ma c'erano alcuni problemi aggiuntivi. Di seguito è riportato un semplice esempio di prova di concetto che mostra come utilizzare una sottoclasse creata automaticamente a livello di programmazione UITableViewCell.

L'immagine seguente è come dovrebbe apparire.I rettangoli gialli sono le visualizzazioni secondarie UILabel nelle celle personalizzate.

enter image description here

Codice

Ecco la sottoclasse UITableViewCell. Il suo compito è inizializzare la cella e il suo contenuto, aggiungere le sottoview e impaginare le sottoview.

import UIKit 
class MyCustomCell: UITableViewCell { 

    var myLabel = UILabel() 

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 
     super.init(style: style, reuseIdentifier: reuseIdentifier) 

     myLabel.backgroundColor = UIColor.yellowColor() 
     self.contentView.addSubview(myLabel) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 

    override func layoutSubviews() { 
     super.layoutSubviews() 

     myLabel.frame = CGRect(x: 20, y: 0, width: 70, height: 30) 
    } 
} 

Ecco il codice del controller di visualizzazione.

import UIKit 
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 

    @IBOutlet weak var tableView: UITableView! 

    let myArray = ["one", "two", "three", "four"] 
    let cellReuseIdendifier = "cell" 


    override func viewDidLoad() { 
     super.viewDidLoad() 

     tableView.registerClass(MyCustomCell.self, forCellReuseIdentifier: cellReuseIdendifier) 

     tableView.dataSource = self 
     tableView.delegate = self 
    } 

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return myArray.count 
    } 

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 

     let cell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdendifier, forIndexPath: indexPath) as! MyCustomCell 
     cell.myLabel.text = myArray[indexPath.row] 

     return cell 
    } 
} 

Note:

  • ho usato un normale UIViewController piuttosto che un UITableViewController. Se si utilizza un UITableViewController, rimuovere i riferimenti al protocollo UITableViewDelegate e UITableViewDataSource poiché questi sono ridondanti per un UITableViewController. Inoltre, non è necessaria la riga @IBOutlet tableView.
  • Il problema principale che ho trovato nella domanda originale era eventName = UILabel(frame: CGRectMake(...)). Invece avrebbe dovuto essere eventName.frame = CGRect(...)
Problemi correlati