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)
}
Related
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
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.
Following up to the excellent write up of a ViewController with a single tableView, I'd like to extend the question to having 2 separate tableViews and custom cells belonging to each one independently.
At the moment, I have the following skeleton, which is semi-working, and I am sure there is a more elegant and less naive approach to solving this.
after the viewDidLoad()
vInfoTV.dataSource = self
vInfoTV.delegate = self
vInfoTV.tag = Int.min
vAppTV.dataSource = self
vAppTV.delegate = self
vAppTV.tag = Int.max
numberOfRowsInSection function
func tableView(_ tableView: UITableView, numberOfRowsInSection section:Int) -> Int {
if tableView.tag == Int.min {
return mydata.cats.count
} else {
return mydata.dogs.count
}
}
Is it appropriate to set the tableView's tags as I do here, and switch on them based on tag value?
Cells in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == Int.min {
// Cat Table View
if indexPath.row == 0 {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "firstCustomCell")
//set the data here
return cell
}
else if indexPath.row == 1 {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "secondCustomCell")
//set the data here
return cell
}
else {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "thirdCustomCell")
//set the data here
return cell
}
} else {
// Dog Table View
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "poodleCell", for: indexPath) as! NewApplicationViewCell
cell.typeL.text = Dogs[(indexPath as NSIndexPath).row].type
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "mastifCell", for: indexPath) as! InitialDMVInspectionTableView
cell.typeL.text = Dogs[(indexPath as NSIndexPath).row].type
return cell
}
}
What is the best way to now hide a tableView, which has no cells (ie no data)?
Is this the right way to do this? All comments welcome!
THANK YOU!
Is it appropriate to set the tableView's tags as I do here, and switch on them based on tag value?
yes, you did exactly how I would do. Set the tag of your tables' differently. However I would not say Int.min or Int.max, rather I would want to know instantly what did I set as the tableviews' tag. So, I would just pick a number like 99 and 100. the reason I would not pick 0 and 1 is by default any object's tag is 0. So, if I put 99, I would be just keeping myself safe saying that even if someone comes and drag another table view inside my view, it will still not conflict with the ones before.
What is the best way to now hide a tableView, which has no cells (ie no data)?
Your tableview will not show up if you don't have data as in your numberOfRowsInSection, you set the row number to be the number of data in your desired data array.
I'm trying to display an icon for each PFTableViewCell based on the imageView property which doesn't work. The code I'm using is below which throws the follow error, "fatal error: unexpectedly found nil while unwrapping an Optional value".
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! i!
if cell == nil {
cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
cell.textLabel?.text = "Some Label"
cell.detailTextLabel?.text = "Another label"
cell.imageView.image = UIImage(named: "icon.png")
return cell
}
I had the same issue. Your crash is linked to the imageView being nil.
I guess this is a bug with PFTableViewCell: it does not "see" the standard imageView from the "Basic" style, and cannot create the corresponding PFImageView (I opened an issue with ParseUI).
Generally speaking, it's better to set your line like so:
cell.imageview?.image
then you won't have the crash.
You won't have the image either, but that's because of Parse's PFTableViewCell ...
If you want your code to work, you need to create a custom cell, with an imageView that you need to set as a PFImageView. This way, there will indeed be an imageView, and the whole PFTableViewCell file loading works perfectly.
I have a tableView with 2 sections, the first section is for text the user inputed earlier, the other section for selections based on that text. The first section has a Default tableViewCell style, the second section has a style of Subtitle. The first section is just a single cell, and it sizes dynamically based on the amount of text without issue. The second section is multiple cells, with UITableViewCell.textLabel and UITableViewCell.detailText set. These are the cells that do not auto size properly at all, I don't know what I am doing wrong. Note: 1) I do have tableView.rowHeight = UITableViewAutomaticDimension set in the viewDidLoad() method. 2) I am not using prototype cells in the storyboard.
This article states that I "must have constraints on the contentView". I honestly have no idea what that means. I know what constraints are in terms of setting content on the storyboard. I just don't know what he means in this context or how I would go about that if I don't have prototype cells.
Also, I have to set two re-use identifiers, depending on which section it is. That way it doesn't try to reuse the cell/section I have set aside for the user's input text.
With all that said in mind, here's the code I have. I'm a newbie to Swift and developing for iOS in general so if you have suggestions/advice for refactoring feel free to let me know. I have commented out some things I have tried. Setting the row height to 66 does work, but that's not the goal here. I want it dynamic because I don't know what will change later on.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cellIdentifier = ""
if indexPath.section == 1 {
//tableView.rowHeight = 66
//tableView.rowHeight = UITableViewAutomaticDimension
cellIdentifier = "DistortionItem"
} else {
//tableView.rowHeight = 160
cellIdentifier = "NegativeThought"
}
var cell: UITableViewCell! = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell
if indexPath.section == 1 {
if cell == nil {
cell = UITableViewCell(style: .Subtitle, reuseIdentifier: cellIdentifier)
}
cell.textLabel?.text = distortionslist.distortions[indexPath.row].0
cell.detailTextLabel?.font = UIFont.systemFontOfSize(10)
cell.detailTextLabel!.text = distortionslist.distortions[indexPath.row].1
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
//tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
//cell.textLabel?.sizeToFit()
//cell.detailTextLabel?.sizeToFit()
} else {
if cell == nil {
//println("Cell set to default")
cell = UITableViewCell(style: .Default, reuseIdentifier: cellIdentifier)
}
cell.textLabel?.font = UIFont.systemFontOfSize(12)
cell.textLabel?.text = entry.thoughtText
cell.textLabel?.numberOfLines = 0
//cell.textLabel?.sizeToFit()
}
return cell
}
Example screenshot: