I am trying to create custom section header with dynamic height. I have created a very simplified version of my app where I am displaying a square image as table section header. The image height is the same as the screen width. The tableView has to be dynamic because the image height changes with screen width. Even though the app works, I could not be able to get rid of the constraint warnings. It is a very simple app and I have no idea why it is throwing me constraint errors. It seems like the the 1:1 ratio on the image constraint is the root of the problem. I have attached the constraints of my xib file, code and output screenshot below.
Xib Constraint
Simulator Output
class ViewController: UIViewController {
#IBOutlet weak var myTableView: UITableView!
var xibRef: CustomHeaderView!
var numberOfSection = 3
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
// Register Xib
let nib = UINib(nibName: "CustomHeaderView", bundle: nil)
self.xibRef = nib.instantiateWithOwner(self, options: nil)[0] as? CustomHeaderView
self.myTableView.registerNib(nib, forHeaderFooterViewReuseIdentifier: "CustomHeaderView")
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath)
cell.textLabel!.text = "section \(indexPath.section) row \(indexPath.row)"
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return numberOfSection
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return view.frame.size.width
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableHeaderFooterViewWithIdentifier("CustomHeaderView") as! CustomHeaderView
return cell
}
}
import UIKit
class NewOrdersViewControllers: UIViewController,UITableViewDelegate,UITableViewDataSource {
var items = ["Chilli and Lemon"]
#IBOutlet var tableView:UITableView!
#IBOutlet weak var lblRestaurantNames: UILabel!
#IBOutlet weak var mapView: UIButton!
var cellIdentifier = "cell"
init() {
super.init(nibName : "NewOrdersViewControllers", bundle:nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "tableCell", bundle: nil)
self.tableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view.
}
#IBAction func mapPush(sender: AnyObject) {
let mapVC = MapViewController()
self.navigationController?.pushViewController(mapVC, animated: true)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:tableCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! tableCell
//cell.textLabel?.text = self.items[indexPath.row] as! String
cell.RestaurantLbl.text = self.items[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
I have done it using xib and it shows the following error.
Could not cast value of type 'UITableViewCell' (0x1fe82bc)
In your viewDidLoad() method you register 2 different cell types for the same reuseIdentifier, so the last one (UITableViewCell) takes effect. That's why in tableView:cellForRowAtIndexPath: the cells being dequeued are also of UITableViewCell class and may not be cast to your custom cell class.
Try to remove the line:
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
it should fix your problem and let the tableView dequeue the correct cells.
Trying to work out a couple of scenes in my app my I cant get the tableViews to work as they should.
Im doing a settings like tableView where the user choses between two possible settings. Each options leads to new tableView where the user must pick one of the available options. Once the user has chosen one, the label in the first scene will display the value chosen (which is stored also as NSUserDefaults). So if Im not mistaken this could be achieved with 3 static tableViews. The code following is how Im trying to do it:
Initial settings view controller:
class SettingsVC2: UITableViewController{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0{
let cell = tableView.dequeueReusableCellWithIdentifier("office_cell", forIndexPath: indexPath)
cell.textLabel?.text = "Select your office"
return cell
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier("dept_cell", forIndexPath: indexPath)
cell.textLabel?.text = "Select your department"
return cell
}
One of the settings tableViews:
class SettingsDepartmentTVC: UITableViewController{
let departamentos: [String] = ["Sales", "Pre-Sales", "General Administration", "Customer Service", "Profesional Services", "Regionales"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return departamentos.count
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("department_cell", forIndexPath: indexPath)
cell.textLabel?.text = self.departamentos[indexPath.row]
return cell
}
}
I believe all datasources and delegates are hooked right and UIKit is imported even if not shown.
Erros Im getting:
Assertion failure in -[UITableView
dequeueReusableCellWithIdentifier:forIndexPath:]
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'unable to dequeue a cell
with identifier office_cell - must register a nib or a class for the
identifier or connect a prototype cell in a storyboard
This is the method where I try to populate the tableView:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("CommentsRowTVC", forIndexPath: indexPath) as! CommentsRowTVC
let single_comment = self.AOS[indexPath.row]
cell.titulo_comentario?.text = single_comment.titulo_comment
cell.cuerpo_comentario?.text = single_comment.cuerpo_comment
cell.fecha_comentario?.text = single_comment.fecha_comment
return cell
}
class CommentsRowTVC: UITableViewCell {
#IBOutlet weak var titulo_comentario: UILabel!
#IBOutlet weak var cuerpo_comentario: UILabel!
#IBOutlet weak var fecha_comentario: UILabel!
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
}
}
Here is the connections inspector for the UIViewController where the table is:
And this one for the UIViewCellController:
Errors to this point:
UIView tableView:numberOfRowsInSection: unrecognized selector sent to instance
Terminating app due to uncaught exception NSInvalidArgumentException, reason: [UIView tableView:numberOfRowsInSection:]: unrecognized selector sent to instance
If you're truly making static table view cells then you should just delete these methods. You only use them when you are dequeueing cells dynamically.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return departamentos.count
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("department_cell", forIndexPath: indexPath)
cell.textLabel?.text = self.departamentos[indexPath.row]
return cell
}
If you are actually trying to make dynamic cells then, as your error message suggests, you need to make sure your identifier matches what you have in interface builder:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("{your_identifier}", forIndexPath: indexPath)
cell.textLabel?.text = self.departamentos[indexPath.row]
return cell
}
With the code you have provided it looks like you have not registered your table view cells and identifiers with the table view.
UITableView Class Reference
Discussion Prior to dequeueing any cells, call this method or the
registerNib:forCellReuseIdentifier: method to tell the table view how
to create new cells. If a cell of the specified type is not currently
in a reuse queue, the table view uses the provided information to
create a new cell object automatically.
If you previously registered a class or nib file with the same reuse
identifier, the class you specify in the cellClass parameter replaces
the old entry. You may specify nil for cellClass if you want to
unregister the class from the specified reuse identifier.
I would suggest that you register them in viewDidLoad().
let KDepartmentCellIdentifier = "department_cell"
tableView.registerClass(UITableViewCell.class, forCellReuseIdentifier: kKDepartmentCellIdentifier)
let KOfficeCellIdentifier = "office_cell"
tableView.registerClass(UITableViewCell.class, forCellReuseIdentifier: kOfficeCellIdentifier)
If you are using a nib then use
registerNib(_ nib: UINib?, forCellReuseIdentifier identifier: String)
I created a UITableViewController subclass:
class RootTableViewController: UITableViewController {
private let cellIdentifier = "MyCell2"
private let cellNibName = "MyCell2"
private var itemList: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<1000 {
itemList.append("T\(i)")
}
let nib = UINib(nibName: cellNibName, bundle: nil)
self.tableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MyCell2
return cell
}
MyCell2 is a subclass of UITableViewCell:
class MyCell2: UITableViewCell {
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
}
}
MyCell2.xib contains a UIButton in its ContentView. The Identifier the UITableViewCell is set to MyCell2 and the Class is set to MyCell2.
When I run the app on iOS8 everything works fine. There is no horizontal scrolling. When I run the app on iOS7 horizontal scrolling takes place:
How can I prevent horizontal scrolling of the table in iOS7?
Edit: My UITableView xib configuration is:
Do you have this UITableView configuration in your xib file/storyboard?
Here's my code
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell :UITableViewCell = tableView.dequeueReusableCellWithIdentifier("discovered") as UITableViewCell!
bluetoothlog.registerNib(UINib(nibName: "devicedet", bundle: nil), forCellReuseIdentifier: "discovered")
//NSBundle.mainBundle().loadNibNamed("devicedet", owner: nil, options: nil)[0] as UITableViewCell
cell = blucell
devname.text = peri[indexPath.row]
devstrength.text = signalstrength[indexPath.row]
bluetoothlog.backgroundColor = UIColor.clearColor()
return cell
}
I have tried with the above code and nothing is displayed in Tableview, please help me to change this code working
Thanks
Register your NIB with the reuse identifier:
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "devicedet", bundle: nil), forCellReuseIdentifier: "Cell")
}
Your cellForRowAtIndexPath would then instantiate the cell.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! devicedet
return cell
}
the command registerNib should be called in ViewDidLoad, but not in each call of cell for row
what is 'bluecell'? In your call-back cellforTable.., you assign cell = blucell -> this can cause your problems.
Try this:
import UIKit
class YourViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//call register nib here!!
bluetoothlog.registerNib(UINib(nibName: "devicedet", bundle: nil), forCellReuseIdentifier: "discovered")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return peri.count
}
//it seem that you have to add a è
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell!
{
let cell :UITableViewCell = tableView.dequeueReusableCellWithIdentifier("discovered") as UITableViewCell!
/* REMOVE THIS
bluetoothlog.registerNib(UINib(nibName: "devicedet", bundle: nil), forCellReuseIdentifier: "discovered")
//NSBundle.mainBundle().loadNibNamed("devicedet", owner: nil, options: nil)[0] as UITableViewCell
*/
//// THEN, UPDATE YOUR CELL with YOUR DATAs
/* I don't understand what are you doing with this line of code:
cell = blucell
*/
devname.text = peri[indexPath.row]
devstrength.text = signalstrength[indexPath.row]
//Seem that bluetoothlog is the tableview, you should call it in viewdidload or viewwillappear()
bluetoothlog.backgroundColor = UIColor.clearColor()
return cell
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell :devicedet = tableView.dequeueReusableCellWithIdentifier("discovered") as UITableViewCell!
bluetoothlog.registerNib(UINib(nibName: "devicedet", bundle: nil), forCellReuseIdentifier: "discovered")
//NSBundle.mainBundle().loadNibNamed("devicedet", owner: nil, options: nil)[0] as UITableViewCell
cell = blucell
devname.text = peri[indexPath.row]
devstrength.text = signalstrength[indexPath.row]
bluetoothlog.backgroundColor = UIColor.clearColor()
return cell
}
In viewDidLoad register XIB as shown below
var userDetailsNIB = UINib(nibName: "UserDetailsCell", bundle: nil)
viewListingTable.registerNib(userDetailsNIB, forCellReuseIdentifier: "UserDetailsViewCellID")
And in func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
function use it by access it with Identifier which created in viewDidLoad method
let cell = tableView.dequeueReusableCellWithIdentifier("UserDetailsViewCellID") as UserDetailsViewCell
return cell
if you would like to Load custom cell(Xib) in UITableView in swift ,you may use the following code.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count; //This function returns number of rows in table view
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:YourTableViewCell! = tableView.dequeueReusableCellWithIdentifier("YourTableViewCell", forIndexPath:indexPath)as! YourTableViewCell
// Do additional code here
}
Don’t forget to register nib in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// registering your nib
let yourNibName = UINib(nibName: "YourTableViewCell", bundle: nil)
tableView.registerNib(yourNibName, forCellReuseIdentifier: "YourTableViewCell")
// Do any additional setup after loading the view.
}
Register xib Cell in Swift
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "XibCell", bundle: nil), forCellReuseIdentifier: "Cell")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! UITableViewCell
return cell
}
My code:
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(
UINib(nibName: "XibCell", bundle: nil),
forCellReuseIdentifier: "Cell"
)
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
return cell as! UITableViewCell
}
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//MARK:- Sample Data Array to load in TableView
let sampleArrray: \[String\] = \["val1", "val2", "val3", "val4", "val5"]
//MARK:- Cell reuse identifier
let cellReuseIdentifier = "cell"
//MARK:- UITableView outlet
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Registering the custom cell
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
// Setting delegate and Datasourse as same viewcontroller (this code is not neccessory if we are setting it from storyboard)
tableView.delegate = self
tableView.dataSource = self
}
//UITableview required methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sampleArrray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as UITableViewCell!
cell.textLabel?.text = sampleArrray\[indexPath.row\]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("clicked at: \(indexPath.row)")
//Here u can get the selected cell and add action related to this cell seelction
}
Added Screenshot for setting Datasource and delegate from storyboard
Your fuction registerNib , you try to write in viewDidLoad() method and remove from cellForRowAtIndexPah and then test your project
Your Cell Instance should be of CustomCell instance and not of UITableView Cell.
var cell :UITableViewCell should be replaced by
var cell :devicedet