I'm trying to build a pretty complex table. One cell of this table imports another table. In this imported table I show different rows depending on the situation. The imported table and all the importable table cells are organized into own nib files with own controllers for every cell. As a good programmer I'm trying to use dependency injection throughout the project. The problem now is that when I use the usual way of registering the nibs in viewDidLoad()
let cellNib = UINib(nibName: "BatchConsumptionCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionCell")
and then use them as dequeueReusableCell(withIdentifier: for:) in tableView(tableView: cellForRowAt:)
let batchConsumptionCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionCell", for: indexPath) as! BatchConsumptionCell
batchConsumptionCell.setConsumption(consumption: consumption)
return batchConsumptionCell
I'm not able to inject the dependencies in time.
In the BatchConsumptionCell all I do in tableView(tableView: cellForRowAt:) works fine. This function is called after dequeueReusableCell(withIdentifier: for:) is executed. But as soon as I'm trying to make the tableView(tableView: numberOfRowsInSection:) dynamic I run into problems. This functions seems to be called before dequeueReusableCell(withIdentifier: for:) so the dependencies are not injected at this point.
I tried to override the init(nibName: bundle:) initializer in my BatchConsumptionCell, but this is a UITableViewCell so I was not able to override this.
How do I approach this? Is there any way I could inject the dependencies as the nib and its controller are initialized? Or do I organize my cells all wrong? Any ideas would be much appreciated.
For more clarity here is my code:
ConsumptionDetailViewController
import UIKit
class ConsumptionDetailViewController: UITableViewController {
// MARK: - Properties
var moc: NSManagedObjectContext!
var consumption: Consumption!
// MARK: - Outlet Properties
#IBOutlet weak var labelDate: UILabel!
#IBOutlet weak var labelTime: UILabel!
...
// MARK: - Default Methods
override func viewDidLoad() {
super.viewDidLoad()
let cellNib = UINib(nibName: "BatchConsumptionCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionCell")
updateTableFields(selectedConsumption: consumption)
}
// MARK: - UI Update Methods
func updateTableFields(selectedConsumption: Consumption) {
labelId.text = selectedConsumption.wtId
...
}
// MARK: - BatchAddEditDelegate Methods
func didFinishEditing(consumption: Consumption) {
updateTableFields(selectedConsumption: consumption)
}
// MARK: - TableView Methods
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
...
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
...
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
...
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
...
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 1 && indexPath.row == 0 {
let batchConsumptionCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionCell", for: indexPath) as! BatchConsumptionCell
batchConsumptionCell.setConsumption(consumption: consumption)
return batchConsumptionCell
}
return super.tableView(tableView, cellForRowAt: indexPath)
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
...
}
}
BatchConsumptionCell
import UIKit
class BatchConsumptionCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
var consumption: Consumption!
var batchConsumptionCount: Int!
#IBOutlet weak var tableView: UITableView!
override func awakeFromNib() {
super.awakeFromNib()
var cellNib = UINib(nibName: "BatchConsumptionBasicCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionBasicCell")
cellNib = UINib(nibName: "BatchConsumptionMultiCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionMultiCell")
self.tableView.delegate = self
self.tableView.dataSource = self
}
func setConsumption(consumption: Consumption) {
self.consumption = consumption
self.batchConsumptionCount = consumption.batchConsumptions?.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if batchConsumptionCount == 1 { // <-- This does not work
return 3
} else {
return batchConsumptionCount
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if batchConsumptionCount == 1 { // <-- This works fine
let batchConsumptionBasicCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionBasicCell", for: indexPath) as! BatchConsumptionBasicCell
let bc = consumption.batchConsumptions?.allObjects[0] as! BatchConsumption
if indexPath.row == 0 {
batchConsumptionBasicCell.configure(title: "Batch", detail: (bc.batch?.wtId)!)
} else if indexPath.row == 1 {
batchConsumptionBasicCell.configure(title: "Weight", detail: String(describing: bc.weight!))
} else if indexPath.row == 2 {
batchConsumptionBasicCell.configure(title: "Price", detail: String(format:"%.2f", bc.price))
}
} else if batchConsumptionCount >= 2 {
let batchConsumptionMultiCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionMultiCell", for: indexPath) as! BatchConsumptionMultiCell
return batchConsumptionMultiCell
}
return UITableViewCell()
}
}
See the comments // <-- This works fine and
// <-- This does not work in BatchConsumptionCell
You should also register nib to the BatchConsumptionCell class, and implement necessary table view's data source & delegate methods:
class BatchConsumptionCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var consumption: Consumption!
var batchConsumptionCount: Int!
override func awakeFromNib() {
super.awakeFromNib()
var cellNib = UINib(nibName: "BatchConsumptionBasicCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionBasicCell")
cellNib = UINib(nibName: "BatchConsumptionMultiCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionMultiCell")
self.tableView.delegate = self
self.tableView.dataSource = self
}
func setConsumption(consumption: Consumption) {
self.consumption = consumption
self.batchConsumptionCount = consumption.batchConsumptions?.count
tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return consumption == nil ? 0 : 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if batchConsumptionCount == .some(1) { return 3 }
return batchConsumptionCount ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch batchConsumptionCount {
case .some(1):
let batchConsumptionBasicCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionBasicCell", for: indexPath) as! BatchConsumptionBasicCell
let bc = consumption.batchConsumptions?.allObjects[0] as! BatchConsumption
if indexPath.row == 0 {
batchConsumptionBasicCell.configure(title: "Batch", detail: (bc.batch?.wtId)!)
} else if indexPath.row == 1 {
batchConsumptionBasicCell.configure(title: "Weight", detail: String(describing: bc.weight!))
} else if indexPath.row == 2 {
batchConsumptionBasicCell.configure(title: "Price", detail: String(format:"%.2f", bc.price))
}
return batchConsumptionBasicCell
default:
let batchConsumptionMultiCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionMultiCell", for: indexPath) as! BatchConsumptionMultiCell
return batchConsumptionMultiCell
}
}
}
Related
I want to implement the different expand/collapse section in tableView and each sections contain the different kind of data.I'm getting the index of section when user click on button. Now i want when user click on button section collapse and expand.Initially first time all sections data should be hidden when user first time comes. See my code and screenshot.
ViewController Code:
enum ProfileItems: Int{
case namePicture = 0
case about = 1
}
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var items:[ProfileItems] = [.namePicture, .about]
var profile:[Profiles] = [
Profiles(name: "Ki", profileImage: "slide2"),
Profiles(name: "Kaleem", profileImage: "slide3"),
Profiles(name: "Jameel", profileImage: "slide2")
]
var about: [About] = [
About(title: "Johns"),
About(title: "Dons")
]
var isHiddenSection: Bool = false {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
registerCell()
}
func registerCell() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: String(describing: PictureTvCell.self), bundle: .main), forCellReuseIdentifier: String(describing: PictureTvCell.self))
tableView.register(UINib(nibName: String(describing: AboutTVCell.self), bundle: .main), forCellReuseIdentifier: String(describing: AboutTVCell.self))
tableView.register(UINib(nibName: String(describing: HeaderTVCell.self), bundle: .main), forCellReuseIdentifier: String(describing: HeaderTVCell.self))
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: HeaderTVCell.self)) as? HeaderTVCell else {return UIView()}
switch items[section]{
case .namePicture:
cell.lableName.text = "Info"
case .about:
cell.lableName.text = "Collection"
}
cell.buttonCollpase.tag = items[section].rawValue
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch items[section] {
case .namePicture:
return profile.count
case .about:
return about.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch items[indexPath.section] {
case .namePicture:
return configurePictureCell(tableView, cellForRowAt: indexPath)
case .about:
return configureAboutCell(tableView, cellForRowAt: indexPath)
}
}
func configurePictureCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PictureTvCell.self)) as? PictureTvCell else {return UITableViewCell()}
cell.profiles = self.profile[indexPath.row]
return cell
}
func configureAboutCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: AboutTVCell.self)) as? AboutTVCell else {return UITableViewCell()}
cell.about = self.about[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
}
extension ViewController: CollpaseSection {
func getIndex(_ index: Int) {
switch items[index] {
case .namePicture:
print("picture")
case .about:
print("about")
tableView.reloadData()
}
}
}
TableViewHederCell Code:
protocol CollpaseSection:AnyObject {
func getIndex(_ index: Int)
}
class HeaderTVCell: UITableViewCell {
#IBOutlet weak var lableName: UILabel!
#IBOutlet weak var buttonCollpase: UIButton!
weak var delegate: CollpaseSection?
#IBAction func didTapCollapse(_ sender: UIButton) {
delegate?.getIndex(sender.tag)
}
}
Screenshot current output:
enter image description here
my cells are not appearing.
I did:
Checked if datasource and delegate were connected
Checked if my custom cells identifier name and class were correct
Things that I didn't:
I am struggling with auto layout, so I just decided not to do it.
My app is loading with the correct amount of cells, but the cells are not registered.
My code:
import UIKit
class WelcomeViewController: UITableViewController, NetworkManagerDelegate {
private var networkManager = NetworkManager()
private var infoForCells = [Result]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "ImageViewCell", bundle: nil), forCellReuseIdentifier: "imageCell")
networkManager.delegate = self
networkManager.fetchNews()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return infoForCells.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath) as? ImageViewCell else{
return UITableViewCell(style: .default, reuseIdentifier: "cell")
}
let cellIndex = infoForCells[indexPath.row]
cell.titleForImage.text = cellIndex.alt_description
print(cell.titleForImage ?? "lol")
// if let image = cellIndex.urlToImage {
// cell.imageForArticle.load(url: image)
// }
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func didUpdateNews(root: Root) {
infoForCells = root.results
}
}
Reload the table
func didUpdateNews(root: Root) {
infoForCells = root.results
tableView.reloadData()
}
In addition to Sh_Khan answer you can also listen to updates of infoForCells property
private var infoForCells = [Result]() {
didSet {
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
}
I have a view controller with a table view and a search bar. I'd like to send a user to one of the three other view controllers based on the text in the cell.
SearchViewController:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if (searchBar.text == search[indexPath.row].cleanName) {
performSegue(withIdentifier: "songs", sender: self)
} else if (searchBar.text != search[indexPath.row].cleanName) {
performSegue(withIdentifier: "artist", sender: self)
} else {
performSegue(withIdentifier: "albums", sender: self)
}
}
Whenever I select the cell in the first view controller, my app crashes and I get Thread 1: signal SIGABRT 'unable to dequeue a cell with identifier SearchTableViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard' This error says that I must register a nib for my song view controller but I already did.
SearhesViewController:
class SearchesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchesBar: UISearchBar!
var searchActive: Bool = false
var search = [Search]()
let filePath = "http://127.0.0.1/musicfiles"
var songs = [Songs]()
var artists = [Artist]()
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "SearchTableViewCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "SearchTableViewCell")
tableView.delegate = self
tableView.dataSource = self
searchesBar.delegate = self
retriveData()
print(search)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
songs = songs.filter({ (songName) -> Bool in
return songName.songname.lowercased().range(of: searchText.lowercased()) != nil
})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (searchActive) {
return search.count
} else {
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SearchTableViewCell", for: indexPath) as! SearchTableViewCell
cell.mainLabel!.text = search[indexPath.row].cleanName
cell.secondLabel!.text = songs[indexPath.row].artistId
cell.cellImage!.image = UIImage(named: songs[indexPath.row].cover)
return cell;
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let searchSong = search[indexPath.row].cleanName
let searchArtist = search[indexPath.row].artistid
let fileURLString = "\(filePath)\(searchSong)\(searchArtist)"
print(fileURLString)
}
The problem is that there is no such nib. Therefore your call to
let nib = UINib(nibName: "SearchTableViewCell", bundle: nil)
yields nil, and your call to
tableView.register(nib, forCellReuseIdentifier: "SearchTableViewCell")
does nothing. You are saying the words, but you are not actually registering any nib. Hence the crash.
Make sure your cell is properly configured on a Storyboard:
Go to your cell on storyboard
In Identity Inspector, the cell must be set to Custom Class
SearchTableViewCell
In Attibutes Inspector, the cell Identifier must be set to SearchTableViewCell
Right now I am trying to move information from my goal cell into a new table view cell, and am having difficulty getting the cell to display.
Here is the code for my goal cell.
import UIKit
class GoalsViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var Goals: [String] = ["goal 1", "goal 2", "goal 3"]
let theEmptyModel: [String] = ["No data in this section."]
var valueToPass = ""
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func showGoalSelected() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
let popUp = GoalSelectedPopUp()
self.view.addSubview(popUp)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "GoalConversationsCell_1") {
let viewController = segue.destination as! ActiveGoalsViewController
viewController.Goals.append([valueToPass])
}
}
}
extension GoalsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Goals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "GoalCell_1", for: indexPath)
cell.textLabel?.text = Goals[indexPath.row]
cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
cell.textLabel?.numberOfLines = 3
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
valueToPass = Goals[indexPath.row]
performSegue(withIdentifier: "activeGoalsSegue", sender: self)
Goals.remove(at: indexPath.row)
if Goals.count != 0 {
showGoalSelected()
} else {
Goals.append(contentsOf: theEmptyModel)
}
tableView.reloadData()
}
}
Here is the goal cells storyboard with the push segue connecting it to the other table view.
That other table view is shown below.
Here is the code for this new tableview.
import UIKit
class ActiveGoalsViewController: UIViewController {
#IBOutlet weak var goalTableView: UITableView!
let sections: [String] = ["Mark as Complete:", "History:"]
var goals: [[String]] = [[], []]
let theEmptyModel: [String] = ["No data in this section."]
extension ActiveGoalsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Goals[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TodayGoalViewCell_1", for: indexPath) as? GoalTableViewCell
cell?.goalLabel.text = Goals[indexPath.section][indexPath.row]
cell?.cellDelegate = self
cell?.index = indexPath
return cell!
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return Goals.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
if Goals[0] != theEmptyModel {
Goals[1].append(Goals[0][indexPath.row])
if Goals[1].first!.contains("No data in this section.") {
Goals[1].removeFirst()
}
Goals[0].remove(at: indexPath.row)
if Goals[0].count == 0 {
Goals[0].append(contentsOf: theEmptyModel)
}
tableView.reloadData()
}
}
}
Once the goal is selected, it sends me to the new storyboard, but this new view does not display the goal that was just added. Can someone help me figure out why this isn't working? Thanks.
I think in the second view controller you need to access the "goals" variable with a lower case g rather then the "Goals" variable with an upper case G.
I try to use types and enum instead of string for the cellIdentifier
I don't want to use the cell class name as identifier since I can have multiple Identifiers for one type cell (for example the basic cell in different tableviews)
enum TableViewCellIdentifier: String {
case formCell, questionCell
// Here I can have the type, and I want to use it to ensure the cell dequeued by the identifier is the right type
var typeCell: UITableViewCell.Type {
switch self {
case .formCell:
return UITableViewCell.self
case .questionCell:
return QuestionCell.self
}
}
// I could use an extension of UITableView to build an other version of dequeueCell,
// but I'll choose after it the type constraint will let me choose.
// Here I want to constraint the return type with the value of myValue.typeCell
func dequeueCell(with tableView: UITableView, for indexPath: IndexPath) -> UITableViewCell {
return tableView.dequeueReusableCell(withIdentifier: self.rawValue, for: indexPath)
}
// func dequeueCell<T: self.typeCell>(with tableView: UITableView, for indexPath: IndexPath) -> T {
// return tableView.dequeueReusableCell(withIdentifier: self.rawValue, for: indexPath) as! T
// }
}
Any Ideas ?
Thanks !
import Foundation
import UIKit
protocol Reusable: class{
static var reuseIdentifier: String { get }
}
extension Reusable {
static var reuseIdentifier: String {
return String(describing: self)
}
}
extension UITableViewCell: Reusable{}
extension UITableViewHeaderFooterView: Reusable{}
protocol NibLoadableView {
static var nibName: String { get }
}
extension NibLoadableView {
static var nibName: String {
return String(describing: self)
}
}
extension UITableViewCell: NibLoadableView {}
extension UITableView {
func register<T: UITableViewCell>(_: T.Type) {
register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}
func registerNib<T: UITableViewCell>(_: T.Type) {
let bundle = Bundle(for: T.self)
let nib = UINib(nibName: T.reuseIdentifier, bundle: bundle)
print(T.nibName, T.reuseIdentifier)
register(nib, forCellReuseIdentifier: T.reuseIdentifier)
}
func registerHeaderNib<T: UITableViewHeaderFooterView>(_: T.Type) {
let bundle = Bundle(for: T.self)
let nib = UINib(nibName: T.reuseIdentifier, bundle: bundle)
print(T.reuseIdentifier, T.reuseIdentifier)
register(nib, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
}
func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T {
print(T.reuseIdentifier)
guard let cell = dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)")
}
return cell
}
func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>(for section: Int) -> T {
print(T.reuseIdentifier)
guard let cell = dequeueReusableHeaderFooterView(withIdentifier: "DemoHeaderView") as? T else {
fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)")
}
return cell
}
}
This is how ViewContoller will use it.
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(TestCell.self)
let headerNib = UINib.init(nibName: "DemoHeaderView", bundle: Bundle.main)
tableView.register(headerNib, forHeaderFooterViewReuseIdentifier: "DemoHeaderView")
tableView.registerHeaderNib(DemoHeaderView.self)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
// MARK: - UITableView delegate
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: TestCell = tableView.dequeueReusableCell(for: indexPath)
cell.textLabel?.text = "\(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = tableView.dequeueReusableHeaderFooterView(for: section)
return headerView
}
}