web-dev-qa-db-fra.com

Créer UITableView par programme dans Swift

J'essaie d'implémenter UITableView par programme sans utiliser xib ou Storyboards. Ceci est mon code:

ViewController.Swift

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let table: UITableViewController = MyTableViewController()
        let tableView: UITableView = UITableView()
        tableView.frame = CGRect(x: 10, y: 10, width: 100, height: 500)
        tableView.dataSource = table
        tableView.delegate = table

        self.view.addSubview(tableView)
    }
}

MyTableViewController.Swift

import UIKit

class MyTableViewController: UITableViewController {

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        NSLog("sections")
        return 2
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        NSLog("rows")
        return 3
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        NSLog("get cell")
        let cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
        cell.textLabel!.text = "foo"
        return cell
    }  
}

Mais lorsque je lance l'application, tout ce que je reçois est un tableau vide. Dans le journal, je vois quelques lignes de sections et rows, mais pas de get cell. Comment puis-je corriger ce code pour obtenir un tableau avec 6 lignes de texte foo?

32
Ondra

Note: Comme vous l'avez dit, vous venez de commencer à programmer dans Swift. J'ai créé une tableView par programme. Copy et paste ci-dessous code dans votre viewController et run le projet ...

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    private let myArray: NSArray = ["First","Second","Third"]
    private var myTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
        let displayWidth: CGFloat = self.view.frame.width
        let displayHeight: CGFloat = self.view.frame.height

        myTableView = UITableView(frame: CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
        myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
        myTableView.dataSource = self
        myTableView.delegate = self
        self.view.addSubview(myTableView)
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Num: \(indexPath.row)")
        print("Value: \(myArray[indexPath.row])")
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath as IndexPath)
        cell.textLabel!.text = "\(myArray[indexPath.row])"
        return cell
    }
}

Sortie:

 enter image description here

71
Joe

Mis à jour pour Swift 3

Option 1:

import UIKit
//
// MARK :- TableViewController
//
class TableViewController: UITableViewController {

    private let headerId = "headerId"
    private let footerId = "footerId"
    private let cellId = "cellId"

    //
    // MARK :- HEADER
    //
    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {

        return 150
    }

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerId) as! CustomTableViewHeader
        return header
    }

    //
    // MARK :- FOOTER
    //
    override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {

        return 150
    }

    override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {

        let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: footerId) as! CustomTableViewFooter
        return footer
    }

    //
    // MARK :- CELL
    //
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 1
    }

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

        return 150
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CustomTableCell
        return cell
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "TableView Demo"
        view.backgroundColor = .white
        setupTableView()
    }

    func setupTableView() {

        tableView.backgroundColor = .lightGray
        tableView.register(CustomTableViewHeader.self, forHeaderFooterViewReuseIdentifier: headerId)
        tableView.register(CustomTableViewFooter.self, forHeaderFooterViewReuseIdentifier: footerId)
        tableView.register(CustomTableCell.self, forCellReuseIdentifier: cellId)
    }
}

//
// MARK :- HEADER
//
class CustomTableViewHeader: UITableViewHeaderFooterView {

    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)

        contentView.backgroundColor = .orange
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

//
// MARK :- FOOTER
//
class CustomTableViewFooter: UITableViewHeaderFooterView {

    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)

        contentView.backgroundColor = .green
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

//
// MARK :- CELL
//
class CustomTableCell: UITableViewCell {

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

        contentView.backgroundColor = .white
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Option 2: remplacer ci-dessus Option 1 TableViewController avec cette classe

import UIKit
//
// MARK :- ViewController
//
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    private let headerId = "headerId"
    private let footerId = "footerId"
    private let cellId = "cellId"

    lazy var tableView: UITableView = {

        let tv = UITableView(frame: .zero, style: .plain)
        tv.translatesAutoresizingMaskIntoConstraints = false
        tv.backgroundColor = .lightGray
        tv.delegate = self
        tv.dataSource = self
        tv.register(CustomTableViewHeader.self, forHeaderFooterViewReuseIdentifier: self.headerId)
        tv.register(CustomTableViewFooter.self, forHeaderFooterViewReuseIdentifier: self.footerId)
        tv.register(CustomTableCell.self, forCellReuseIdentifier: self.cellId)
        return tv
    }()

    //
    // MARK :- HEADER
    //
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {

        return 150
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerId) as! CustomTableViewHeader
        return header
    }

    //
    // MARK :- FOOTER
    //
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {

        return 150
    }

    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {

        let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: footerId) as! CustomTableViewFooter
        return footer
    }

    //
    // MARK :- CELL
    //
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 1
    }

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

        return 150
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CustomTableCell
        return cell
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "TableView Demo"
        view.backgroundColor = .white
        view.addSubview(tableView)
        setupAutoLayout()
    }

    func setupAutoLayout() {

        tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    }
}

 enter image description here

16
iajmeri43

Cela n'a aucun sens que vous utilisiez une UITableViewController comme source de données et déléguée pour la vue de table de votre contrôleur de vue. Votre propre contrôleur de vue doit être la source de données et le délégué de la vue de table.

Comme vous semblez vouloir un contrôleur de vue avec une vue de table qui ne prend pas la totalité de la vue, déplacez-le dans votre contrôleur de vue comme suit:

ViewController.Swift:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tableView: UITableView = UITableView()
        tableView.frame = CGRect(x: 10, y: 10, width: 100, height: 500)
        tableView.dataSource = self
        tableView.delegate = self

        self.view.addSubview(tableView)
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        NSLog("sections")
        return 2
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        NSLog("rows")
        return 3
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        NSLog("get cell")
        let cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
        cell.textLabel!.text = "foo"
        return cell
    }  
}
4
rmaddy

Swift 4 compatible

Au lieu d'ajouter une UITableView à votre UIViewController, vous devriez envisager de créer une UITableViewController et éviter de configurer des délégués:

class YourTVC: TableViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    // setup stuff
    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "yourCell")
  }

  // MARK: TableView
  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 3 // set to value needed
  }

  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath)
    cell.textLabel?.text = "Cell at row \(indexPath.row)"
    return cell
  }

}
3
budidino

Vous n'avez pas besoin de créer une classe séparée pour UITableView. Dans votre classe ViewController, implémentez les protocoles UITableViewDelegate et UITableViewDataSource, puis implémentez les méthodes de délégation . Je pense que votre code devrait ressembler à 

class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource {

    override func viewDidLoad() {
        super.viewDidLoad()

        let table: UITableViewController = MyTableViewController()
        let tableView: UITableView = UITableView()
        tableView.frame = CGRect(x: 10, y: 10, width: 100, height: 500)
        tableView.dataSource = table
        tableView.delegate = table

        self.view.addSubview(tableView)
    }
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        NSLog("sections")
        return 2
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        NSLog("rows")
        return 3
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        NSLog("get cell")
        let cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
        cell.textLabel!.text = "foo"
        return cell
    } 
}

Indiquez-nous plus d'informations ou affichez les journaux si le problème persiste.

2
saad_nad
import UIKit

class ViewController: UITableViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.register(TableCell.self, forCellReuseIdentifier: "cell")
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 10
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableCell
    cell.nameLabel.text = "TableViewCell programtically"
    cell.nameLabel.textAlignment = .center
    cell.nameLabel.textColor  = .white
    return cell
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 100
}
}

J'ai eu un problème similaire en ce que les données ne seraient pas peuplées pour mon UITableView programmatique. En effet, j'utilisais un délégué/source de données sans référence forte. Une fois que j'ai gardé une référence à ce sujet (une classe implémentant à la fois UITableViewDataSource et UITableViewDelegate), les données ont été renseignées.

0
salami