detailTextLabel and imageView not accessible on table view cells - ios

I am using Xcode Version 8.2.1 (8C1002) - and I believe therefore Swift 3 - to try to make a table view with cells other than default, and can't seem to get the cell detailTextLabel or cell imageView to be other than nil.
I have set the Deployment Target of the project to 9.0 on Universal Devices.
In the interface builder, I have established Content as Dynamic Prototypes with 1 Prototype Cell.
In the interface builder, I have set that Table View Cell Style as “Right Detail” with an image “animal08.png” and an Identifier of “PlayerCell”.
In the view controller for the table view, in the override func viewDidLoad():
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "PlayerCell")
and
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell")!
print(cell.subviews.count)
cell.textLabel!.text = "Name: " + myNames[indexPath.row]
cell.detailTextLabel!.text = "Ranking: " + myRankings[indexPath.row].description
let avatarIndex = indexPath.row + 1
let avatarIndexName = "animal0" + avatarIndex.description
cell.imageView!.image = UIImage.init(named: avatarIndexName)
return cell;
}
When I run the program on an iPhone 5 iOS 5.2(13C75) simulator, when I get to the “cell.detailTextLabel!.text statement, I get:
“fatal error: unexpectedly found nil while unwrapping an Optional value”
Similarly, the attempt to access the imageView property finds nil.
Sure enough, the print(cell.subviews.count) statement reveals that there is only 1 subview of the cell.
I can see no set or get UITableViewCellStyle property of the UITableViewCell other than through the interface builder, or in the init(style: UITableViewCellStyle, reuseIdentifier: String?) initializer.
I believe using the init method defeats the benefits of dequeueReusableCell, and should not be used in this circumstance.
I have read (without substantiation) that the tableview.register method resets the tableViewCellStyle to default.
The above code seems to be identical to boilerplate code on the Apple Developer site and numerous tutorials by a wide range of contributors.
So, how can I access the detailTextLabel and imageView subviews and still use dequeued cells?

You should change the cell style to a value other than UITableViewCellStyleDefault. Unwrapping isn't necessary as well in this situation AFAIK.
Regarding the other stuff you try:
By the way printing cell.subviews.count won't give you any usable value as all subviews in UITableViewCell should be added (and default subviews are) inside its -contentView (cell property).
Also using cells initialised using -initWithStyle:reuseIdentifier: has no extra drawbacks as you may still use the reusing mechanism as well as specify the required cell style at the same time. Dequeuing actually has more sense when reusing Storyboard-defined cells with attributes specified in the Interface Builder.

Related

How to left/right-align custom UITableView cell

I’m coding a “chatbot” app, and to hold the message bubbles I have a UITableView and a custom message-bubble shaped cell. Here’s what it looks like so far:
All the cells will look the same, except I’d like every other cell to be, say, half the width of the table and alternating right/left aligned. How could I do this programmatically?
The better way - to create two classes InMessageCell, OutMessageCell, and add all properties (like aligning of all elements) hide inside of this cell. Both cell will have the same width, but all bubbles will be moved on one or other side. It may inheritance from the main class MessageCell, so all logic may stay in main class, but UI part - splitted up.
Two straightforward ways of achieving this by using custom table view cells:
Have two different cell classes. They can share much of their implementation (e.g. by class heritage), but differ in their layout (one is left aligned, one right aligned). For each row in your table, decide which cell you need to use, and dequeue the appropriate one.
Have one cell class whose layout can be toggled when it's being dequeued. (How exactly this toggle looks like depends of course on how you chose to layout your cell. It could e.g. be that you exchange the constant to an autolayout constraint you reference via #IBOutlet, the switching of a background image, or changing the alignment property of a stack view, among many others.)
Both ways depend on making a decision which cell flavor to use in your UITableViewDataSource's tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) function.
Here is a slightly abstract example using the first method. In your UITableViewDataSource:
enum ChatCellAlignment {
case left
case right
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellAlignment: ChatCellAlignment = self.cellAlignmentForIndexPath(indexPath) // you need to implement something like this
var identifier = "Default"
switch cellAlignment {
case .left:
identifier = "LeftAlignedBubbleCell"
case .right:
identifier = "RightAlignedBubbleCell"
}
let cell = self.tableView.dequeueReusableCell(withIdentifier: identifier)
if let cell = cell as? ChatBubbleCell { // assuming your custom cell classes all inherit from a "ChatBubbleCell"
cell.set(text: self.textForIndexPath(indexPath))
... // whatever setup you need to do on your custom cell
}
return cell
}
You can give the table view cell a value to know it. Then you can use autolayout (SnapKit) to make it align left or right

Can I use a single custom cell for multiple different cells?

I have created a single prototype cell which has two labels (mainLabel and subLabel) and an uiimageview. In the uitableview I'd like to have several cells which reuse the prototype and when needed the subLabel is hidden and the uiimageview is changed with different one or with a uiswitch. The two labels have different text for each cell. Do you have any suggestions/hints in order to do it? possibly in a mvvm architecture?
I'll describe what I am doing:
I have a struct (the Model) with two properties: label and sublabel. This is then instantiate by a viewModel which provides text for each cell, done by a method called getModel(_ indexPath: IndexPath) -> cellModel { ... }. Finally in UIViewController, in tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) { ... } I am calling getModel(), using dequeueReusableCell and setting up each cell.
In getModel() there is a huuuuge switch which I use to know which cell is which
Then in uitableviewcell I have some method that hides sublabel and changes uiimageview.
It kind of works, however I have some issues with while scrolling. For example, sometimes a uiimageview is drawn in another cell, or a subLabel is hidden, even if it is not supposed to. I guess this is due because it is reusing the cell, and I am not resetting it.
Anyway, any suggestions or ideas?
I know this is overkilling...
No need for any pattern. Yes, you can use that single cell design for all cells. Just hide/empty label(s) and image view as you like per cell.
First of all you have to set default value to both the labels and imageview
i.e. (consider a title label, a sub label and a imageview)
lblTitle.isHidden = false
lblSubLabel.isHidden = false
imgViewIcon.image = nil
Then just show labels in specific condition that you want to match and set image in imageview
i.e. (consider your condition to hide sub label)
if needToHide == true {
lblSubLabel.isHidden = true
}

Xcode (Swift) error - use of undeclared type 'CustomCell'

I've been learning table views from tutorials on YouTube. I was following every step told in the video, and did everything the same as the author of this tutorial, but for some reason I got a strange error - I've heard that it's about old version of Xcode used in the tutorial - I'm working on the latest one.
Debugger tells that "CustomCell" is undeclared.
Every help will be appreciated!
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayOfCwiczenia.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomCell = tableView.dequeueReusableCellWithIdentifier("Cell") as CustomCell
let person = arrayOfCwiczenia[indexPath.row]
cell.setCell(cwiczenia.nazwa, imageName: cwiczenia.obrazek)
return cell
}
CustomCell looks to be a subclass of UITableViewCell
Looks like they left out the part where you need to create it
Create a new file called CustomCell and make sure it's base class is UITableViewCell
You must have a file where you define the behaviour of the custom cell - if that's not called 'CustomCell' then it won't be declared.
Just make sure that you have the same name when you define the class and when you use it.
I would suggest looking at Apple's walkthrough on how to implement a table view. It has step by step instructions with pictures. If you scroll down the the Design Custom Table Cells section I think you will be able to see how to link the custom cell to a file properly
Your tutorial should have mentioned all the details, but here it goes...
You need to define a subclass of UITableViewCell named CustomCell:
import UIKit
class CustomCell: UITableViewCell
{
// Your custom properties/methods.outlets/etc.
}
(typically, in a source file named CustomCell.swift, but this is not compulsory)
If you are using a storyboard, you need to set the class of your prototype cells to "CustomCell" in the identity inspector (third tab from the left on the right inspector pane - the one with the icon that looks like a newspapaer):
Also, In the attributes inspector (fourth tab from the right, icon looks like a slider), set the cell's identifier (in the case of your code, "Cell"):
If you are not using a storyboard, you need instead to register the custom cell class and identifier programmatically:
func viewDidLoad()
{
tableView.registerClass(CustomCell.self, forCellReuseIdentifier: "Cell")
// other setup...
}
I had this same error. Tried cleaning and building which worked but the main issue seemed to just be saving the CustomCell which then becomes recognised by the compiler and removes the error.
Its not limited to cells I've had it with other custom classes before as well. Good one to know about though!

iOS tableview cell is empty at random

Screenshot of weird behavior
The screenshot tells is quite well. I have a tableview with dynamic custom cells. I added a println for one of the contents of the cell to check, if the labels are set. I can see in the debug log, that each cell has its content. Still, on the device there are empty cells at random, which means, the row, where no content appears, is changing a lot. Even just scrolling up and down makes the second row disappear, but the third row is filled. Scrolling again turns this around again. If I close the app and start it again, every row is filled correctly.
Here is the code for the cell generation:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Return a count picker cell
if countPickerTableRow == indexPath.row {
...
}
// Return a normal wish list entry cell
else {
let article = wishListEntries[indexPath.row]!
let cell = tableView.dequeueReusableCellWithIdentifier("ArticleCell", forIndexPath: indexPath) as! WOSArticleCell
// Correct the order in case a count picker cell was inserted
var row = indexPath.row
if countPickerTableRow != -1 && indexPath.row > countPickerTableRow {
row--
}
cell.setThePreviewImage(UIImage(data: article.thumbnail))
cell.setArticleName(article.name)
cell.setArticleDescription(article.text)
cell.setArticleNumber(article.number)
cell.setArticleCount(article.count as Int)
cell.setOrderInTable(row)
cell.setTableViewController(self)
cell.setNeedsDisplay()
cell.setInputAccessoryView(numberToolbar) // do it for every relevant textfield if there are more than one
println(String(indexPath.row) + " " + cell.nameLabel.text!)
return cell
}
}
In the custom cell class there is nothing special. Just a few outlets to the labels.
Here is a screen of the storyboard:
Storyboard
Can anyone please help me finding out what is going on here? I can't find the reason why the debug log can output the contents of a cell, but the device is not able to render them.
You should change the logic of your code. If the PickerCell comes up just call reloadData() and reload everything in the tableview. If the amount of rows you have is small this won’t be an issue and it’s not an expensive operation as you are not doing any heavy calculating during display.
If you need to update only a single cell because of changes you made in the PickerCell then you should be calling reloadRowsAtIndexPaths:withRowAnimation: with the indexPath of the cell to be updated.
Your issue is with your subclass WOSArticleCell. Have you implemented prepareForUse()? If you have, are you setting any properties to nil?
UITableViewCell Class Reference
Discussion
If a UITableViewCell object is reusable—that is, it has a reuse
identifier—this method is invoked just before the object is returned
from the UITableView method dequeueReusableCellWithIdentifier:. For
performance reasons, you should only reset attributes of the cell that
are not related to content, for example, alpha, editing, and selection
state. The table view's delegate in tableView:cellForRowAtIndexPath:
should always reset all content when reusing a cell. If the cell
object does not have an associated reuse identifier, this method is
not called. If you override this method, you must be sure to invoke
the superclass implementation.

Strange UITableView error: "Assertion Failure" on dequeuing static cells in storyboard

I am getting this error: *** Assertion failure in -[UITableView dequeueReusableCellWithIdentifier:forIndexPath:] with static cells.
My code is in Swift (the reuse identifiers are strings set as constants in the file):
...
private let topTableViewCellIdentifier = "topTableViewCell"
private let bottomTableViewCellIdentifier = "bottomTableViewCell"
...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let reuseIdentifier = (indexPath.section == 0) ? topTableViewCellIdentifier : bottomTableViewCellIdentifier
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath)
return cell
}
I have searched stack overflow for this error, and found a bunch of answers all basically saying the same thing: make sure the reuse identifier is the same as the one for the cell in the storyboard.
However, in my case, it is.
I have confirmed this repeatedly, and have cut and pasted from one to the other repeatedly in order to ensure this is the case. Xcode clearly indicates that the correct value is being set. I have cleaned. I have even restarted. I have done everything I can think of, and it does not see the cells in the storyboard. They are set as static, and The correct class is being set in the inspector (though that should be clear by the fact that it gets the correct value)
Any thoughts or assistance would be appreciated. I am using El Capitan and Xcode 7.
If you are attempting to use a static layout for a table view, you typically do not implement tableView:cellForRowAtIndexPath:, the UITableViewController does this for you. It is invalid to call the dequeueReusableCellWithIdentifier:forIndexPath: method for a statically configured UITableView (as the error indicates).
Make sure your UITableView is set to Dynamic Prototypes

Resources