I have UITableView and the contect is Dynamic Prototype.
so I have 5 cells and each cell has it own identifier.
so when I try to return the value in (cellForRowAt)
it would let me.
would please help with that ?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section) == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as UITableViewCell!
// cell?.textLabel!.text = objectsArray[indexPath.section].sectionObjects[indexPath.row]
return cell!
}
else if (indexPath.section) == 2 {
let cell3 = tableView.dequeueReusableCell(withIdentifier: "cellThree") as UITableViewCell!
return cell3!
}
else if (indexPath.section) == 3 {
let cell4 = tableView.dequeueReusableCell(withIdentifier: "cellFour") as UITableViewCell!
return cell4!
}
else if (indexPath.section) == 4 {
let cell5 = tableView.dequeueReusableCell(withIdentifier: "cellFive") as UITableViewCell!
return cell5!
}
return cell!
}
Thanks !
New Update :-
so the error that is showing to me is (Use of unresolved identifier 'cell')
so when I return at the end (return cell!) it shows this error.However, if I deleted that line it shows me another error asking me to return a value
so bottomline I'm confised what value should I return at the end of (cellForRowAt).
Return values
This method can only return one cell each time it's called. The method will be called each time a new cell is about to appear on the screen. Take a look at the method signature:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
That UITableViewCell at the end is the expected return value and in this case it's a single cell.
The error
Let's take a look at your error:
Use of unresolved identifier 'cell'
"Unresolved identifier" means that it has no idea what "cell" is as it has not been declared in the current scope. But don't you declare cell in this method? Yes you do, but they are in a separate scope. Variable scope defines how long a variable lives and where it can be seen. You can tell when a new variable scope is declared when you see a new set of curly brackets {}, which each of your if statements declare. Variables declared within each set of curly brackets can only be seen by code contained within those brackets. Once execution leaves those brackets, those variables are gone. Let's take a look at what variables are viewable in the scope of your final return statement by removing the scopes created by your if statements:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return cell!
}
Now it is clear that cell doesn't exist at the point of the return. You'll need to declare cell in the method's scope and give it some value. Please note that force unwrapping your cell optionals will likely lead to a crash at runtime as the method must not return nil. You'll want to create cells when tableView.dequeueReusableCell() returns nil.
EDIT: I just realized you aren't using the for indexPath parameter in dequeueReusableCellWithIdentifier. Use that, otherwise it's the wrong one. See:
https://developer.apple.com/documentation/uikit/uitableview/1614878-dequeuereusablecellwithidentifie
https://www.natashatherobot.com/ios-using-the-wrong-dequeuereusablecellwithidentifier/
#Nawaf you can't return multiple values from any function, hence you can't return multiple cells from one call of cellForRow. You may be misunderstanding how cellForRowAtIndexPath works. Right now your code is not populating anything in the cells.
Use of unresolved identifier 'cell'
Furthermore, your error can be occurring for multiple reasons. For one, check that your view controller is correctly assigned to the one in storyboard and that you have assigned a reuseIdentifier in storyboard.
See the links for more info:
https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614861-tableview?language=objc
How does cellForRowAtIndexPath work?
Also a side note for code quality: don't force cast to the cell because that can cause unintended errors. Use conditional binding instead:
guard let cell = tableView.dequeueReusableCellWithIdentifier(...) as? UITableViewCell else {
// What to do when the cast failed
}
Good luck :)
Related
I wonder if anyone can offer any guidance? I am writing an iPhone app, using Xcode 13.2.1. I am displaying a tableview within a scene that uses XIBs. It works fine. Above the table I have a header that is being displayed, it too works fine.
However, what I'd like to do is display the header, then display a cell that doesn't use the XIB (and that is a height of 50), and then displays every other cell after that first cell using a XIB (height is 195 - just an FYI). Thus, to do/implement this what I am trying to do is implement some kind of 'if statement' such that if indexPath.row is 0 then set the cell type to <call it cell type 1>, and if the indexPath.row is not 0 then set the cell type to <call it cell type 2>. I don't believe that I can use an IF statement because later in the code block it won't recognise the value of cell because it would have been set in an IF statement. Hence, I think I need to use a turnery operator, however I am struggling to construct the turnery operator.
The current code that sets up the cell for XIB in the
// MARK: TableView CELL Information
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Current code that sets up the cell for a XIB template
guard let cell: CustomTableViewCellTypeA = self.tableView.dequeueReusableCell(withIdentifier: "customCell") as? CustomTableViewCellTypeA else {
os_log("Dequeued cell isn't an instance of CustomTableViewCellTypeA", log: .default, type: .debug)
fatalError()
}
I KNOW THE FOLLOWING CODE DOESN'T WORK - however I am showing it this way to try and explain what I am trying to achieve:
// Intent is to use an IF or turnery operator to set the correct cell type
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
} else {
// Current code that sets up the cell for a XIB template
guard let cell: CustomTableViewCellTypeA = self.tableView.dequeueReusableCell(withIdentifier: "customCell") as? CustomTableViewCellTypeA else {
os_log("Dequeued cell isn't an instance of CustomTableViewCellTypeA", log: .default, type: .debug)
fatalError()
}
}
The follow on code (if I can somehow get the above to work) would mean that I would display some information in the first cell which would be <call it cell type 1> and then display other information in <call it cell type 2>.
Anyone done this before or would have any guidance on how to create such a turnery operator? I have tried many things but can't seem to manage to find the solution.
Cheers James.
Actually this was much easier to solve than trying to add complexity of turnery operators to determine which cell to dequeue. It was simply a case of using an IF and adding in the code I wanted to execute along with ensuring I put a return cell statement in it, meaning that if the IF-statement wasn't executed then the code executes the other dequeue statement... Thus, the code looks like this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row < 1 {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Select/tap on an event record below to edit the details of the journey."
cell.textLabel?.textAlignment = .center
cell.textLabel?.numberOfLines = 0
cell.backgroundColor = .orange
return cell
}
guard let cell: CustomTableViewCellTypeA = self.tableView.dequeueReusableCell(withIdentifier: "customCell") as? CustomTableViewCellTypeA else {
os_log("Dequeued cell isn't an instance of CustomTableViewCellTypeA", log: .default, type: .debug)
fatalError()
}
This gave me the result I needed. Also, the feedback from Shawn Frank above helped me realise I hadn't registered the first cell type (only the second one), thus when I registered both the first and second cell types within the class it all worked beautifully. Thank you to all who looked at the question and the fine folks above who gave guidance. Cheers James.
I have a UITableView and a custom TableViewCell class, I am setting a non-null value in viewDidLoad method, but while setting the value to UITableViewCell in cellForRow method , the value magically becomes nil. I am unable to access the set variable from my custom UITableViewCell
Here is the snippet of code. Print statement in ViewDidLoad prints the value while the one in cellForRowAtIndexPath returns nil
override func viewDidLoad() {
super.viewDidLoad()
Docco360Util.shared().getDoctorsWithResultBlock { (doctorObjects, error) in
if let err = error{
print(err)
}else{
self.doctors=doctorObjects
print(doctorObjects![1].professionalHeader)//output is printed here
self.doctorTableView.reloadData()
}
}
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifierForUsers = "DoctorTableViewCell"
var cell = tableView.dequeueReusableCell(withIdentifier: identifierForUsers, for: indexPath) as! DoctorTableViewCell
if cell == nil{
cell=DoctorTableViewCell(style: .default, reuseIdentifier: identifierForUsers)
}
print(self.doctors[indexPath.row].professionalHeader)//output in nil here
cell.doctor=self.doctors[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DoctorTableViewCell", for: indexPath) as! DoctorTableViewCell
print(self.doctors[indexPath.row].professionalHeader)
cell.doctor = self.doctors[indexPath.row]
return cell
}
First, you should ensure that the closure of Docco360Util.shared().getDoctorsWithResultBlock is executed in the main (UI) thread. If not, you should put it into a DispatchQueue.main.async block.
Then, you should verify that the result block is executed (e.g. the print statement prints something). Btw, why do you call print(doctorObjects![1].professionalHeader) with index 1 and not 0? In tableView(tableView:cellForRowAt:) you element with index 0 - maybe this doctor is nil?
You could start by setting a breakpoint to your property declaration, a watchpoint, or implement a property observer (willSet) and add a breakpoint her, to check if somebody is messing up with your variables.
If all this does not help, you might want to post more code, expecially all lines of code where you write your self.doctors array.
I could finally fix it. The problem was in declaration of variables. I was using Objective-C header file for declarations. While declaring I declared them as "weak" instead of "retain" and that was causing the problem.
I am getting following errors:
1) Non-optional expression of type 'UITableViewCell' used in a check for optionals
2) Value of type 'UITableViewCell' has no member 'congigureCell'
Please
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell:UITableViewCell = countryList.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell // Error 1 happens here {
let text: String!
if inSearchMode {
text = filteredCountriesList[indexPath.row]
} else {
text = countriesList[indexPath.row]
}
cell.congigureCell(text: text) // Error 2 happens here
return cell
} else {
return UITableViewCell()
}
}
1) The ! mark at the end of
countryList.dequeueReusableCell(withIdentifier: "cell")!
uses force unwrap to make it non-optional, so you shouldn't check it inside if let, or even better way is to just remove ! mark
2) congigureCell probably the method of different class, not UITableViewCell. You should substitude UITableViewCell by this class to cast it
Make sure you have done following steps.
Add cell identifier in storyboard to your custom cell. i.e "cell"
Assign delegate and datasource of your YourTableview to YOURViewController.swift via storyboard or in code.
In YOURViewController.swift access cell using datasource of table
view as.
Add a custom class of sub class UITableViewCell and assign it to
tour cell in storyboard.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = countryList.dequeueReusableCell(withIdentifier: "cell") as! YOURTableViewCellClass {
let text: String!
if inSearchMode {
text = filteredCountriesList[indexPath.row]
} else {
text = countriesList[indexPath.row]
}
cell.congigureCell(text: text) // Error 2 happens here
return cell }
The ! mark is uses to force unwrap the optional value that can be nil. But "if let" and "guard let" has been check for optionals, so you don't need ! mark.
Just use
if let cell:UITableViewCell = countryList.dequeueReusableCell(withIdentifier: "cell") as UITableViewCell
cell in this line is 'UITableViewCell', but congigureCell is not a member of UITableViewCell.
If you want to use your own cell(like MyCell), you should convert it to MyCell.
let myCell = cell as! MyCell
1 .Instead of dequeueReusableCellWithIdentifier: use dequeueReusableCellWithIdentifier:forIndexPath: the later one never provides a nil value so you dont need to worry about the 'nil error'.
2.UItableViewCell dont have configure cell or congigureCell as in your case instead you have to create a custom tableViewCell and add function as configureCell() and then in this line
if let cell:UITableViewCell = countryList.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
replace as UITableViewCell as as yourCustomTableViewCellClass
I'm implementing logic for returning specific cells in a table view call. What suggestions exist for covering the "default" case, if I'm using a switch statement?
See example here:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.section {
case 0: // CompanyDetailsGeneralTableViewCell
let cell = tableView.dequeueReusableCellWithIdentifier("CompanyDetailsGeneral", forIndexPath: indexPath) as! CompanyDetailsGeneralTableViewCell
// STUFF FOR CompanyDetailsGeneralTableViewCell
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("CompanyResources", forIndexPath: indexPath) as! CompanyResourcesTableViewCell
return cell
default:
// Can't reach here (never!) as table has only 2 sections.
return UITableViewCell() // Hack. BEST PRACTICE?
}
}
Among the approaches I've considered:
Not using the switch at all, but it seems more structurally fitting for a switch.
Throwing a runtime exception. Not possible as runtime exceptions are not exciting in Swift.
returning nil - Can't do that, as this function does not return an optional.
Would appreciate comments and feedback.
You should be able to call terminating functions like assertionFailure(), preconditionFailure() or fatalError("Unexpected section in TableView") in order to terminate the application in that scenario.
I do the same thing that you do. It may not display anything but at least the app won't crash which is important to users.
I'm using Tableview controller to make two prototype cells that app crash due to
thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)
pointed to >>> return cell! in the first cell
fatal error: unexpectedly found nil while unwrapping an Optional value
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 10
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (indexPath.section == 0) {
let cell = tableView.dequeueReusableCellWithIdentifier("one", forIndexPath: indexPath) as? one
cell?.textLabel?.text = "book"
return cell!
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("two", forIndexPath: indexPath) as! two
cell.textLabel?.text = "not a book"
return cell
}
If I may improve the code:
let cellID = (indexPath.section == 0) ? "one" : "two"
if let cell = tableView.dequeueReusableCellWithIdentifier(cellID, forIndexPath: indexPath)
as? UITableViewCell
cell.textLabel!.text = (cellID == "one") ? "book" : "not a book"
return cell
}
else {
assertionFailure("Unable to dequeue a cell of ID \(cellID)")
return nil
}
Then you'll get sensible error behavior. I'll bet that, even though you've named your classes one and two, that you have neglected to set their 'Identifier' within StoryBoard in the Attributes Inspector for each cell prototype.
Your answer is very wrong. You should not be creating lots of table cells. This is a misuse of tables. You should only use the ones on the reuse queue.
Use my version of the code, and set a breakpoint just after the if let cell. Analyze the object in the debugger and see what type it really is.
If instead you hit the assertionFailure, you still haven't really set the Identifier properly in StoryBoard. Show us a screen capture to convince us.
Looks like it's because the call
let cell = tableView.dequeueReusableCellWithIdentifier("two", forIndexPath: indexPath) as! two
is doing a forced down cast of the cell to type two, and the object returned is not of type two. Using ! here is basically saying:
"Dequeue a cell (returns type AnyObject) and then downcast it two type two. I know down casts can fail if the type doesn't match, but in this case I'm sure it will. No need to handle errors"
As casts can fail, the cast returns an optional. In this case the cell returned can't be downcast as the types don't match. Check the code that registers the identifier "two", and check you are registering the right type of class (A UITableViewCell subclass named two)
I think you should edit code :
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 2
}