Swift Expandable tableView cell buttons are not appearing upon scrolling down - ios

When scrolling off the screen with my custom cell in an expanded state the buttons are being hidden which should not be the case. Beneath the labels, 2 buttons are not appearing when scrolling up again. I'm guessing something needs to happen after the cell has been dequeued. Any help would be much appreciated.
I have a custom cell where initially the height for a row is 80, upon clicking on the cell it expands to 120. By default I have the buttons hidden like so:
#IBOutlet weak var followButton: UIButton! {
didSet {
followButton.isHidden = true
}
}
#IBOutlet weak var blockButton: UIButton! {
didSet {
blockButton.isHidden = true
}
}
I have an var expandedIndexSet : IndexSet = [] which tracks which cell has been expanded.
The method below updates indexSet accordingly.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
tableView.beginUpdates()
let cell = tableView.cellForRow(at: indexPath) as! StackOverflowTableViewCell
if(expandedIndexSet.contains(indexPath.row)){
expandedIndexSet.remove(indexPath.row)
} else {
expandedIndexSet.insert(indexPath.row)
}
cell.blockButton.isHidden = !cell.blockButton.isHidden
cell.followButton.isHidden = !cell.followButton.isHidden
tableView.endUpdates()
}
The height gets adjusted like so:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if expandedIndexSet.contains(indexPath.row) {
return 140
}
else {
return 80
}
}

The following snippet might be helpful, please try.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ....
cell.followButton.isHidden = !expandedIndexSet.contains(indexPath.row)
cell.blockButton.isHidden = cell.followButton.isHidden
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// Update View Model
if(expandedIndexSet.contains(indexPath.row)){
expandedIndexSet.remove(indexPath.row)
} else {
expandedIndexSet.insert(indexPath.row)
}
// Table View Animation
tableView.beginUpdates()
tableView.reloadRows(at: indexPaths, with: .automatic)
tableView.endUpdates()
}

Related

how to set cell row height in table view if it exceed its indexpath.row?

I am trying to make a FAQ view controller using table view, I need little bit fix in the UI. here is the display of my FAQ VC right now
(please ignore the red line)
as we can see, basically there are 2 row height, 80 & 160. if the row is tapped (selected) the row height will expand from 80 (yellow line) to 160 (purple line).
I want to make the row height under the last question is still 80 not 160. I have tried but I can't set the row below the last question. here is my code I use
class FAQVC: UIViewController {
#IBOutlet weak var retryButton: DesignableButton!
#IBOutlet weak var tableView: UITableView!
var FAQs = [FAQ]()
var selectedIndexs: [IndexPath: Bool] = [:]
override func viewDidLoad() {
super.viewDidLoad()
getFAQData()
}
}
extension FAQVC : UITableViewDelegate, UITableViewDataSource {
// MARK: - Table View Delegate and Datasource
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return FAQs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FAQCell") as! FAQCell
cell.FAQData = FAQs[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if self.cellIsSelected(indexPath: indexPath) {
return 160
} else {
return 80
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let isSelected = !self.cellIsSelected(indexPath: indexPath)
selectedIndexs[indexPath] = isSelected
tableView.beginUpdates()
tableView.endUpdates()
}
func cellIsSelected(indexPath: IndexPath) -> Bool {
if let number = selectedIndexs[indexPath] {
return number
} else {
return false
}
}
}
Please put this code in your viewDidLoad() method of your controller.
YOUR_TABLE_VIEW.tableFooterView = UIView(frame: .zero)
this will remove extra separators.
Quickest way to do this would be to add an extra empty cell at the end with row height 80
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return FAQs.count + 1
}
Also, make sure you make a change to cellForRowAt method to accommodate this :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row < FAQs.count {
let cell = tableView.dequeueReusableCell(withIdentifier: "FAQCell") as! FAQCell
cell.FAQData = FAQs[indexPath.row]
return cell
}
return UITableViewCell()
}
EDIT :
I just read that you don't really require separators after the last cell. In that case look here https://spin.atomicobject.com/2017/01/02/remove-extra-separator-lines-uitableview/

Resize cell when click on custom button in Swift 4

I have a view controller in which i have used table view controller in it. In my cell there is a button on which when user click the cell size should come to 550 and when click again it should come back to its original height. I have tried bit code after searching for it but it isn't working is their any solution that can work for me?. My code is bit this,
var indexOfCellToExpand: Int!
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == indexOfCellToExpand {
return 170 + expandedLabel.frame.height - 38
}
return 170
}
Use Auto layout for Expand-collapse Cell,
Attaching Demo for that case
link:https://www.dropbox.com/s/ieltq0honml35l8/TAbleDemo.zip?dl=0.
set cell button height constraint and update constraint on Value on select- deselect event and just reload data
in main viewcontroller code
self.tblView.estimatedRowHeight = 100
self.tblView.rowHeight = UITableViewAutomaticDimension
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TblCell", for: indexPath) as! TblCell
cell.btnExpandCollepse.addTarget(self, action: #selector(ViewController.expandCollapse(sender:)), for: .touchUpInside)
return cell
}
#objc func expandCollapse(sender:UIButton) {
self.tblView.reloadData()
}
Code in Cell Class
#IBOutlet var btnExpandCollepse: UIButton!
#IBOutlet var constraintBtnHeight: NSLayoutConstraint!
#IBAction func onExpandCollepse(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if !sender.isSelected{
self.constraintBtnHeight.constant = 50
}else{
self.constraintBtnHeight.constant = 500
}
}
for constraint check below image
http://prntscr.com/hur8ym
Updated Demo for custom Content height of Lable with Autoresizing Cell
https://www.dropbox.com/s/o742kflg5yeofb8/TAbleDemo%202.zip?dl=0
https://www.dropbox.com/s/o742kflg5yeofb8/TAbleDemo%202.zip?dl=0
Swift 4.x
fileprivate var expandedIndexSet = Set<IndexPath>()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if expandedIndexSet.contains(indexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "CELL_EXPANDED", for: indexPath) as! CellExpanded
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "CELL_COLLAPSED", for: indexPath) as! CellCollapsed
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
if expandedIndexSet.contains(indexPath) {
expandedIndexSet.remove(indexPath)
} else {
expandedIndexSet.insert(indexPath)
}
tableView.reloadRows(at: [indexPath], with: .fade)
}

Expanded and Swipe-able cell in TableView

I have TableView, where users can click and expand each cell (UI: click). This is the code for that (also I created .xib file to layout this cell):
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
var selectedIndex: IndexPath?
var isExpended = false
func didExpandCell() {
self.isExpended = !isExpended
self.tableView.reloadRows(at: [selectedIndex!], with: .automatic)
}
#IBOutlet weak var tableView: UITableView!
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "wordCell", for: indexPath) as! CellController
if searchController.isActive {
cell.word.text = filtered[indexPath.row].word
cell.translation.text = filtered[indexPath.row].translation
cell.date.text = filtered[indexPath.row].fullDate
cell.exOne.text = filtered[indexPath.row].exOne
cell.exTwo.text = filtered[indexPath.row].exTwo
} else {
cell.word.text = words[indexPath.row].word
cell.translation.text = words[indexPath.row].translation
cell.date.text = words[indexPath.row].fullDate
cell.exOne.text = words[indexPath.row].exOne
cell.exTwo.text = words[indexPath.row].exTwo
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedIndex = indexPath
self.didExpandCell()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if isExpended && self.selectedIndex == indexPath {
return 180
}
return 70
}
}
Also, all Cells are swipe-able. This is the code:
func tableView(_ tableView: UITableView, editActionsForRowAt: IndexPath) -> [UITableViewRowAction]? {
let edit = UITableViewRowAction(style: .normal, title: "Edit") { action, index in
print("Edit")
}
edit.backgroundColor = .blue
let delete = UITableViewRowAction(style: .normal, title: "Delete") { action, index in
print("Delete")
}
delete.backgroundColor = .red
return [delete, edit]
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
The problem:
When I swipe Cell, it always expends in a half, this is my UI after swiping: click. Is it possible not to expand Cell if I swipe them?
Excuse me, but the problem was that I haven't set constraints for expanded version of the cell. That's why this labels were moving with swipe menu. Now it works.
Thank you for all your replies!

Change image on selection in UITableViewCell(swift 3 xcode)

I have an imageView inside my tableViewCell and i would like to have its image changed on selection. This is the code I have for it:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let myCell = tableView.cellForRow(at: indexPath) as! TableCell
myCell.resourceIcons.image = UIImage(named: "RubiusResources2")
tableView.deselectRow(at: indexPath, animated: true)
}
The code works, but some of the other rows in a different section further down the tableView also seem change.
EDIT:
Using the comments bellow I came to the following solution:
I first created a 2D bool array to the amount of sections and rows my table had and set them all to false.
var resourceBool = Array(repeating: Array(repeating:false, count:4), count:12)
I then created an if statement to check if the array at indexPath was false or true. This would be where the states of the image would change.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! TableCell
if (global.resourceBool[indexPath.section][indexPath.row] == false) {
myCell.resourceIcons.image = global.systemResourceImages[0]
} else if (global.resourceBool[indexPath.section][indexPath.row] == true) {
myCell.resourceIcons.image = global.systemResourceImages[1]
}
return myCell
}
Then, in the didSelectRow function I change the array at indexPath to true and reload the tableView data.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
global.resourceBool[indexPath.section][indexPath.row] = true
tableView.reloadData()
tableView.deselectRow(at: indexPath, animated: true)
}
From my understanding, the states of an object must always be in the cellForRow.
One of the solution would be that you need to maintain a seperate list of rows you selected, compare them in cellForRowAt method.
Code would look something like this.
var selectedArray : [IndexPath] = [IndexPath]()
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let myCell = tableView.cellForRow(at: indexPath) as! TableCell
myCell.resourceIcons.image = UIImage(named: "RubiusResources2")
tableView.deselectRow(at: indexPath, animated: true)
if(!selectedArray.contains(indexPath))
{
selectedArray.append(indexPath)
}
else
{
// remove from array here if required
}
}
and then in cellForRowAt, write this code to set proper images
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
.
.
.
if(selectedArray.contains(indexPath))
{
// use selected image
}
else
{
// use normal image
}
.
.
.
}

User Creation Form just Like iOS Contact Creation working behind this? [duplicate]

My table view can expand and collapse cells when they are pressed, but the content that appears when the cell expands loads before the animation is finished.
What I am left with is this:
What I would like it to look like is this example. This content appears as if it were behind a curtain and the cell expansion animation just reveals it.
Here is the code that controls the table view:
class HistoryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
var expandedIndexPath: NSIndexPath? // Index path of the cell that is currently expanded
let collapsedHeight: CGFloat = 44.0 // Constant to set the default collapsed height
var ticketHistoryService = TicketHistoryService() // Service to gather info about Ticket History CoreData
var tickets = [Ticket]()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Remove any appended table view cells
tableView.tableFooterView = UIView()
self.tickets = self.ticketHistoryService.fetchData() // Load inital data
}
// MARK: - Table View Methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tickets.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! HistoryTableViewCell
let ticket = self.tickets[indexPath.row]
cell.titleLabel!.text = ticket.ticketNumber
return cell
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
self.ticketHistoryService.removeObject(indexPath.row)
self.tickets = self.ticketHistoryService.fetchData()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.beginUpdates()
let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! HistoryTableViewCell
if indexPath.isEqual(self.expandedIndexPath){ // If currently selected cell was just previously selected
self.expandedIndexPath = nil
cell.commentLabel.hidden = true
}
else {
self.expandedIndexPath = indexPath
cell.commentLabel.hidden = false
}
self.tableView.endUpdates()
}
func tableView(tableView: UITableView, willDeselectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! HistoryTableViewCell
cell.commentLabel.hidden = true
return indexPath
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.isEqual(self.expandedIndexPath) {
return UITableViewAutomaticDimension
}
return collapsedHeight
}
}
One approach is to have your cell clip subview content that would expand outside of itself:
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! HistoryTableViewCell
cell.clipsToBounds = true

Resources