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
}
Related
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
I am trying to have two buttons as the first row and the rest of the rows as it is shown in the following picture (I apologize for the flipped picture):
I know how to put two buttons in a cell, like so:
And use the following code to add the first cell to my tableview like so:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
//Configure the first cell with the two buttons
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = cathyList[indexPath.row]
}
return cell
}
My logic allows me to have two buttons as the first row cell but I don't know how to make the buttons big and square like the ones that are shown here. Any suggestions?
You format your button as what you want on Storyboard. I suggest 2 options you can get this:
You can add image and title on button and use Edge property to achieve this. Look at this:
This way is hard to achieve what you need, you have to make sure your icon has the right size, and it's not dynamic.
The second way is easier and more dynamic. But maybe not the best solution. I usually use this way.
You add an image (show your icon), a label (your title) and embed them in a view. Then add a button stretch all the view. Connect this button to action and use it as usual.
Hope this help.
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.
I am trying to use a UIcollectionView in my SpriteKit game to display the level select scene. The collection view has 6 sections (start, world 1-5) with 8 cells in each (level 1-8). They are custom cells with a simple UILabel for now.
I can use the did select item for index path to load my levels, it's all good. However my problem is the following
The first section in the collection view is the start screen so I don't want the collection view to show the 8 cells in that section. That section is supposed o only showing a background image and a tap to start lable. So what I tried in cellForIndexPath is to
1) hide cells in that section but that causes text label issues with the cells in the other sections
2) hide the text labels and make the cells color transparent, same problem as 1
So basically what could I do to solve this issue?
I could put the start section into a different SKScene but I prefer if it's all in the collection view.
Another option I was thinking is to make each section have only 1 cell, the size of screen, and add 8 UIButtons to each section except the start section.
I also need some of those cells/buttons to be disabled until the previous level is unlocked. I am not sure what a better approach is, 8 cells as buttons or 8 UIButtons in 1 cell.
I am only looking for a UICollectionView solution, I already have an alternative, because it will make my life much easier when converting to tvOS and using the focus stuff and navigating through 6 sections with 30+ buttons
Thank you very much for any help
You can register more that one kind of cell in UICollectionView:
If you are using Interface builder add another Collection View Cell from Objects library and set it's reuse identifier
Otherwise register nib or class in viewDidLoad method of your UICollectionViewController using self.collectionView?.registerNib(forCellWithReuseIdentifier:) or self.collectionView?.registerClass(forCellWithReuseIdentifier:)
Then when you dequeue cell use proper reuse identifier:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var reuseIdentifier = "identifier of cell with 8 button"
if indexPath.section == 0 {
reuseIdentifier = "identifier of start cell as you set in IB of viewDidLoad"
}
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath)
// Configure the cell depending on indexPath
return cell
}
I'd go for 8 cells as buttons rather than using 1 cell with a series of UIButtons, that will keep things simple and allow you to use didSelectItemAtIndexPath. Can't you just use numberOfItemsInSection to define only 1 cell in the first section?
My approach would probably be to use a custom flow layout and a supplementary view for your background image and tap to start button - a bit complex but very flexible.
I wonder what techniques there are when it comes to toggle between different layouts in a table view.
What I have right now is an embedded tableview/custom cell inside my VC.
It has a simple list design, an image / title.
What I would like to do is when the user press the "grid" button it will change the layout into a bigger cell, kinda like how instagram looks.
So is it possible to animate between different cell layouts in a tableview?
thanks!
I don't believe that it's possible to change the style once it has been initialised. You can however change between your own custom cells at runtime and is probably best achieved using a protocol for different cell types.
Yes it is possible. I have used it but I am not saying that this is the ultimate way.
Here are the steps you may like to follow :
Design two different cells with different identifiers(I did it from storyboard)
In cellForRowAtIndexPath method, check for the identifiers and display the layouts accordingly.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = (self.layoutSegmentControl.selectedSegmentIndex == 0) ? "gridLayoutCell" : "listLayoutCell" //I used segment control to toggle, change the condition as per your need
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! MyCustomTableViewCell
if(cellIdentifier == "gridLayoutCell")
{
//Set the values
cell.bigImgView.image = [yourimage] //for ex
...
}
if(cellIdentifier == "listLayoutCell")
{
//Set the values
cell.thumbnailImgView.image = [yourimage] //for ex
...
}
}
reload your tableview when toggling(layout changes)
Try this and let me know. Hope it works for you!