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!
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 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
}
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.
So I have this issue where I have to load one entity on two collection views, and one table view. The thing is, primarily I just had to load one entity on the VC where the tableview is located. And that data entity has these parameters for i.e.:
Engine type (diesel or petrol)
Car color
Max.velocity
Year of production
Now, the main point was, to color the cells on the tableview depending on the type of the car engine. So, if the data Entity "Car" has a Bool value of "isEngineDiesel" = true, then the cell would be orange, if false, then it would be light blue. And this worked just fine, a simple if statement on the table view delegate method for loading such cells. But, now I had to implement another VC which has two collection views, in which, the first one loads ONLY Diesel engine Car entity's, and the other Petrol type.
So I guess the issue is already clear here. How can I accomplish this? Because after countless hours of experimenting the only idea I had was two make TWO entity's in which the one is DieselCar and the other PetrolCar. But that means changing the complete data structure, and also, instead of one table view I would actually need two, which doesn't seem like a good idea due the fact that it would "overflow" with all the data there is.
So...any ideas gents?
EDIT:
So far I've only managed to get the cell titles, but the return value of number of cells is still a mystery on how to solve.
The Code for collection view delegate:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CollectionViewCell
if let carData = fetchedResultsController?.objectAtIndexPath(indexPath) as? Car {
cell.layer.cornerRadius = 20
cell.layer.masksToBounds = true
if collectionView == collectionViewDiesel {
if carData.isEngineDiesel == true {
cell.backgroundColor = UIColor(netHex: 0x8DF060)
// Display the cell name
cell.cellTitle.text = carData.cellTitle
}
}else if collectionView == collectionViewPetrol {
if carData.isCarDiesel == false {
cell.backgroundColor = UIColor(netHex: 0xEB9B2D)
// Display the cell name
cell.cellTitle.text = carData.cellTitle
}
}
}
return cell
}
And this is the method I need answered:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
On the other VC you need data structure for two collection views so I think make a dictionary from your data base on the isEngineDiesel if true add objects in one array and if false in another array then set those arrays for the respective diesel and petrol keys .
In you collection view delegate there always a UICollectionView argument place check on that (you can use tags or even == operator if you have outlet) to load the data from dictionary base on keys .
I am displaying data in a collection view, I know how to pass the data on with prepareForSegue function but am trying to have the app determine which segue to use depending on the cell property data. (Each segue goes to a different view controller to display relevant information.)
For e.g.
If the cell.type is equal to "1" then perform segueOne if it is of type "2" then perform segueTwo.
I was trying to do something like this;
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CollectionViewCell
if cell[indexPath].type = "1" {
performSegueWithIdentifier("showPage1", sender: self)
} else if self.cell[indexPath].type = "2" {
performSegueWithIdentifier("showPage2", sender: self)
} else { println("error when selecting cell to segue") }
}
However with this I get an error;
'CollectionViewCell' does not have a member named Subscript
Has anybody got any ideas ?
Assuming the items in your collection view can be re-arranged (or might be some time in the future), the indexPath will not be sufficient to give you the information which cell was selected. Thus, IMO your idea to give the cell a property is a feasible one.
The easiest "quick and dirty" way is to simply hardcode the segue identifier string into your cell. This is not the best design because you are introducing dependencies between app elements that should know of each other.
class MyCell : UICollectionViewCell {
var segue = "DefaultSegue"
}
Now calling the appropriate segue is really easy in didSelectItemAtIndexPath...
self.performSegueWithIdentifier(cell.segue, sender:cell)
It would of course be preferable to use an enum. Safer, more readable and better maintainable.
enum Segue : String {
case ToInfo = "SegueToInfo"
case ToLogin = "SegueToLogin"
// etc.
}
The ivar for MyCell would now be var : Segue = SomeDefaultValue and you can call it the same way.
BTW: Regarding your original question please note the following: as has been pointed out, you cannot subscript a cell. (UICollectionViewCell is not a Dictionary, so cell["key"] does not make sense.) Also, I am not a fan of dequeueing the cell in more than one place - instead you could call cellForItemAtIndexPath or do the work in that method in the first place, as I have suggested.
You're trying to index into a UICollectionViewCell, but of course that class is not an array, so you can't 'subscript' it.
My suggestion is to refactor your code. Whatever data you're storing in your cell you can presumably get from your data model, because that's where it originally came from. You are probably putting that in your cell in cellForIndexPath.
If that is the case, then there is no reason you can't get the same data from the same place in your func ... shouldSelectItemAtIndexPath ... -> Bool. I'd suggest doing it there. Your cell should only contain the data it needs to properly render itself to the screen.
See if that helps.