I have to show tableview with two sections based on flag value. Based on flag value I have to show/hide first section.
First section has only one row and static customised cell which will show always same data.
And second section is another customised cell, Which is dynamic rows shows from server data.
I need to show Second section is always. First section based on flag I have to show or hide.
How to handle this?
Here is my code
override func viewDidLoad() {
super.viewDidLoad()
self.registerCells()
}
func registerCells(){
self.DetailsTableview.register(RadioButtonTableViewCell.nib, forCellReuseIdentifier: RadioButtonTableViewCell.identifier)
if flagValue == true {
self.DetailsTableview.register(UPPercentageCell.nib, forCellReuseIdentifier: PercentageCell.identifier)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
if flagValue == true {
return 2
}
return 1
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if flagValue == true {
return 20
}
return 40
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if flagValue == true {
if section == 0 {
return 1
} else {
return self.response?.data?.count ?? 0
}
}
return self.response?.data?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if flagValue == true {
if indexPath.section == 0 {
let percentagecell: PercentageCell = tableView.dequeueReusableCell(withIdentifier: "PercentageCell", for: indexPath) as! UPPercentageCell
percentagecell.percentage = "20" //some dynamic value
percentagecell.isUserInteractionEnabled = false
return percentagecell
} else {
let cell: RadioButtonTableViewCell = tableView.dequeueReusableCell(withIdentifier: "RadioButtonTableViewCell", for: indexPath) as! RadioButtonTableViewCell
cell.displayDataToUI(title: response?.data?[indexPath.row] ?? "", currentIndexpath: indexPath, selectedIndexpath: selectedIndexpath ?? IndexPath())
cell.radioButtonClicked = {
[weak self] (indexpath) in
self?.saveButton.isUserInteractionEnabled = true
self?.reloadTableviewFromSelectedIndexpath(indexpath: indexpath)
}
return cell
}
} else {
let cell: RadioButtonTableViewCell = tableView.dequeueReusableCell(withIdentifier: "RadioButtonTableViewCell", for: indexPath) as! RadioButtonTableViewCell
cell.displayDataToUI(title: response?.data?[indexPath.row] ?? "", currentIndexpath: indexPath, selectedIndexpath: selectedIndexpath ?? IndexPath())
cell.radioButtonClicked = {
[weak self] (indexpath) in
self?.saveButton.isUserInteractionEnabled = true
self?.reloadTableviewFromSelectedIndexpath(indexpath: indexpath)
}
return cell
}
return UITableViewCell()
}
Is there any better approach to achieve this?
Highly appreciate your valuable suggestions.
Your approach is fine, but you can simplify things a bit with fewer if/else blocks...
First:
func registerCells(){
self.DetailsTableview.register(RadioButtonTableViewCell.nib, forCellReuseIdentifier: RadioButtonTableViewCell.identifier)
// doesn't hurt to go ahead and register both cell classes for reuse
self.DetailsTableview.register(UPPercentageCell.nib, forCellReuseIdentifier: PercentageCell.identifier)
}
next, just a more compact way of writing it:
override func numberOfSections(in tableView: UITableView) -> Int {
return flagValue ? 2 : 1
}
and:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// this will only be true if
// section is 0
// AND
// flagValue is true
if section == 0 && flagValue {
return 1
}
// whether we're in section 0 WITHOUT the "top" section, or
// we're in section 1 WITH the "top" section
return self.response?.data?.count ?? 0
}
and:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// this will only be true if
// section is 0
// AND
// flagValue is true
if indexPath.section == 0 && flagValue {
let cell = tableView.dequeueReusableCell(withIdentifier: UPPercentageCell.identifier, for: indexPath) as! UPPercentageCell
cell.percentage = "20" //some dynamic value
cell.isUserInteractionEnabled = false
return cell
}
// we want to display the same cells from the same data array
// whether we're in section 0 WITHOUT the "top" section, or
// we're in section 1 WITH the "top" section
let cell = tableView.dequeueReusableCell(withIdentifier: RadioButtonTableViewCell.identifier, for: indexPath) as! RadioButtonTableViewCell
cell.displayDataToUI(title: response?.data?[indexPath.row] ?? "", currentIndexpath: indexPath, selectedIndexpath: selectedIndexpath ?? IndexPath())
cell.radioButtonClicked = {
[weak self] (indexpath) in
self?.saveButton.isUserInteractionEnabled = true
self?.reloadTableviewFromSelectedIndexpath(indexpath: indexpath)
}
return cell
}
Related
I'm newer in xcode and swift and i found a guide for expand cell when the entire cell is pressed.
Now i wanna expand the cell when the button inside the cell is pressed.
I have this in my view controller :
//datasource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 100 {
return nameArr.count
}else{
return prova.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == 100 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell") as! MainTableViewCell
cell.lblName.text = nameArr[indexPath.row]
cell.expand.tag = indexPath.row
return cell
}else{
tableView.estimatedRowHeight = 60
tableView.rowHeight = UITableView.automaticDimension
let cell = tableView.dequeueReusableCell(withIdentifier: "InsideTableViewCell") as! InsideTableViewCell
cell.lblInsideName.text = prova[indexPath.row]
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//se la cella è selezionata e deve essere tirata ancora giù ritorna 243 che sarebbe il valore della cella + l'immagine dentro da mostrare
if selectedIndex == indexPath.row && isCollapsed == true && tableView.tag == 100 {
return 375
}else {
//altrimenti se è gia collassata e ripremiamo sulla cella ritorna 50 e quindi richiude la cella
return 96
}
}
//delegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if selectedIndex == indexPath.row {
if self.isCollapsed == false {
self.isCollapsed = true
}else{
self.isCollapsed = false
}
}else{
self.isCollapsed = true
}
self.selectedIndex = indexPath.row
//tableView.reloadRows(at: [indexPath], with: .automatic)
tableView.beginUpdates()
tableView.endUpdates()
}
But i don't know ho to do that.
In my viewController now i have this variable :
var selectedIndex = -1 //tell me which cell i pressed
var isCollapsed = false // tell if the cell is already collapsed
You can define a clouser in cell and call it when you press the button in your cell
in your cell define:
var buttonClicked: (() -> Void)?
in your cell button call this clouser
func buttonPressAction() {
buttonClicked?()
}
in your cellForRow method change it like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == 100 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell") as! MainTableViewCell
cell.buttonClicked = { [weak self] in
if self.isCollapsed == false {
self.isCollapsed = true
} else{
self.isCollapsed = false
}
}else{
self.isCollapsed = true
}
}
}
Modify the data source to change it
And of course the data source can be more complex it can be an object that contains whether or not to open the property
let nameArr:[Bool] = [false, false, false]
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.nameArr[indexPath.row] = !self.nameArr[indexPath.row]
tableView.reloadRows(at: [indexPath], with: .none)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let isshow = self.nameArr[indexPath.row]
if isshow {
return 375
} else {
return 69
}
}
You should update the table view after changing the height:
tableView.reloadData().
You can do it by sending info about that action to the view controller using delegates.
1) You should save a state of your cells. You can store it in the cell model:
class YourCellModel {
var isExpanded = false
}
2) You should create a delegate protocol:
protocol YourCellDelegate: AnyObject {
func buttonPressed()
}
3) Add properties for the cell delegate and the cell model. Also you should add a function buttonPressed:
class YourCell: UITableViewCell {
weak var delegate: YourCellDelegate?
var model: YourCellModel?
//...
#IBAction func buttonPressed() {
model?.isExpanded = true
delegate?.buttonPressed()
}
}
4) You should store cell models in the view controller: {
class YourViewController: UIViewController {
var cellModels: [YourCellModel] = []
//...
override func viewDidLoad() {
super.viewDidLoad()
cellModels = Array(repeating: YourCellModel(), count: <count of cells, maybe it is a prova.count>)
}
}
5) Setup cell models and delegates to the cells in cellForItem:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = cellModels[indexPath.row]
if tableView.tag == 100 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell") as! MainTableViewCell
cell.lblName.text = nameArr[indexPath.row]
cell.expand.tag = indexPath.row
cell.model = model
cell.delegate = self
return cell
} else {
tableView.estimatedRowHeight = 60
tableView.rowHeight = UITableView.automaticDimension
let cell = tableView.dequeueReusableCell(withIdentifier: "InsideTableViewCell") as! InsideTableViewCell
cell.lblInsideName.text = prova[indexPath.row]
cell.model = model
cell.delegate = self
return cell
}
}
6) Update heightForItem:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let model = cellModels[indexPath.row]
if model.isExpanded {
return 375
} else {
return 96
}
}
7) Your view controller should implement YourCellDelegate:
extension YourViewController: YourCellDelegate {
func buttonPressed() {
tableView.reloadData()
}
}
I have attached the image click the card view expands the same card inside the table cell dynamically its passible to achieve this?
I have searched a lot but not working
Hear my code added header cell with CardView
added arrow button to click the button expand the cell
its able expand but not in parent card it was showing diff card
I have adde my source code
var hiddenSections = Set<Int>()
let tableViewData = [
["1","2","3","4","5"],
["1","2","3","4","5"],
["1","2","3","4","5"],
]
override func viewDidLoad() {
super.viewDidLoad()
let CustomeHeaderNib = UINib(nibName: "CustomSectionHeader", bundle: Bundle.main)
historyTableView.register(CustomeHeaderNib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.tableViewData.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.hiddenSections.contains(section) {
return 0
}
return self.tableViewData[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = self.tableViewData[indexPath.section][indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return view.frame.width/4
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = self.historyTableView.dequeueReusableHeaderFooterView(withIdentifier: "customSectionHeader") as! CustomSectionHeader
header.setupCornerRadious()
let sectionButton = header.expandBtn
sectionButton?.setTitle(String(section),
for: .normal)
sectionButton?.tag = section
sectionButton?.addTarget(self,action: #selector(self.hideSection(sender:)), for: .touchUpInside)
return header
}
#objc
private func hideSection(sender: UIButton) {
let section = sender.tag
func indexPathsForSection() -> [IndexPath] {
var indexPaths = [IndexPath]()
for row in 0..<self.tableViewData[section].count {
indexPaths.append(IndexPath(row: row,
section: section))
}
return indexPaths
}
if self.hiddenSections.contains(section) {
self.hiddenSections.remove(section)
self.historyTableView.insertRows(at: indexPathsForSection(),
with: .fade)
} else {
self.hiddenSections.insert(section)
self.historyTableView.deleteRows(at: indexPathsForSection(),
with: .fade)
}
}
With out sections also you can achieve this. To do this,
1.Return cell height as section height. If user clicks on the cell then return total content height to the particular cell.
2.You need to take an array, if user selects cell, add indexPath number in to array. If selects already expand cell remove it from array. In height for row at index check indexPath is in array or not.
This is one of the way. With sections also you can do that.
//MARK:- UITableView Related Methods
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrDict.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// var cel = tblExpandedTest.dequeueReusableCellWithIdentifier("expCell", forIndexPath: indexPath) as! CDTableViewCell
var cel : CaseHearingTabTVC! = tableView.dequeueReusableCell(withIdentifier: "caseHearingTabCell") as! CaseHearingTabTVC
if(cel == nil)
{
cel = Bundle.main.loadNibNamed("caseHearingTabCell", owner: self, options: nil)?[0] as! CaseHearingTabTVC;
}
//cell?.backgroundColor = UIColor.white
cel.delegate = self
if indexPath != selctedIndexPath{
cel.subview_desc.isHidden = true
cel.subview_remarks.isHidden = true
cel.lblHearingTime.isHidden = true
}
else {
cel.subview_desc.isHidden = false
cel.subview_remarks.isHidden = false
cel.lblHearingTime.isHidden = false
}
return cel
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectIndex = true;
if(selectedInd == indexPath.row) {
selectedInd = -1
}
else{
let currentCell = tableView.cellForRow(at: indexPath)! as! CaseHearingTabTVC
cellUpdatedHeight = Float(currentCell.lblHearingTime.frame.origin.y + currentCell.lblHearingTime.frame.size.height) + 2;
selectedInd = -1
tblCaseHearing.reloadData()
selectedInd = indexPath.row
}
let previousPth = selctedIndexPath
if indexPath == selctedIndexPath{
selctedIndexPath = nil
}else{
selctedIndexPath = indexPath
}
var indexPaths : Array<IndexPath> = []
if let previous = previousPth{
indexPaths = [previous]
}
if let current = selctedIndexPath{
indexPaths = [current]
}
if indexPaths.count>0{
tblCaseHearing.reloadRows(at: indexPaths, with: UITableView.RowAnimation.automatic)
}
}
func tableView(_ tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowIndexPath indexPath:IndexPath) {
(cell as! CaseHearingTabTVC).watchFrameChanges()
}
func tableView(_ tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowIndexPath indexPath:IndexPath) {
(cell as! CaseHearingTabTVC).ignoreFrameChanges()
}
func tableView(_ TableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
if indexPath == selctedIndexPath{
return CGFloat(cellUpdatedHeight)
}else{
return CaseHearingTabTVC.defaultHeight
}
}
Best approach is to create two different cells for normal card and expanded card.
fileprivate var selectedIndex: Int?
func registerTableViewCells() {
tableView.register(UINib(nibName:Nib.CardCell , bundle: nil), forCellReuseIdentifier: "CardCell")
tableView.register(UINib(nibName:Nib.ExpandedCardCell , bundle: nil), forCellReuseIdentifier: "ExpandedCardCell")
}
override func viewDidLoad() {
super.viewDidLoad()
self.registerTableViewCells()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard let index = selectedIndex else {
return 115
}
if index == indexPath.row{
return 200
}
return 115
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let selected = selectedIndex, selected == indexPath.row{
let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandedCardCell", for: indexPath) as! ExpandedCardCell
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "CardCell", for: indexPath) as! CardCell
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedIndex == indexPath.row{
selectedIndex = nil
}
else{
selectedIndex = indexPath.row
}
UIView.performWithoutAnimation {
tableView.reloadData()
}
}
It is posible to add a select all option in TableView Multiple checkmarks...?
I have a code ready, and I try to do this function. please help me !
CODE:
struct Area {
let name : String
var isSelected : Bool
init(name : String, isSelected : Bool = false) {
self.name = name
self.isSelected = isSelected
}
}
var areas = [Area(name: "Select all"),Area(name: "a"),Area(name: "b"),Area(name: "c"),Area(name: "d"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return areas.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let area = areas[indexPath.row]
cell.textLabel?.text = area.name
cell.accessoryType = area.isSelected ? .checkmark : .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
areas[indexPath.row].isSelected.toggle()
tableView.reloadRows(at: [indexPath], with: .none)
let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
print(selectedAreas)
}
Thanks.
You have to check if the user selected the first row ("Select all") and update the other rows accordingly:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// toggle the selected area
areas[indexPath.row].isSelected.toggle()
// save the new state for later use
let isSelected = areas[indexPath.row].isSelected
if indexPath.row == 0 {
// "Select all" was selected – update all areas
for i in 1..<areas.count {
areas[i].isSelected = isSelected
}
// update UI
tableView.visibleCells.forEach { $0.accessoryType = isSelected ? .checkmark : .none }
} else {
// update UI
tableView.cellForRow(at: indexPath)?.accessoryType = isSelected ? .checkmark : .none
}
tableView.deselectRow(at: indexPath, animated: true)
}
Recommendation
To separate concerns visually you could also use an own table view section for the "Select all" row. In that case some more changes are necessary:
var areas = [
// you do not need an area for "Select all" any longer
Area(name: "a"),
Area(name: "b"),
Area(name: "c"),
Area(name: "d")
]
var allSelected: Bool {
// helper var to see if all areas are currently selected
return areas.filter({!$0.isSelected}).isEmpty
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 1: return "Areas"
default: return nil
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: return 1 // select all
case 1: return areas.count
default:
// we should never get here
fatalError()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.selectionStyle = .none
if indexPath.section == 0 {
cell.textLabel?.text = "Select all"
cell.accessoryType = allSelected ? .checkmark : .none
} else {
let area = areas[indexPath.row]
cell.textLabel?.text = area.name
cell.accessoryType = area.isSelected ? .checkmark : .none
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
// (de-)select all
let shouldSelect = !allSelected
for i in 0..<areas.count {
areas[i].isSelected = shouldSelect
}
} else {
areas[indexPath.row].isSelected.toggle()
}
tableView.reloadRows(at: tableView.indexPathsForVisibleRows ?? [], with: .automatic)
}
First you need to enable multiple selection for the table view
tableView.allowsMultipleSelection = true
tableView.allowsMultipleSelectionDuringEditing = true
Select all rows
for section in 0..<tableView.numberOfSections {
for row in 0..<tableView.numberOfRows(inSection: section) {
let indexPath = IndexPath(row: row, section: section)
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
}
To enable checkmark whenever a row is selected:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
let area = areas[indexPath.row]
area.isSelected = true
cell.accessoryType = .checkmark
}
}
Also remove checkmark when it is deselected:
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
let area = areas[indexPath.row]
area.isSelected = false
cell.accessoryType = .none
}
}
Please vote this answer if it helps. Thanks.
First change your Area to class so it will be reference type
class Area {
let name : String
var isSelected : Bool
init(name : String, isSelected : Bool = false) {
self.name = name
self.isSelected = isSelected
}
}
Now change your didselect logic
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let area = areas[indexPath.row]
area.isSelected.toggle()
if area.name == "Select all"{
areas = areas.map{$0.isSelected = true}
}
tableView.reloadRows(at: [indexPath], with: .none)
let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
print(selectedAreas)
}
In didSelectRow:
if indexPath.row == 0 { // the first item is the select all button
// find out which areas should be selected
let indicesThatShouldBeSelected = (1..<areas.count).filter { !areas[$0].isSelected }
// update model
indicesThatShouldBeSelected.forEach { areas[$0].isSelected = true }
// update view
tableView.reloadRows(at: indicesThatShouldBeSelected.map { IndexPath(section: 0, row: $0) }, with: .none)
}
You could also just do tableView.reloadData(), which reloads the whole table view instead of just the rows that needs reloading.
If the selectAll row is at index 0 check the row, then set all isSelected members to true and reload the entire table
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
areas.indices.forEach{areas[$0].isSelected = true}
tableView.reloadData()
} else {
areas[indexPath.row].isSelected.toggle()
tableView.reloadRows(at: [indexPath], with: .none)
}
let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
print(selectedAreas)
}
And if you want to exclude the first item to be selected drop the first index
areas.indices.dropFirst().forEach{areas[$0].isSelected = true}
Is there a way to customize a section of cell? Probably the easiest way is to design a cell in the storyboard but I do not know how to implement it in my code.
This is what I got so far. It is pretty basic and copied from a tutorial on youtube. So sectionData should be replaced with the input for the customized section/subCell.
The upper cell should be the 'mainCell' and the cell below should be displayed after the mainCell is touched
import UIKit
struct cellData {
var opened = Bool()
var title = String()
var sectionData = [String]()
}
class ViewController: UITableViewController {
var tableViewData = [cellData]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableViewData = [cellData(opened: false, title: "Title1", sectionData: ["Cell1","Cell2","Cell3"]),
cellData(opened: false, title: "Title2", sectionData: ["Cell1","Cell2","Cell3"]),
cellData(opened: false, title: "Title3", sectionData: ["Cell1","Cell2","Cell3"]),
cellData(opened: false, title: "Title4", sectionData: ["Cell1","Cell2","Cell3"])]
}
override func numberOfSections(in tableView: UITableView) -> Int {
return tableViewData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableViewData[section].opened == true {
return tableViewData[section].sectionData.count + 1
} else {
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dataIndex = indexPath.row - 1
if indexPath.row == 0 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {return UITableViewCell()}
cell.textLabel?.text = tableViewData[indexPath.section].title
return cell
} else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {return UITableViewCell()}
cell.textLabel?.text = tableViewData[indexPath.row].sectionData[dataIndex]
return cell
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
if tableViewData[indexPath.section].opened == true {
tableViewData[indexPath.section].opened = false
let sections = IndexSet.init(integer: indexPath.section )
tableView.reloadSections(sections, with: .none)
} else {
tableViewData[indexPath.section].opened = true
let sections = IndexSet.init(integer: indexPath.section )
tableView.reloadSections(sections, with: .none)
}
}
}
}
https://www.appcoda.com/expandable-table-view/
you can follow this tutorial. You can reload the cell which you want to expand using below code. I have added in the `didSelectRowAt. Set expandCell variable to true for changing height of cell when reloading.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Expand View
self.expandCell = true
self.tableView.beginUpdates()
self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
self.tableView.endUpdates()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == expandRowIndex && self.expandCell {
return 200
}
return UITableViewAutomaticDimension
}
but the question you asked is irrelevant to the once you want to implement. anyway the answer for your question is, you can implement viewForHeaderInSection and viewForFooterInSection to customize your tableview sections.
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = "create your custom cell here or you can init from your nib"
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
if you want to do it in storyboard, just drag and drop UITableViewCell inside your tableview, assign some reuserIdentifier. call this tableview cell in your viewForHeaderInSection
I have a tableview with custom headers. and the uitableview row cells have refresh button that will call an API and reload the sender cell. However, whenever a refresh is done, the section headers disappear.
extension AccountViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return platforms.count - 1
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "BalanceHeaderCell") as? BalanceHeaderCell
if self.currentUser != nil {
self.saveUserDefault(item: currentUser?.balance ?? "", key: Account.accountBalanceRecord)
cell?.headerRefreshButton.tag = section
cell?.headerRefreshButton.addTarget(self, action: #selector(refreshAccountBalanceInfo), for: UIControlEvents.touchUpInside)
cell?.set(user: self.currentUser!)
}
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlatformHeaderCell") as? PlatformHeaderCell
return cell
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "BankCardsCell", for: indexPath) as? BankCardsCell
cell?.setCell(bankCount: self.bankCount)
return cell!
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlatformListCell", for: indexPath) as? PlatformListCell
cell?.set(platforms: platforms[indexPath.row])
cell?.platformRefreshButton.tag = indexPath.row
cell?.platformRefreshButton.addTarget(self, action: #selector(refreshPlatformBalanceInfo), for: UIControlEvents.touchUpInside)
return cell!
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 73
} else {
return 40
}
}
What are your suggestions?