UITableView changes scroll position when cell height changes - ios

I have a tableView that expands cell height when user taps on cell. The problem is in some cells after expanding cell height tableView scrolls very higher or lower the selectedCell I mean it miss the current cell position
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if cellsMode[indexPath.row] {
return 344
}
else {
return 140 }
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.cellsMode[indexPath.row] = !self.cellsMode[indexPath.row]
table.reloadData()
}
I tried to use
let indexPath = NSIndexPath(row: cellInex, section: 0)
self.table.scrollToRow(at: indexPath as IndexPath, at: .bottom, animated: false)
but it not worked.
How can i fixed it?

Just reload cell not entire tableview, like bellow:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.cellsMode[indexPath.row] = !self.cellsMode[indexPath.row]
tableView.reloadRows(at: [indexPath], with: .automatic)
}

Related

How to change tableview section cell height when click on specific cell?

i have many sections in table view cell each section contain many cell. i need to enlarge the cell while click on the cell .now when i click on cell all cells height changing inside the section.please help me to solve this
var expandedIndexSet : IndexSet = []
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if expandedIndexSet.contains(indexPath.section) {
return 406
} else {
return 88
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if(expandedIndexSet.contains(indexPath.section)){
expandedIndexSet.remove(indexPath.section)
} else {
expandedIndexSet.insert(indexPath.section)
}
tableView.reloadRows(at: [indexPath], with: .automatic)
}
You're storing only section indices. Instead of that you need to store IndexPath objects:
var expandedIndexSet = Set<IndexPath>()
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if expandedIndexSet.contains(indexPath) {
return 406
} else {
return 88
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if(expandedIndexSet.contains(indexPath)){
expandedIndexSet.remove(indexPath)
} else {
expandedIndexSet.insert(indexPath)
}
tableView.reloadRows(at: [indexPath], with: .automatic)
}

Expand the only tapped cell and collapse other cells

I am doing expand/collapse when tapped on tableview cell, but I have to close all other cells except the tapped one. Tried this solution Expand only the cell that has been tapped this solution is not working for me.
below code which I have written for expand/collapse
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datasource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView .dequeueReusableCell(withIdentifier: String(describing: ExpandingTableViewCell.self), for: indexPath) as! ExpandingTableViewCell
cell.set(content: datasource[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let content = datasource[indexPath.row]
content.expanded = !content.expanded
tableView.reloadRows(at: [indexPath], with: .automatic)
}
You need to collapse all cells and change the current clicked one state , then reload all the table
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let current = datasource[indexPath.row].expanded
datasource.forEach { $0.expanded = false }
let content = datasource[indexPath.row]
content.expanded = !current
tableView.reloadData()
}

UITableView leaves pattern of blank cells after deleting one or more rows

TableView leaving blank cells
Repeating blank cells
When deleting rows from the bottom
After deleting a row or multiple rows in my TableView, the TableView Cells seems to shift or refresh in an odd way that creates multiple blank rows. Seems to start with rows that are off-screen.
I have tried using beginUpdates, endUpdates, and performBatchUpdates with no change in behavior. I have also confirmed that the data source array is being updated properly and so is the number of rows in the tableview.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return paymentsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! UserPaymentCell
let payment = paymentsArray[indexPath.row]
cell.payment = payment
cell.selectionStyle = .none
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset = UIEdgeInsets(top: 0, left: 75, bottom: 0, right: 0)
cell.layoutMargins = UIEdgeInsets.zero
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
let payment = paymentsArray[indexPath.row]
if payment.payerUID == Auth.auth().currentUser?.uid {
return true
} else {
return false
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
let payment = paymentsArray[indexPath.row]
switch editingStyle {
case .delete:
deleteBillAndRefreshTotals(bill: payment, indexPath: indexPath)
default:
return
}
}
func deleteBillAndRefreshTotals(bill: Bill, indexPath: IndexPath) {
print("DELETING CELL")
paymentsArray.remove(at: indexPath.row)
paymentsTableView.deleteRows(at: [indexPath], with: .automatic)
print(paymentsTableView.numberOfRows(inSection: 0))
}
Expected results - for row to be deleted and all cells above or below the deleted cell to shift together.
override func prepareForReuse() {
super.prepareForReuse() // <--
self.nameLabel.text = nil
self.backgroundColor = .white
}
Within my custom cell implementation, the above function was being called without calling super.prepareForReuse first. Therefore causing the issues above.
after perform delete operations call reloaddata method so after that tableview will refresh.
func deleteBillAndRefreshTotals(bill: Bill, indexPath: IndexPath) {
print("DELETING CELL")
paymentsArray.remove(at: indexPath.row)
paymentsTableView.reloaddata()
print(paymentsTableView.numberOfRows(inSection: 0))
}
You can try this code :
paymentsTableView.beginUpdates()
paymentsTableView.deleteRows(at: [indexPath], with: .automatic)
paymentsTableView.endUpdates()

Tableview inside another table view with dynamic height for inner tableview

im new to iOS development.The thing im trying to achieve is to have a tableView inside another tableView and must have selection for items in that inner tableView. There are some problems facing when i implemented this.
i need to have a dynamic table view height (ie,for the first time loading ,inner table view should only have a height to show 3 cells and on clicking a show more button in the outer tableview cell ,the outer table view cell will expand and show the remaining cells in the inner table view)
On selecting a cell in the inner tableView, the same cell in the inner table view cell all other cells are also getting selected.
Im attaching the code i did and image of what im trying to achieve.
Please click here to see the desing i want to achieve
Outer Main Table View methods
extension Explore:UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 13
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = exploreTableView.dequeueReusableCell(withIdentifier: "exploreCell", for: indexPath) as! ExploreAlbumPosting
if isExpanded{
cell.showMoreBtn.setTitle("Show Less", for: .normal)
cell.showMoreBtn.setImage(#imageLiteral(resourceName: "ic_arrow_drop_up"), for: .normal)
}
else{
cell.showMoreBtn.setTitle("Show More", for: .normal)
cell.showMoreBtn.setImage(#imageLiteral(resourceName: "ic_arrow_drop_down"), for: .normal)
}
cell.showMoreBtn.addTarget(self, action: #selector(showMoreBtnTapped(sender:)), for: .touchUpInside)
cell.showMoreBtn.tag = indexPath.row
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if isExpanded && selectedIndex == indexPath{
return 180 * numberOfRowsInInnerTableView
}
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 215
}
}
show More Btn Tapp action
func showMoreBtnTapped(sender:UIButton){
let row = sender.tag
self.selectedIndex = IndexPath(item: row, section: 0)
isExpanded = !isExpanded
self.exploreTableView.reloadRows(at: [selectedIndex!], with: .automatic)
self.exploreTableView.scrollToRow(at: selectedIndex!, at:UITableViewScrollPosition.middle, animated: true)
}
inner Table view Methods ( This is extended to the customcell class - UITableViewCell of outer table view)
extension ExploreAlbumPosting:UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = innerAlbumTableView.dequeueReusableCell(withIdentifier: "ExploreAlbumInnerCell", for: indexPath)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 92
}
}
Please help me.Thank you

How to Change UITableViewCell's Height on Tap?

I'm trying to have a UITableViewCell reveal more content about a particular cell whenever that specific cell is tapped. Once the cell has expanded and is tapped again, the cell should shrink back to its original size.
I'm pretty sure something has to be done with the delegates heightForRowAtIndexPath and didSelectRowAtIndexPath, but I don't know how to select a specific table cell row using didSelectRowAtIndexPath.
// Height of table cell rows
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 45
}
//On cell tap, expand
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.rowHeight = 75;
}
Also, is it possible to hide any content that's overflowing from the parent cell? Kind of like overflow: hidden; in CSS.
Declare a global variable NSInteger type and store row on tableview selected in didSelectRowAtIndexPath and also reload here . After check row in heightForRowAtIndexPath and increase height there .
Try Like this
var selectedIndex : NSInteger! = -1 //Delecre this global
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == selectedIndex{
selectedIndex = -1
}else{
selectedIndex = indexPath.row
}
tableView.reloadData()
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == selectedIndex
{
return 75
}else{
return 45
}
}
What you have to do is to save the index of the selected Cell in didSelectRow method. and also have to begin/end updates on table view. This will reload some part of tableview. and will call heightForRow method. In that method you can check that if your row is selected one then return expandedHeight, otherwise return the normal height
In height for row:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if self.selectedSortingRow == indexPath.row {
return ExpandedRowHeight
}
else{
return normalHeight
}
}
In didSelect Row:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.beginUpdates()
selectedSortingRow = (int) indexPath.row
self.tableView.endUpdates()
}
Swift 4 answer with recommended comments. No need to make a new variable, tableView holds a value for selectedRow indexPath.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if self.tableView.indexPathForSelectedRow?.row == indexPath.row {
return 75;
} else {
return 45;
}
}
Swift 4. Animated resize with multiple selection. Based on Ryderpro answer.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.beginUpdates()
tableView.endUpdates()
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.beginUpdates()
tableView.endUpdates()
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.contains(indexPath) {
return 75
} else {
return 45
}
}

Resources