Different Layout in different Table view sections - uitableview

Hi I want to have different layouts in different sections of UITableView, I want to do it using dynamic prototype cells and not using static cells.
I don't know how to create it, Please help. Any links or something. I want to achieve like this, please see the picture
pls download the pic
Please give your code if any in swift.

According to your details, it seems like, you want a grouped tableView where your different section will have different types of cell. That is pretty easy.
So let's start. I explained the whole walk through from the scratch through the screen shots. If you just follow this, you will understand the concept and the process.
(I am assuming, you know how to add necessary constraints)
Firstly, Go to the Storyboard and drag a tableView in your Controller.
Then create 2 Custom UITableViewCell classes-
Now drag and drop 2 TableView Cells inside your TableView in storyboard.
So, you have 2 tableView Cells inside your TableView for which you have already created two Custom Cells.
Now, we need to assign the cell classes, so that it can understand which class it should corresponds to. Select the First cell in the storyboard, click on the class inspector and assign its class-
Do the Same for the second cell -
We also need to give them unique identifiers. Select the first cell and assign an identifier like -
Do the same for the second cell -
We are almost done setting up the UI. The last piece is to tell the UITableView that it is going to be a "Group" type.
Again select the TableView and assign its type to "Group" like-
Now, we are good to go.
Let's declare some IBOutlets in our custom TableViewCells that we created earlier.
TypeOneTableViewCell.swift class-
import UIKit
class TypeOneTableViewCell: UITableViewCell {
#IBOutlet weak var cellImageView: UIImageView!
#IBOutlet weak var cellTitleLabel: UILabel!
#IBOutlet weak var cellSubtitleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
and TypeTwoTableViewCell.swift class-
import UIKit
class TypeTwoTableViewCell: UITableViewCell {
#IBOutlet weak var cellTitleLabel: UILabel!
#IBOutlet weak var cellSubtitleLabel: UILabel!
#IBOutlet weak var cellButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
}
Go to the Storyboard and add an image and two labels in the First prototype cell and attach them with the outlets.
Now in the second cell, add a button and two labels and connect the outlets the same as before-
Enough with the setting up. Let's jump into doing some real stuff. Go to your controller class and first create an IBOutlet for your tableView like-
#IBOutlet weak var groupedTableView :UITableView!
Don't forget to attach the TableView's outlet in storyboard.
Now, we need the TableView Delegate and Datasource. So, let's include them in the protocol list like-
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
Right now, you may be getting an error because you haven't implement the required delegate methods which is in the UITableViewDatasource protocol, but it's okay, it will be resolved soon.
First thing first. Specify who is going implement the delegate and datasource methods. Go to your viewDidLoad method and add this -
override func viewDidLoad() {
super.viewDidLoad()
self.groupedTableView.dataSource! = self
self.groupedTableView.delegate! = self
}
then tell your tableView that you will have 2 sections through the numberOfSectionsInTableView method like-
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
and then specify how many of the cells, each section is going to held. Let's assume the 1st section contains 4 rows and the 2nd one contains 3 rows. To do so, use the numberOfRowsInSection method.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if section == 0{
return 4
}
else{
return 3
}
}
and the last part, defining the cell and it's data-
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
if indexPath.section == 0{
let cell : TypeOneTableViewCell = tableView.dequeueReusableCellWithIdentifier("typeOneCell", forIndexPath: indexPath) as! TypeOneTableViewCell
cell.imageView!.image = UIImage(named: "noImage.png")
cell.cellTitleLabel.text = "Header " + "\(indexPath.section)" + "-" + "\(indexPath.row)"
cell.cellSubtitleLabel.text = "Details " + "\(indexPath.section)" + "-" + "\(indexPath.row)"
return cell
}
else{
let cell : TypeTwoTableViewCell = tableView.dequeueReusableCellWithIdentifier("TypeTwoCell", forIndexPath: indexPath) as! TypeTwoTableViewCell
cell.cellTitleLabel.text = "Header " + "\(indexPath.section)" + "-" + "\(indexPath.row)"
cell.cellSubtitleLabel.text = "Details " + "\(indexPath.section)" + "-" + "\(indexPath.row)"
return cell
}
}
There you GO! TableView have many delegate methods like heightForRowAtIndexPath to specify the heights of custom cells. In my case I specified it as 80.0 like-
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 80.0
}
You can do a lot more customization with those delegate methods. Check apple's guide for UITableView.
P.S. : for beautification, I added an image here. If you implement the same way, I did, you should see the output like-
Hope this helps.

For cells to be dynamic, do this:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (indexPath.row==0 && indexPath.section==0){
//static cell
return 120 //static height
}
return UITableViewAutomaticDimension //for dynamic cell height
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Also, you need to make sure that your elements in the cell are bound with autolayout for the correct placements of the elements with dynamic height.

Related

TableView empties when interacting

So I have 2 Views that are shown inside a UIView, based on what is selected on the SegmentViewController. I create dummy data, returning 20 row of a custom cell. This works great.
Everything is fine, till I interact with the TableView.
Bellow is my code:
GoalsViewController.swift
import UIKit
class GoalsViewController: UIViewController {
#IBOutlet weak var goalsTableView: UITableView!
let goalCellIdentifier = "goalCell"
override func viewDidLoad() {
super.viewDidLoad()
goalsTableView.delegate = self
goalsTableView.dataSource = self
goalsTableView.register(UINib(nibName: "GoalsViewCell", bundle: nil), forCellReuseIdentifier: goalCellIdentifier)
goalsTableView.reloadData()
}
}
extension GoalsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = goalsTableView.dequeueReusableCell(withIdentifier: goalCellIdentifier, for: indexPath) as! GoalsViewCell
cell.goalTitle.text = "aaaaa"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected \(indexPath.row)")
}
}
After any of the empty rows is selected, the didSelectRowAt is not called, so the cells are not there at all. I tried to find a solution, but I was only to find issues about empty lists, before being populated.
What could be the reason for the empty tableview?
I might be wrong here but one thing that I've noticed is that you are not implementing a function which sets the height of each cell.
// Specify the height of your cells
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100 // Or your given cell height.
}
So here is my theory: If you are using constraints something is missing and your cell's height can't be identified by constraints alone or you are not using constraints at all thus you must use heightForRowAt function to specify each cell's height.
I will explain what was the issue for people who probably did not know (like me).
So my UITableView is inside a UIView that changes based on what the user is selecting. In total I had 2 different Views that where switching. The reason that it was emptying it was because my parent ViewController, could not access the delegate for UITableView. To fix that, after adding a subview to the UIView, you need also to move the ViewController to the parent controller. In code it goes like this.
// Empty array of UIViewControllers
var views: [UIViewController]!
// Add the UIViewControllers to the array
views = [UIViewController()]
views.append(EventsViewController())
views.append(GoalsViewController())
for view in views {
// Needed to adjust the size of Subview to size of View
view.view.frame = containerView.bounds
// Add the subviews
containerView.addSubview(view.view)
}
// Bring the view in position 1 to the front of the UIView
containerView.bringSubviewToFront(views[1].view)
// Add Views[1] UIViewController as a child to the parent controller
self.addChild(views[1])
views[1].didMove(toParent: self)
// After done with everything with the UIViewController remove it
views[1].removeFromParent()
addChild Apple.com
didMove Apple.com

Swift 3 - Expandable table view cells without closing other ones

I am using Swift 3.
I've been following this tutorial: https://www.youtube.com/watch?v=VWgr_wNtGPM , supplemented by this answer on StackOverflow.
However, the way that this works is that if I click on a cell, it expands while hiding other cells. How do I make it such that when I expand it, the other already-expanded cells stay expanded?
The best approach I suggest you for achieving this in an elegant way is implementing it through UIStackView elements.
Take a look this post http://www.atomicbird.com/blog/uistackview-table-cells
if you wanna do this yourself, you could try this way.
first step is you should create a model list just like:
var cellsData: [CustomData] = [];
the CustomData seem like:
class CustomData {
var isExpanded: Bool = false;
// whatever other variables
}
then your custom cell should whatever look like but you must do something in the tableView:didSelectItemAt like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = indexPath.row;
self.cellsData[row].isExpanded = !self.cellsData[row].isExpanded;
self.tableView.reloadRows(at: [indexPath], with: .none); // or the other animations
}
then in the "tableView:cellForRowAt" seems like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell;
if(cell.isExpanded){
// do something when the cell is expanded
}else{
// do something when the cell is not expanded
}
}
remember, the cell is reusable, means if you have used the cell more than one time, then the cell will keep the state when it was used the last time.
You can use ExpyTableView, which makes an expandable section from your given header cell. Compatible down to iOS 8.0.
All you have to do is to import ExpyTableView and then:
class ViewController: ExpyTableViewDataSource, ExpyTableViewDelegate {
#IBOutlet weak var expandableTableView: ExpyTableView!
// First, set data source and delegate for your table view.
override func viewDidLoad() {
super.viewDidLoad()
expandableTableView.dataSource = self
expandableTableView.delegate = self
}
// Then return your expandable cell instance from expandingCell data source method.
func expandableCell(forSection section: Int, inTableView tableView: ExpyTableView) -> UITableViewCell {
// this cell will be displayed at IndexPath with section: section and row 0
}
}
You can see your former table view section is now an expandable table view section. You can also download the example project and see more detailed examples.

Prototype UITableViewCell with other objects (UITextField, UISwitch)

I'm trying to make a UITableView that can support having different objects/elements inside it. Specifically, these elements are a UITextField and UISwitch.
The first problem:
The elements do not show up. They are placed in the prototype cell, which is then constructed inside the class I have set up. I have verified that the cell setup is working because I can change the words on each cell, but there are no elements within the cell.
Here is the code that constructs my cells right now:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return 1
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "EmailCell")
return cell
}
Second problem (which might be solved along with the first):
I have no way of accessing the information in each UITextField or each UISwitch. How can I get this information from all cells that exist?
Thanks in advance for the help!
There are multiple things wrong with your code.
For custom cells you need to implement a custom UITableViewCell subclass. Here is an example:
import UIKit
class EmailCell: UITableViewCell {
#IBOutlet var customTextField: UITextField!
#IBOutlet var customSwitch: UISwitch!
}
After that, open your Storyboard and select the prototype cell. Change it's class to EmailCell in the Identity Inspector.
Also make sure to connect your ui elements to the #IBOutlets created earlier. See this StackOverflow post if you need help with #IBOutlet.
In the next step, change your tableView(_:, cellForRowAt:) implementation like this:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "EmailCell", for: indexPath) as! EmailCell
// Configure your custom ui elements however you want
cell.customTextField.text = "somestring"
cell.customSwitch.isOn = true
return cell
}
Make sure your cells have reuse identifiers and you're using
tableView.dequeueReusableCell(withIdentifier: -Your cell Id- , for: indexPath) as? -Your Cell Class-
in your cell for row at index datasource method
next you can add targets to your cell text field / switch by doing this in your cell for row at index datasource method
cell.-your switch / text field-.addTarget(self, action: #selector(valueChanged(_:)), for: .valueChanged)
and you should subclass a uitableview cell to add the property / iboutlets
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var yourSwitch: UISwitch!
}

Swift - UITableview: How to make a tap on one cell cause change a label in another cell?

I have a UITableView, and I'd like a tap in cell X to cause the label text in cell Y to change from "Waiting..." to "Done". I'm pretty baffled as to how I should go about this. I've tried to do this using didSelectRowAtIndexPath , but seemingly I can only modify properties of the cell at the row tapped on.
Thanks!
Its quite easy to get a cell you need to change, but you have to know which row of your table it is:
class MyCell : UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
}
class TableViewController: UITableViewController {
...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = 7 //or what you set
let cellWhereIsTheLabel = tableView.cellForRow(at: IndexPath(row: row, section: 0)) as! MyCell
cellWhereIsTheLabel.myLabel.text = "Done"
}
...
}
A solution will be to update the datasource of your tableview.
When user taps on cell, didSelectRowAtIndexPath is called. In this function you can update datasource to provoke changes in other cells and then call reloadData to apply changes.
When you call reloadData tableView is updated by cellForRowAtIndexPath. This function uses your datasource to update cell.

Connecting a UITableViewCell Label in Swift

I have a TableView Cell and inside that I have an ImageView and Label. But when I connect to them using:
#IBOutlet weak var menuListLabel: UILabel!
#IBOutlet weak var menuListImage: UIImageView!
Illegal Configuration:
The menuListImage outlet from the ViewController to the UIImageView is
invalid. Outlets cannot be connected to repeating content.
You need to create a custom class that inherits from UITableViewCell, and configure the outlets there.
class MyCustomTableViewCell: UITableViewCell {
#IBOutlet weak var menuListLabel: UILabel!
#IBOutlet weak var menuListImage: UIImageView!
}
Next, you need to configure the cell in your storyboard. Select your cell. Open the Identity inspector and set the Custom Class to "MyCustomTableViewCell".
Then, with the cell still selected, go to the Attributes inspector, and set the Reuse Identifier to "MyCustomTableViewCell". (This identifier can be whatever you want, you just need to use this exact value when calling 'dequeueReusableCellWithIdentifier'. I like to use my cell's class name as the identifier so it's easy to remember.)
In your table view controller, implement the necessary methods to build your table using your custom cell.
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1 // however many sections you need
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1 // however many rows you need
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// get an instance of your cell
let cell = tableView.dequeueReusableCellWithIdentifier("MyCustomTableViewCell", forIndexPath: indexPath) as MyCustomTableViewCell
// populate the data in your cell as desired
cell.menuListLabel.text = "some text"
cell.menuListImage.image = UIImage(named: "some image")
return cell
}

Resources