Tableview cell subtitles not showing or the app quits - ios

My tableview cell subtitles aren't showing when I use this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView.tag == 1 {
guard let latestCell = tableView.dequeueReusableCell(withIdentifier: "latestCell") else {
return UITableViewCell(style: .subtitle, reuseIdentifier: "latestCell")
}
latestCell.textLabel?.text = latest[indexPath.row]
latestCell.detailTextLabel?.text = latestSub[indexPath.row]
latestCell.accessoryType = .disclosureIndicator
return latestCell
}
}
But then if I use this:
else if tableView.tag == 2 {
let olderCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "olderCell")
olderCell.textLabel?.text = older[indexPath.row]
olderCell.detailTextLabel?.text = olderSub[indexPath.row]
olderCell.accessoryType = .disclosureIndicator
return olderCell
}
else {
return cell!
}
}
The subtitles load perfectly, but after I close the app and reload the view, the app autoquits without giving a crash log or taking me to the debugging-tab.
I know that the arrays from which the data comes from are fine, and I think that I've set up everything right in the storyboard. A lot of similar questions have already been posted on the subject, but they all seem to come down to forgetting to set the cellStyle to .subtitle. Thanks in advance for any help I get!
BTW. My regular cell titles are working just like I want them to. No problem there.

In your first section, your guard statement is returning before you've set the cell's text and detail text. If you changed it to:
if let latestCell = tableView.dequeueReusableCell(withIdentifier: "latestCell") {
cell = latestCell
} else {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "latestCell")
}
cell.textLabel?.text = latest[indexPath.row]
cell.detailTextLabel?.text = latestSub[indexPath.row]
cell.accessoryType = .disclosureIndicator
return cell
The labels will now get set.
And your crash is caused by this:
return cell!
If cell == nil, then cell! will attempt to unwrap it. And really, what you should be doing is calling super's implementation:
return super.tableView(tableView, cellForRowAt: indexPath)
Good luck.

Related

swift Non-void function should return a value in a guard let

guard let cell = tableView
.dequeueReusableCell(withIdentifier: cellIdentifier) as? FolderAndFileCell else {
print("some")
return
}
It says that
Non-void function should return a value
What should I return here?
Inside cellForRowAt you have to
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? FolderAndFileCell else {
print("some")
return UITableViewCell()
}
This signature
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
should has a non void return value
The well-know approach is
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! FolderAndFileCell
This is an instance where you should be doing this instead.
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! FolderAndFileCell
You shouldn't need to use guard because I assume you are wanting to set the cell to FolderAndFileCell. If you can't set the cell to that, and go ahead and return a UITableViewCell() you are going to just get an empty cell and not know the reasoning.
I suggest you force cast to FolderAndFileCell and deal with the error if it presents then simply returning an empty cell if an error setting the cell is present.

tableView cell.detailTextLabel.text returns nil

I try to set a string to my detailTextLabel in a tableView but it's returning nil. I have read other posts where I am not the first one but I cannot understand what is going wrong in my case. I am using Swift 4.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") else {
return UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "Cell")
}
return cell
}()
let filtersRow: Bool = (currentSearchType == .all && indexPath.section == 0)
var titleText: String = ""
if filtersRow == true {
titleText = "Filters"
var detailText: String = ""
if currentFilters.count == 0 {
detailText = "None"
}
else if currentFilters.count == 1 {
detailText = currentFilters.first!
}
else {
detailText = "\(currentFilters.count)"
}
cell.textLabel?.text = titleText /// -> shows 'Filters' as expected
cell.detailTextLabel?.text = detailText /// -> shows nothing
print("Detail text: \(cell.detailTextLabel?.text)") --> returns nil
print("cell.textLabel? \(String(describing: cell.textLabel))") /// --> Optional(<UITAbleViewLabel...>)
print("cell.detailTextLabel? \(String(describing: cell.detailTextLabel))") /// ---> nil
cell.accessoryType = .disclosureIndicator
cell.accessoryType = .disclosureIndicator
return cell
}
...
There is definitely something wrong with the way I get my cell, but I do the same thing in an other viewController and it is going well...
Does anyone would have an idea?
This happens when the detailTextLabel isnt created. Mostly a bug in your code or storyboard. So check the creation of the problematic Cell.
Read also this Stackoverflow Q&A about this topic

Could not cast value of type 'UITableViewCell' to 'ProjectName.UserCell'

Hey guys befor you mark this as a duplicate just please hear me out. I have tried everything related to this topic including adding
self.tableView.registerClass(UserCell.self, forCellReuseIdentifier: "cell")
I have also changed my placeholder cell to match the UserCell class
Im Not sure what it could be! I get the error :
Could not cast value of type 'UITableViewCell' (0x1134700e0) to 'Lightning_Chat.UserCell'
with type SIGBRT not sure what could be going on and ive tried everything please help!
here is table view code :
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
// not casting for some reason
let cell : UserCell = UITableViewCell(style: .subtitle , reuseIdentifier: "cellId") as! UserCell
cell.textLabel?.text = contacts[indexPath.row].userName
cell.detailTextLabel?.text = contacts[indexPath.row].score
if let profileImageUrl = contacts[indexPath.row].picURL {
let url = URL(string: profileImageUrl)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
//download hit an error
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
cell.profileImageView.image = UIImage(data: data!)
}
}).resume()
}
return cell;
}
here is my view did load :
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UserCell.self, forCellReuseIdentifier: "cellId")
//irrelevant
fetchUsers()
}
Instead of:
let cell : UserCell = UITableViewCell(style: .subtitle , reuseIdentifier: "cellId") as! UserCell
Try this:
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! UserCell
You are not creating the cell correctly.
Change:
let cell : UserCell = UITableViewCell(style: .subtitle , reuseIdentifier: "cellId") as! UserCell
to:
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! UserCell
Note that your code is directly creating a UITableViewCell which is why you can't cast it. You are bypassing the standard cell reuse as well.
Here are the step to fix your problem
Change your table view content to be dynamic cell, instead of static cell
Maintain the code on your viewDidLoad function self.tableView.register(UserCell.self, forCellReuseIdentifier: "cellId"). Also don't forget to connect table view delegate and datasource
On your table view cellForRowAt index path method, don't instantiate the cell directly. Instead use let cell = self.tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!;
Basically that error you got, is because of mix and match the wrong properties on your table view.
Hope it helps

Swift 3 - Problems in reusing cell with multiple custom cells

I've got problems when I scroll down in my UITableview. The table shows me cells with old content when the cell is reused.
The Probleme is the following:
Swift wants to reuse an old cell, but doesn't properly clear the old content from the old cell. This leads to cells with old content, although I'm providing new data to the cells.
Architecture of the UITableView if the following:
Each custom cell has their own identifier
Each custom cell is separated in an own class
Screenshots of the problem:
Beginning of the Questionnaire Screen Shot:
The scrolled down table:
The problem here is the "Handedness"-Cell which is showing the cell number 3 (because of the reuse of the cell), which is not right
The numberOfSection-Method
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
The numberOfRowsInSection-Method
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(section == 0){
return questionnaireStructure.count
} else {
return 1
}
}
The cellForRowAt-Method
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// first section is the normal Questionnaire
if(indexPath.section == 0){
// current questionnaireStructure
let questStruct:QuestionnaireStructure? = questionnaireStructure[indexPath.row]
// current cell is a "Headline"
if(questStruct?.elementtype == "elements/headlines"){
let cell = tableView.dequeueReusableCell(withIdentifier: "HeadlineStructureCellID", for: indexPath) as! Headline
cell.headline.text = questStruct?.headline
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else if(questStruct?.elementtype == "elements/texts"){
// current cell is a "texts"
let cell = tableView.dequeueReusableCell(withIdentifier: "TextsStructureCellID", for: indexPath) as! Texts
cell.textsLabel.text = questStruct?.text
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else if(questStruct?.questiontype == "Slider"){
// currrent cell is a "slider-Question"
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionSliderStructureCellID", for: indexPath) as! Slider
cell.sliderQuestion.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
let values = (questStruct?.values)!
let valueArray = values.array as! [Values]
cell.slider.minimumValue = Float(valueArray[0].min)
cell.slider.maximumValue = Float(valueArray[0].max)
let answers = (questStruct?.answers)!
let answerArray = answers.array as! [Answers]
cell.minLabel.text = answerArray[0].label
cell.maxLabel.text = answerArray[1].label
return cell
} else if(questStruct?.questiontype == "SingleChoice"){
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionSingleChoiceStructureCellID", for: indexPath) as! SingleChoiceCell
let radioButtonController = SSRadioButtonsController()
radioButtonController.delegate = self
radioButtonController.shouldLetDeSelect = true
cell.radioButtonController = radioButtonController
cell.updateCellData(questStruct: questStruct!, indexInTable: indexPath.row)
return cell
} else if(questStruct?.questiontype == "MultipleChoice"){
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionMultipleChoiceStructureCellID", for: indexPath) as! MultipleChoiceCell
cell.multQuestionLabel.text = questStruct?.question
cell.questStruct = questStruct
return cell
} else if(questStruct?.questiontype == "YesNoSwitch"){
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionYesNoSwitchStructureCellID", for: indexPath) as! YesNoSwitch
cell.yesNoQuestion.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else if(questStruct?.questiontype == "TextDate"){
let cell = tableView.dequeueReusableCell(withIdentifier: "Datepicker", for: indexPath) as! DatePicker
cell.question.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionSingleChoiceStructureCellID", for: indexPath) as! SingleChoiceCell
//cell.singleChoiceLabel.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
} else {
//last section is the save button
// show the save button when the Questionnaire is loaded
if(questionnaireStructure.count != 0){
let cell = tableView.dequeueReusableCell(withIdentifier: "SaveStructureCellID", for: indexPath) as! SaveQuestionnaire
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "TextsStructureCellID", for: indexPath) as! Texts
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
}
}
What I checked:
the data of "questStruct" is providing the latest data
overriding the "prepareForReuse"-Methode without success
Here:
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionSingleChoiceStructureCellID", for: indexPath) as! SingleChoiceCell
//cell.singleChoiceLabel.text = questStruct?.question
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
You need to "reset" the cell in case it's being reused. Options are:
write a reset() function in the cell, to clear any assigned data and display "default" content, or
create an empty questStruct and call cell.updateCellData(questStruct: questStruct!, indexInTable: indexPath.row)
Option 1. is probably the easiest and most straight-forward.
Are you sure the data isn't actually duplicated in the questStruct array? If that's not the case then all I can think is that it looks like you have two places where a single choice cell is used. In one of them you set a bunch of data, while in the other one you don't seem to set any data. I'm talking about that last else statement where you have the part where you set singleChoiceLabel.text except it's commented out. If that condition gets hit and it's reusing a cell that was configured for the other singleChoiceStructure branch of the if condition then the information will still be filled out from the previous configuration. It's possible the questionType property of one of your QuestionnaireStructure objects is either spelled incorrectly or just a value you haven't accounted for, which is causing the if statement to hit the else which returns an unconfigured QuestionSingleChoice cell that might still have information from the last time it was used.

Tableview crashes when reloading data

I've unexpected problems with tableviews. It seems that the app quits without a error every time I try to reload my tableviews' data. I know that the array is formed correctly, so there's something wrong with these functions:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1 {
return latest.count
}
else if tableView.tag == 2{
return older.count
}
else {
return 0 //In case of error
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView.tag == 1 {
print("Success")
cell = tableView.dequeueReusableCell(withIdentifier: "latestCell")! as UITableViewCell
cell = UITableViewCell(style: UITableViewCellStyle.subtitle,
reuseIdentifier: "latestCell")
cell?.textLabel?.text = latest[indexPath.row]
cell?.detailTextLabel?.text = latestSub[indexPath.row]
cell?.accessoryType = .disclosureIndicator
return cell!
}
else if tableView.tag == 2 {
cell = tableView.dequeueReusableCell(withIdentifier: "olderCell")! as UITableViewCell
cell = UITableViewCell(style: UITableViewCellStyle.subtitle,
reuseIdentifier: "olderCell")
cell?.textLabel?.text = older[indexPath.row]
cell?.detailTextLabel?.text = olderSub[indexPath.row]
cell?.accessoryType = .disclosureIndicator
return cell!
}
else {
return cell!
}
}
The answers before to these type of questions here were about forgetting to set delegates and datasource and so... So I believe this is an appropriate question.
Thanks in advance!
EDIT:
var latest = [String]()
var older = [String]()
var latestSub = [String]()
var olderSub = [String]()
override func viewDidAppear(_ animated: Bool) {
latestTable.reloadData()
olderTable.reloadData()
}
The full log;
2017-02-16 15:57:28.889 NotebookApp[24985:604704] Firebase automatic screen reporting is enabled. Call +[FIRAnalytics setScreenName:setScreenClass:] to set the screen name or override the default screen class name. To disable automatic screen reporting, set the flag FirebaseAutomaticScreenReportingEnabled to NO in the Info.plist
2017-02-16 15:57:28.997 NotebookApp[24985:] Firebase Analytics v.3600000 started
2017-02-16 15:57:28.999 NotebookApp[24985:] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled
2017-02-16 15:57:29.012: FIRInstanceID AppDelegate proxy enabled, will swizzle app delegate remote notification handlers. To disable add "FirebaseAppDelegateProxyEnabled" to your Info.plist and set it to NO
2017-02-16 15:57:29.020 NotebookApp[24985:] Successfully created Firebase Analytics App Delegate Proxy automatically. To disable the proxy, set the flag FirebaseAppDelegateProxyEnabled to NO in the Info.plist
2017-02-16 15:57:29.090 NotebookApp[24985:] The AdSupport Framework is not currently linked. Some features will not function properly.
2017-02-16 15:57:29.180 NotebookApp[24985:] Firebase Analytics enabled
2017-02-16 15:57:45.703779 NotebookApp[24985:604704] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"",
""
)
Will attempt to recover by breaking constraint
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in may also be helpful.
2017-02-16 16:01:34.316879 NotebookApp[24985:604704] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /Users/tuomasnummela/Library/Developer/CoreSimulator/Devices/AA87179A-11E5-4A3A-A63F-B785AA71EC95/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2017-02-16 16:01:34.317476 NotebookApp[24985:604704] [MC] Reading from private effective user settings.
I kind of didn't think that there would be anything useful here because when the app quits it doesn't take me to the error-page-thing and highlight any code.
What my project looks like after the app has forcedly closed itself.
The things that can go wrong here is it can't find the cell with the reuse identifier that you are passing in the below line
cell = tableView.dequeueReusableCell(withIdentifier: "olderCell")! as UITableViewCell
All you have to do is register the cell with reuse identifier in your viewDidLoad
<olderTableView>.registerClass(UITableViewCell.self, forCellReuseIdentifier: "olderCell")
<latestTableView>.registerClass(UITableViewCell.self, forCellReuseIdentifier: "latestCell")
or
you can simply replace the dequeue and initialization lines with the following
if tableView.tag == 1 {
let reuseId = "latestCell"
let latestCell = tableView.dequeueReusableCell(withIdentifier: reuseId) ?? UITableViewCell(style: .subtitle, reuseIdentifier: reuseId)
latestCell.textLabel?.text = latest[indexPath.row]
latestCell.detailTextLabel?.text = latestSub[indexPath.row]
latestCell.accessoryType = .disclosureIndicator
return latestCell
}
The first error is in the viewDidAppear you must call the super.viewDidAppear(animated), the tableview reload will never fired.
You have a number of errors in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell. The crash is likely from force unwrapping cell which was nil in the last line.
Here is my stab at cleaning it up. Be sure to register you UITableViewCell subclass with tableView.register(SubtitleCell.self, forCellReuseIdentifier:"latestCell"). Unless there is other code not visible, you can use the same identifier for latestCell and olderCell - they don't seem to be different from the code you've posted
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell //can't be optional
if tableView.tag == 1 {
print("Success")
cell = tableView.dequeueReusableCell(withIdentifier: "latestCell", forIndexPath: indexPath) as! SubtitleCell
//cell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "latestCell") //don't call this here, make a subclass of UITableViewCell to set style
cell.textLabel?.text = latest[indexPath.row]
cell.detailTextLabel?.text = latestSub[indexPath.row]
cell.accessoryType = .disclosureIndicator
return cell
}
else if tableView.tag == 2 {
//same comments as above
cell = tableView.dequeueReusableCell(withIdentifier: "olderCell", for: indexPath) as! SubtitleCell
cell.textLabel?.text = older[indexPath.row]
cell.detailTextLabel?.text = olderSub[indexPath.row]
cell.accessoryType = .disclosureIndicator
return cell
}
else {
cell = tableView.dequeueReusableCell(withIdentifier: "latestCell", forIndexPath: indexPath)
return cell //you were returning nil here - need to return a cell
}
}
Here is an example of how to init the correct cell style
class SubtitleCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
}

Resources