dequeue reusable cell always crashes - swift

in this code i add the programatic VC:
#IBAction func addPerson(_ sender: UIBarButtonItem) {
let controller = CNContactPickerViewController()
controller.delegate = self
navigationController?.present(controller, animated: true, completion: nil)
}
here i use to go on another VC when i click on a contact (to show info into cells and then i want to make a checkbox for phones/emails to show back in my first VC)
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
let vc = EditContactViewController()
self.navigationController?.pushViewController(vc, animated: true)
i created a class for the next cell and i set up identifier as cell in storyboard
when i do this in tableView cellForRowAtIndexPath
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? ConfigCell
return cell!
this gives me:
fatal error: unexpectedly found nil while unwrapping an Optional value
i also tried withIdentifier,forIndexPath and it says:
2016-11-12 01:35:28.626 iEvents3[2637:47714] *** Terminating app due
to uncaught exception 'NSInternalInconsistencyException', reason:
'unable to dequeue a cell with identifier cell - must register a nib
or a class for the identifier or connect a prototype cell in a
storyboard'
i deleted reuse identifier and put it back again , cleaned the application, restart, everything.
Thank You for your time!

There can be several reason for this:
If you have your UITableViewCell in controller itself, then check the reuse identifier whether it is set to "cell"
If you have a separate .xib for your UITableViewCell, then
a) Register the .nib of cell with your table view in viewDidLoad() of your controller
self.tableView.register(UINib(nibName:"CustomTableViewCell"), forCellWithReuseIdentifier: "cell")
Register the .nib only when you are sure the outlet of table view is set. Otherwise, if the outlet if table view is still nil, it won't register the .nib of your cell.
b) Check whether the reuse identifier is set to "cell"

Make sure there is no '!' sign in interface outlet connections

Even if you don't have a xib for the cell, you may need to register it:
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")

Do you have that cell as a xib? Did you add your cell to the table view? Try this if this is the case
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
self.tableView.register(UINib(nibName: "ConfigCell",bundle: nil), forCellReuseIdentifier: "cell")
}

When cell expand it crash application
func tableView(_ tableView: SLExpandableTableView!, willExpandSection section: UInt, animated: Bool)
{
}
func tableView(_ tableView: SLExpandableTableView!, didExpandSection section: UInt, animated: Bool)
{
let indexPath = IndexPath(row: 0, section: Int(section))
let cell: SLExpandableHeaderCell = (tableView.cellForRow(at: indexPath) as! SLExpandableHeaderCell)
cell.empProfImage?.image = UIImage(named: "upArrowGrey.png")
// let imgCategorySymbol: UIImageView = (cell.viewWithTag(5030) as! UIImageView)
// imgCategorySymbol.image = UIImage(named: "upArrowGrey.png")
for i in 0..<arrCategoriesServices.count
{
if !selectedSectionIndex.contains("\(i)") && i != Int(section) {
tableView.collapseSection(i, animated: true)
}
}
}
func tableView(_ tableView: SLExpandableTableView!, willCollapseSection section: UInt, animated: Bool) {
let indexPath = IndexPath(row: 0, section: Int(section))
let cell: SLExpandableHeaderCell = (tblExpandableView.cellForRow(at: indexPath) as! SLExpandableHeaderCell)
cell.empProfImage?.image = UIImage(named: "downArrowGrey.png")
// let imgCategorysymbol: UIImageView? = (cell.viewWithTag(5030) as! UIImageView)
// imgCategorysymbol?.image = UIImage(named: "downArrowGrey.png")
}
func tableView(_ tableView: SLExpandableTableView, canChangeSection section: Int) -> Bool {
if collapsingSection != section {
return true
}
else {
return false
}
}
func tableView(_ tableView: SLExpandableTableView!, didCollapseSection section: UInt, animated: Bool) {
}
func tableView(_ tableView: SLExpandableTableView, canExpandSection section: Int) -> Bool
{
return true
}
func tableView(_ tableView: SLExpandableTableView!, needsToDownloadDataForExpandableSection section: Int) -> Bool
{
return false
}
func tableView(_ tableView: SLExpandableTableView!, expandingCellForSection section: Int) -> UITableViewCell
{
var cell: SLExpandableHeaderCell!
if cell == nil
{
cell = tblExpandableView.dequeueReusableCell(withIdentifier: headerCell) as! SLExpandableHeaderCell
}
let label = UILabel()
label.frame = CGRect(x: CGFloat(10), y: CGFloat(10), width: CGFloat(100), height: CGFloat(25))
cell.addSubview(label)
cell.empProfImage?.image = UIImage(named: "downArrowGrey.png")
cell?.selectionStyle = .none
let cat: CategoryModel = (arrCategory[section] as! CategoryModel)
label.text = cat.categoryName
print("Expanding table for categoty : \(cat.categoryName)")
// cell.textLabel?.frame = frame
// cell?.textLabel?.text = cat.categoryName
// Util.setCategoryDetailFontColorAndSize(label: (cell?.textLabel!)!, labelText: cat.categoryName)
Util.setCategoryDetailFontColorAndSize(label: label, labelText: cat.categoryName)
cell?.textLabel?.backgroundColor = UIColor.clear
return cell!
}
public func tableView(_ tableView: SLExpandableTableView!, downloadDataForExpandableSection section: Int)
{
}

In my case it was a silly mistake, just a misprint in the name of the .xib, but it took me too much time to find it because the name was very long and difficult to see the mistake, and so I was searching for other causes.
It seems so obvious now.. but shit happens :)
I hope save your time if it's your case.

For me, the TableView Cell was set to Static content instead of Dynamic since I want it to generate X numbers of cells based on my collection. A good clue was that I saw a weird Header Text with a blue cube above my Table View Cell in the hierarchy overview panel in Xcode.
Here's a visual where to set the Cells to Dynamic.

Related

Making a new UITableView cell's textField the firstResponder

I currently have a table view with cells that contain a label and a textfield, I also have a + bar button item that adds new cells.
What I hope to accomplish is when the user presses the + button the new cell is created and the text field of this cell would automatically become first responder.
Below is my current code for creating the new entry:
func newNoteline() {
let entityDescription = NSEntityDescription.entity(forEntityName: "NotebookContentEntity", in: context)
let item = NotebookContentEntity(entity: entityDescription!, insertInto: context)
item.notebookEntry = ""
item.timeOfEntry = timeOutlet.text
do {
try context.save()
} catch {
print(error)
return
}
loadNotelines()
}
I have thought of several ways of trying to solve this but without much luck of making them work including using a .tag on the text field as soon as it's made - using the text field delegate or using the tableView delegate method - indexPathForPreferredFocusedView.
I just can't figure out how to force the focus to a specific textfield within a cell without the user tapping the text field. Any thoughts?
Calling textField.becomeFirstResponder() method should do what you are looking for.
When to call this function is up to you. for e.g. in below code at cellForRowAt I checked if the value is empty then make the current textfield first responder.
class Test: UIViewController{
var myView: TestView{return view as! TestView}
unowned var tableView: UITableView {return myView.tableView}
unowned var button: UIButton {return myView.button}
var list = [String]()
override func loadView() {
view = TestView()
}
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...10{
list.append("Test \(i)")
}
tableView.dataSource = self
button.addTarget(self, action: #selector(didSelect(_:)), for: .touchUpInside)
}
#objc func didSelect(_ sender: UIButton){
let indexPath = IndexPath(row: list.count, section: 0)
list.append("")
tableView.insertRows(at: [indexPath], with: .automatic)
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
extension Test: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TestViewCell", for: indexPath) as! TestViewCell
let value = list[indexPath.row]
cell.textField.text = value
if value.isEmpty{
cell.textField.becomeFirstResponder()
}
return cell
}
}

Navigate from a ViewController to an another on didSelectRowAt programmatically

I have a UIViewController that contains a custom UITableView. The table has a custom UITableViewCell too.
How to navigate from the first ViewController to an another when you select/click one of the rows?
Note
I have not used StoryBoard.
Update
This my code. Each one of the classes are external file. Let me know, if you need more code.
class TestViewController: UIViewController, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(testTableView)
}
let testTableView: TestTableView = {
let table = TestTableView()
table.register(TestTableViewCell.self, forCellReuseIdentifier: TestTableViewCell.identifier)
return table
}()
}
class TestTableView: UITableView, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TestTableViewCell.identifier, for: indexPath)
return cell
}
}
class TestTableViewCell: UITableViewCell {
static let identifier = "testCell"
}
Here is a complete answer:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newViewController = NewViewController()
self.navigationController?.pushViewController(newViewController, animated: true)
}
In UIKit in order to programmatically navigate from TableCell you can do it without storyboard.
You have two ways to view next viewController
If you want to present .......
animating from bottom to top
(one thing to remember) you can not present new view without dismissing previous presented screen, otherwise app crash due to conflict of presented screen
yourTableView.deselectRow(at: indexPath, animated: true)//used for single Tap
let vC = YourViewController (nibName: "" , bundle : nil)
vC.modalPresentationStyle = .fullScreen //this line is optional for fullscreen
self.present(vC, animated: true , completion: nil)
Or if you want to View your viewController normally (2 is batter) ....
animate from right to left
yourTableView.deselectRow(at: indexPath, animated: true)
let vC = YourViewController()
self.navigationController?.pushViewController(vC, animated: true)
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = yourCustomVCName (nibName: "yourCustomVC nibName" , bundle : nil)
self.present(vc, animated: true , completion: nil)
}

UIMenuController in a TableViewCell Element

I am looking to get a UIMenuController when an image within a cell is tapped. To be clear this is NOT when the cell is tapped, but rather when the image within the cell is tapped. The tap gesture on the image is firing off properly and running the function, but the menu never shows.
Custom Cell:
class VideoHomeCell: UITableViewCell {
var thumbImage:UIImage! {
didSet {
createCell()
}
}
private lazy var thumbImageView:UIImageView = {
let view = UIImageView(image: self.thumbImage)
view.contentMode = .scaleAspectFill
view.clipsToBounds = true
view.isUserInteractionEnabled = true
view.translatesAutoresizingMaskIntoConstraints = false
let gesture = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
view.addGestureRecognizer(gesture)
return view
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func createCell() {
self.addSubview(thumbImageView)
thumbImageView.heightAnchor.constraint(equalToConstant: 100).isActive = true
thumbImageView.widthAnchor.constraint(equalToConstant: 100).isActive = true
thumbImageView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
thumbImageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
}
#objc func imageTapped(recognizer: UIGestureRecognizer) {
let menuController = UIMenuController.shared
let trimItem = UIMenuItem(title: "Trim", action: #selector(menuSelected))
menuController.menuItems = [trimItem]
menuController.setTargetRect(self.thumbImageView.frame, in: self.superview!)
menuController.setMenuVisible(true, animated: true)
}
#objc func menuSelected() {
print("Menu Selection!")
}
}
TableView:
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.videoModelArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! VideoHomeCell
cell.thumbImage = self.videoModelArray[indexPath.row].originalThumbnail
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}
I have tried different values within setTargetRect to include the gesture's view and self.frame. I have also tried rendering in self. Additionally, I manually set a CGRect. Nothing I do allows for the menu to display.
I solved this issue by creating a protocol within the cell subclass and I assigned the table view controller as the delegate. When the image is tapped it fires off the delegate and then the UIMenuController is created within the tableview rather than within the tableviewcell. I pass back the cell and I am then able to use it's view to show the menu. It is also required to have the following on the tableview per Leo Dabus' comment.
override var canBecomeFirstResponder: Bool {
return true
}
In the end I while I was not able to make this work inside the tableviewcell subclass, I do find this an acceptable workaround.

Can't get indexPath of cell in header

I have created prototype custom header cell for a tableView with a button on it. I am trying to get the indexPath of the cell when the button is tapped, but I don't receive it. Any idea what I am doing wrong here?
protocol MediaHeaderCellDelegate: class {
func editPost(cell: MediaHeaderCell)
}
class MediaHeaderCell: UITableViewCell {
weak var delegate: MediaHeaderCellDelegate?
#IBAction func moreOptionsAction(_ sender: UIButton) {
delegate?.editPost(cell: self)
}
}
class NewsfeedTableViewController:UITableViewController, MediaHeaderCellDelegate {
func editPost(cell: MediaHeaderCell) {
guard let indexPath = tableView.indexPath(for: cell) else {
print("indexpath could not be given")
return}
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
let cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.mediaHeaderCell) as! MediaHeaderCell
cell.delegate = self
cell.media = media[section]
return cell
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.mediaCell, for: indexPath) as! MediaTableViewCell
cell.currentUser = currentUser
cell.media = media[indexPath.section]
cell.delegate = self
return cell
}
}
So this is actually all about learning what section a section header belongs to?? Here’s what I do. I have a header class:
class MyHeaderView : UITableViewHeaderFooterView {
var section = 0
}
I register it:
self.tableView.register(
MyHeaderView.self, forHeaderFooterViewReuseIdentifier: self.headerID)
I use and configure it:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let h = tableView
.dequeueReusableHeaderFooterView(withIdentifier: self.headerID) as! MyHeaderView
// other stuff
h.section = section // *
return h
}
Now if the header view is tappable or contains a button or whatever, learning what section this is the header of is trivial.
Your immediate issue is that you are using a table cell as a section header view. That should not be done. Once you resolve that, your next task is to determine the table section from the header view whose button was tapped.
First, change your MediaHeaderCell to be a header view that extends UITableViewHeaderFooterView and update your protocol accordingly:
protocol MediaHeaderViewDelegate: class {
func editPost(view: MediaHeaderView)
}
class MediaHeaderView: UITableViewHeaderFooterView {
weak var delegate: MediaHeaderViewDelegate?
#IBAction func moreOptionsAction(_ sender: UIButton) {
delegate?.editPost(cell: self)
}
}
Then you need to register the header view in your view controller.
Then update your viewForHeaderInSection:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: Storyboard.mediaHeaderView) as! MediaHeaderView
view.delegate = self
view.media = media[section]
view.tag = section
return view
}
And last, update your protocol method implementation:
func editPost(view: MediaHeaderView) {
let section = view.tag
// do something
}
There is one possible issue with this. If your table allows sections to be added or removed, then it is possible that a header view's tag could be wrong when the button is tapped.

creating custom tableview cells in swift

I have a custom cell class with a couple of IBOutlets. I have added the class to the storyboard. I have connected all my outlets. my cellForRowAtIndexPath function looks like this:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as SwipeableCell
cell.mainTextLabel.text = self.venueService.mainCategoriesArray()[indexPath.row]
return cell
}
Here is my custom cell class:
class SwipeableCell: UITableViewCell {
#IBOutlet var option1: UIButton
#IBOutlet var option2: UIButton
#IBOutlet var topLayerView : UIView
#IBOutlet var mainTextLabel : UILabel
#IBOutlet var categoryIcon : UIImageView
init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
}
When I run the app, all my cell are empty. I have logged out self.venueService.mainCategoriesArray() and it contains all the correct strings. I have also tried putting an actual string equal to the label, and that produces the same result.
What am I missing? Any help is appreciated.
Custom Table View Cell Example
Tested with Xcode 9 (edit also tested on 11 / 12 Beta 2) and Swift 4 (edit: also tested on 5.2)
The asker of the original question has solved their problem. I am adding this answer as a mini self contained example project for others who are trying to do the same thing.
The finished project should look like this:
Create a new project
It can be just a Single View Application.
Add the code
Add a new Swift file to your project. Name it MyCustomCell.swift. This class will hold the outlets for the views that you add to your cell in the storyboard.
import UIKit
class MyCustomCell: UITableViewCell {
#IBOutlet weak var myView: UIView!
#IBOutlet weak var myCellLabel: UILabel!
}
We will connect these outlets later.
Open ViewController.swift and make sure you have the following content:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// These strings will be the data for the table view cells
let animals: [String] = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
// These are the colors of the square views in our table view cells.
// In a real project you might use UIImages.
let colors = [UIColor.blue, UIColor.yellow, UIColor.magenta, UIColor.red, UIColor.brown]
// Don't forget to enter this in IB also
let cellReuseIdentifier = "cell"
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.animals.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.myView.backgroundColor = self.colors[indexPath.row]
cell.myCellLabel.text = self.animals[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
Setup the storyboard
Add a Table View to your view controller and use auto layout to pin it to the four sides of the View Controller. Then drag a Table View Cell onto the Table View. And then drag a View and a Label onto the Prototype cell. (You may need to select the Table View Cell and manually set the Row Height to something taller in the Size inspector so that you have more room to work with.) Use auto layout to fix the View and the Label how you want them arranged within the content view of the Table View Cell. For example, I made my View be 100x100.
Other IB settings
Custom class name and Identifier
Select the Table View Cell and set the custom class to be MyCustomCell (the name of the class in the Swift file we added). Also set the Identifier to be cell (the same string that we used for the cellReuseIdentifier in the code above.
Hook Up the Outlets
Control drag from the Table View in the storyboard to the tableView variable in the ViewController code.
Do the same for the View and the Label in your Prototype cell to the myView and myCellLabel variables in the MyCustomCell class.
Finished
That's it. You should be able to run your project now.
Notes
The colored views that I used here could be replaced with anything. An obvious example would be a UIImageView.
If you are just trying to get a TableView to work, see this even more basic example.
If you need a Table View with variable cell heights, see this example.
This is for who are working custom cell with .xib
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let identifier = "Custom"
var cell: CustomCell! = tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomCel
if cell == nil {
tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: identifier)
cell =tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomCell
}return cell}
I have the same problem.
Generally what I did is the same as you.
class dynamicCell: UITableViewCell {
#IBOutlet var testLabel : UILabel
init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
and in the uitableviewcell method:
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell :dynamicCell = tableView.dequeueReusableCellWithIdentifier("cell") as dynamicCell
cell.testLabel.text = "so sad"
println(cell.testLabel)
return cell;
}
and yeah the tableview shows nothing! But guess what, it actually shows something...because the log I get from the println(cell.testLabel) shows that all the Labels are actually displayed out.
BUT! their Frames is strange, which have something like this:
frame = (0 -21; 42 21);
so it has a (0,-21) as (x,y), so that means the label just appears at somewhere outside the bound of the cell.
so I try to add adjust the frame manually like this:
cell.testLabel.frame = CGRectMake(10, 10, 42, 21)
and sadly, it doesn't work.
---------------update after 10 min -----------------
I DID IT.
so, it seems that the problem comes from the Size Classes.
Click on your .storyboard file and go to the File Inspector Tab
UNCHECK THE Size Classes checkbox
and finally, my "so sad"Label comes out!
Thanks for all the different suggestions, but I finally figured it out. The custom class was set up correctly. All I needed to do, was in the storyboard where I choose the custom class: remove it, and select it again. It doesn't make much sense, but that ended up working for me.
Last Updated Version is with xCode 6.1
class StampInfoTableViewCell: UITableViewCell{
#IBOutlet weak var stampDate: UILabel!
#IBOutlet weak var numberText: UILabel!
override init?(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init(coder aDecoder: NSCoder) {
//fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Details
Xcode Version 10.2.1 (10E1001), Swift 5
Solution
import UIKit
// MARK: - IdentifiableCell protocol will generate cell identifier based on the class name
protocol Identifiable: class {}
extension Identifiable { static var identifier: String { return "\(self)"} }
// MARK: - Functions which will use a cell class (conforming Identifiable protocol) to `dequeueReusableCell`
extension UITableView {
typealias IdentifiableCell = UITableViewCell & Identifiable
func register<T: IdentifiableCell>(class: T.Type) { register(T.self, forCellReuseIdentifier: T.identifier) }
func register(classes: [Identifiable.Type]) { classes.forEach { register($0.self, forCellReuseIdentifier: $0.identifier) } }
func dequeueReusableCell<T: IdentifiableCell>(aClass: T.Type, initital closure: ((T) -> Void)?) -> UITableViewCell {
guard let cell = dequeueReusableCell(withIdentifier: T.identifier) as? T else { return UITableViewCell() }
closure?(cell)
return cell
}
func dequeueReusableCell<T: IdentifiableCell>(aClass: T.Type, for indexPath: IndexPath, initital closure: ((T) -> Void)?) -> UITableViewCell {
guard let cell = dequeueReusableCell(withIdentifier: T.identifier, for: indexPath) as? T else { return UITableViewCell() }
closure?(cell)
return cell
}
}
extension Array where Element == UITableViewCell.Type {
var onlyIdentifiables: [Identifiable.Type] { return compactMap { $0 as? Identifiable.Type } }
}
Usage
// Define cells classes
class TableViewCell1: UITableViewCell, Identifiable { /*....*/ }
class TableViewCell2: TableViewCell1 { /*....*/ }
// .....
// Register cells
tableView.register(classes: [TableViewCell1.self, TableViewCell2.self]. onlyIdentifiables)
// Create/Reuse cells
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.row % 2) == 0 {
return tableView.dequeueReusableCell(aClass: TableViewCell1.self, for: indexPath) { cell in
// ....
}
} else {
return tableView.dequeueReusableCell(aClass: TableViewCell2.self, for: indexPath) { cell in
// ...
}
}
}
Full Sample
Do not forget to add the solution code here
import UIKit
class ViewController: UIViewController {
private weak var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
}
// MARK: - Setup(init) subviews
extension ViewController {
private func setupTableView() {
let tableView = UITableView()
view.addSubview(tableView)
self.tableView = tableView
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.register(classes: [TableViewCell1.self, TableViewCell2.self, TableViewCell3.self].onlyIdentifiables)
tableView.dataSource = self
}
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int { return 1 }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 20 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch (indexPath.row % 3) {
case 0:
return tableView.dequeueReusableCell(aClass: TableViewCell1.self, for: indexPath) { cell in
cell.textLabel?.text = "\(cell.classForCoder)"
}
case 1:
return tableView.dequeueReusableCell(aClass: TableViewCell2.self, for: indexPath) { cell in
cell.textLabel?.text = "\(cell.classForCoder)"
}
default:
return tableView.dequeueReusableCell(aClass: TableViewCell3.self, for: indexPath) { cell in
cell.textLabel?.text = "\(cell.classForCoder)"
}
}
}
}
Results
Uncheck "Size Classes" checkbox works for me as well, but you could also add the missing constraints in the interface builder. Just use the built-in function if you don't want to add the constraints on your own. Using constraints is - in my opinion - the better way because the layout is independent from the device (iPhone or iPad).
It is Purely swift notation an working for me
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cellIdentifier:String = "CustomFields"
var cell:CustomCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? CustomCell
if (cell == nil)
{
var nib:Array = NSBundle.mainBundle().loadNibNamed("CustomCell", owner: self, options: nil)
cell = nib[0] as? CustomCell
}
return cell!
}
[1] First Design your tableview cell in StoryBoard.
[2] Put below table view delegate method
//MARK: - Tableview Delegate Methods
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return <“Your Array”>
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
var totalHeight : CGFloat = <cell name>.<label name>.frame.origin.y
totalHeight += UpdateRowHeight(<cell name>.<label name>, textToAdd: <your array>[indexPath.row])
return totalHeight
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell : <cell name>! = tableView.dequeueReusableCellWithIdentifier(“<cell identifier>”, forIndexPath: indexPath) as! CCell_VideoCall
if(cell == nil)
{
cell = NSBundle.mainBundle().loadNibNamed("<cell identifier>", owner: self, options: nil)[0] as! <cell name>;
}
<cell name>.<label name>.text = <your array>[indexPath.row] as? String
return cell as <cell name>
}
//MARK: - Custom Methods
func UpdateRowHeight ( ViewToAdd : UILabel , textToAdd : AnyObject ) -> CGFloat{
var actualHeight : CGFloat = ViewToAdd.frame.size.height
if let strName : String? = (textToAdd as? String)
where !strName!.isEmpty
{
actualHeight = heightForView1(strName!, font: ViewToAdd.font, width: ViewToAdd.frame.size.width, DesignTimeHeight: actualHeight )
}
return actualHeight
}
Set tag for imageview and label in cell
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.tableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("imagedataCell", forIndexPath: indexPath) as! UITableViewCell
let rowData = self.tableData[indexPath.row] as! NSDictionary
let urlString = rowData["artworkUrl60"] as? String
// Create an NSURL instance from the String URL we get from the API
let imgURL = NSURL(string: urlString!)
// Get the formatted price string for display in the subtitle
let formattedPrice = rowData["formattedPrice"] as? String
// Download an NSData representation of the image at the URL
let imgData = NSData(contentsOfURL: imgURL!)
(cell.contentView.viewWithTag(1) as! UIImageView).image = UIImage(data: imgData!)
(cell.contentView.viewWithTag(2) as! UILabel).text = rowData["trackName"] as? String
return cell
}
OR
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "imagedataCell")
if let rowData: NSDictionary = self.tableData[indexPath.row] as? NSDictionary,
urlString = rowData["artworkUrl60"] as? String,
imgURL = NSURL(string: urlString),
formattedPrice = rowData["formattedPrice"] as? String,
imgData = NSData(contentsOfURL: imgURL),
trackName = rowData["trackName"] as? String {
cell.detailTextLabel?.text = formattedPrice
cell.imageView?.image = UIImage(data: imgData)
cell.textLabel?.text = trackName
}
return cell
}
see also TableImage loader from github
The actual Apple reference documentation is quite comprehensive
https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/CreateATableView.html#//apple_ref/doc/uid/TP40015214-CH8-SW2
Scroll down until you see this part