I Want to add a UIButton to my UITableViewCell.My cells expands in height when it is selected.. The button must only be displayed when didSelectRow is called in the expanded area ... I have 4 cells that I want to populate with different questions, forms and buttons. Should I sub-class each cell?
This is my code so far:
let SelectedCellHeight: CGFloat = 500.0
let UnselectedCellHeight: CGFloat = 44.0
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let logItemCell = tableView.dequeueReusableCellWithIdentifier("LogCell", forIndexPath: indexPath) as! UITableViewCell
}
// Used to expand cell when selected
func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
if let selectedCellIndexPath = selectedCellIndexPath {
if selectedCellIndexPath == indexPath {
return SelectedCellHeight
}
}
return UnselectedCellHeight
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
if let selectedCellIndexPath = selectedCellIndexPath {
if selectedCellIndexPath == indexPath {
self.selectedCellIndexPath = nil
} else {
self.selectedCellIndexPath = indexPath
}
} else {
selectedCellIndexPath = indexPath
}
tableView.beginUpdates()
tableView.endUpdates()
}
Yes, you should make 4 prototype cells with different identifier and class and return the one you want in 'cellForRowAtIndexPath' depending on the indexPath.
You should subclass each cell that you want. And when didSelectRow is called and the cell is expanded button.hidden = false should be called
Related
What I'm trying to achieve is, when a cell in UITableview is tapped, it will expand and display custom cell1, other cells will remain custom cell2.
What I achieved so far is expand the cell, but the custom cell will not change.
After initial investigation, I thought it's the currentRow value wasn't changed. But then I realized if it isn't changed, the row won't expand. Thank you for your help.
Here are the code:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var currentRow = 0
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == currentRow {
let cell:selectedWeatherViewCell = self.weatherCityTable.dequeueReusableCellWithIdentifier("selectedWeatherCell") as! selectedWeatherViewCell
cell.selectedCityLabels.text = city[indexPath.row]
cell.selectedTempLabels.text = tempertureF[indexPath.row]
cell.backgroundColor = UIColor.redColor()
return cell
} else {
let cell:cityWeatherViewCell = self.weatherCityTable.dequeueReusableCellWithIdentifier("cityWeatherViewCell") as! cityWeatherViewCell
cell.cityLabels.text = city[indexPath.row]
cell.tempLabels.text = tempertureF[indexPath.row]
cell.backgroundColor = UIColor.orangeColor()
return cell
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedRowIndex = indexPath
currentRow = selectedRowIndex.row
tableView.beginUpdates()
tableView.endUpdates()
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == currentRow {
return 260
} else {
return 100
}
}
}
You need to reload the affected row(s) in order for the cell type to change -
var currentRow:Int?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var reloadRows=[NSIndexPath]()
if self.currentRow != nil && indexPath.row != self.currentRow! {
reloadRows.append(NSIndexPath(forRow: self.currentRow!, inSection: indexPath.section))
}
self.currentRow=indexPath.row
reloadRows.append(NSIndexPath(forRow: self.currentRow!, inSection: indexPath.section))
tableView.reloadRowsAtIndexPaths(reloadRows, withRowAnimation: UITableViewRowAnimation.Automatic)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
I'm trying to add a UITableViewCell to another UITableView section whenever a button on the cell is tapped. However, I'm quite confused about the process of how to change a cell's section location after it has already been loaded into the table view. Currently I have two sections and am adding 5 custom UITableViewCells into the first section.
Any ideas on how to move the cells to the second section on tap?
Here are cell and section methods in my view controller class:
var tableData = ["One","Two","Three","Four","Five"]
// Content within each cell and reusablity on scroll
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var tableCell : Task = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! Task
tableCell.selectionStyle = .None
tableView.separatorStyle = UITableViewCellSeparatorStyle.None
var titleString = "Section \(indexPath.section) Row \(indexPath.row)"
tableCell.title.text = titleString
println(indexPath.row)
return tableCell
}
// Number of sections in table
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
// Section titles
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "First Section"
} else {
return "Second Section"
}
}
// Number of rows in each section
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return tableData.count
} else if section == 1 {
return 0
} else {
return 0
}
}
You need to have a separate datasource for first and second sections. When button is tapped, modify datasource and move cell to new section with moveRowAtIndexPath(indexPath: NSIndexPath, toIndexPath newIndexPath: NSIndexPath) UITableView method.
For example:
var firstDataSource = ["One","Two","Three","Four","Five"]
var secondDataSource = [ ]
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return section == 0 ? firstDataSource.count : secondDataSource.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = indexPath.section == 0 ? firstDataSource[indexPath.row] : secondDataSource[indexPath.row]
return cell
}
// For example, changing section of cell when click on it.
// In your case, similar code should be in the button's tap event handler
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
if indexPath.section == 0
{
let data = firstDataSource[indexPath.row]
tableView.beginUpdates()
secondDataSource.append(data)
firstDataSource.removeAtIndex(indexPath.row)
let newIndexPath = NSIndexPath(forRow: find(secondDataSource, data)!, inSection: 1)
tableView.moveRowAtIndexPath(indexPath, toIndexPath: newIndexPath)
tableView.endUpdates()
}
}
I have the following code:
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yearsList.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if let aIndexPath = selectedIndexPath {
if (aIndexPath != indexPath) {
return kPauYearCellCollpasedHeight
} else {
let height : CGFloat = PauYearTableViewCell.buttonsContainerHeight(examsList)
return height
}
} else {
return kPauYearCellCollpasedHeight
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("PauYearCell", forIndexPath: indexPath) as! PauYearTableViewCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
cell.backgroundView = nil
cell.separatorInset = UIEdgeInsetsZero;
cell.yearLabel.text = yearsList[indexPath.row]
cell.examsList = examsList
cell.checkHeight(selectedIndexPath == indexPath)
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.beginUpdates()
if let theSelectedIndexPath = selectedIndexPath {
if (indexPath == theSelectedIndexPath) {
selectedIndexPath = nil
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
} else {
let previousIndexPath = theSelectedIndexPath
selectedIndexPath = indexPath
self.tableView.reloadRowsAtIndexPaths([previousIndexPath], withRowAnimation: UITableViewRowAnimation.None)
self.tableView.reloadRowsAtIndexPaths([selectedIndexPath!], withRowAnimation: UITableViewRowAnimation.None)
}
} else {
selectedIndexPath = indexPath
self.tableView.reloadRowsAtIndexPaths([selectedIndexPath!], withRowAnimation: UITableViewRowAnimation.None)
}
self.tableView.endUpdates()
}
I use it in order to expand/collapse the selected UITableViewCell in order to show some buttons below the cell title.
But I can't make it work right. I have tried every solution I have found but when I tap on a cell, some cells of the UITableViewCell blinks for a moment causing a strange visual effect.
Here you can see a video reproducing the error:
https://youtu.be/cq0RBwEyFXI
If you can help my or point me on the right direction I will be very pleasure because I am starting to get frustated
Why do you set cell.backgroundView to nil?
I'm having an issue with making an image appear when a row is selected and then making the image disappear when another row is selected. If I touch a selected row it does not get deselected – this is OK as that is the behaviour that I want. I only want the currently selected row to be deselected when I touch another row.
I am writing in Swift.
I am using Kai Engelhardt's solution to expand the selected row, as answered here.
This UIImage should appear/disappear: cellContent.ringImage.image = UIImage(named: "ring.png")
I'm guessing that my logic is wrong in the selectedCellIndexPath part below.
This is my code:
In my TVC:
class MenuViewController: UIViewController{
var selectedCellIndexPath: NSIndexPath?
let SelectedCellHeight: CGFloat = 222.0
let UnselectedCellHeight: CGFloat = 64.0
let menuItems = [
("1","test 1"),
("2","test 2"),
("3","test 3"),
("4","test 4"),
("5","test 5")]
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == menuTable {
return menuItems.count
} else {
return 0}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MenuTableViewCell
if tableView == menuTable {
let (my, section) = menuItems[indexPath.row]
cell.myLabel.text = my
cell.sectionLabel.text = section
cell.selected = true
}
}
func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
if let selectedCellIndexPath = selectedCellIndexPath {
if selectedCellIndexPath == indexPath {
return SelectedCellHeight
}
}
return UnselectedCellHeight
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MenuTableViewCell
var cellContent = tableView.cellForRowAtIndexPath(indexPath) as! MenuTableViewCell
let cellLabel = cellContent.sectionLabel.text
if let selectedCellIndexPath = selectedCellIndexPath {
if selectedCellIndexPath != indexPath {
self.selectedCellIndexPath = indexPath
cellContent.ringImage.image = UIImage(named: "ring.png")
} else {
self.selectedCellIndexPath != indexPath
cellContent.ringImage.hidden = true
tableView.deselectRowAtIndexPath(indexPath, animated: true)
// cellContent.testbutton.removeFromSuperView
}
} else {
selectedCellIndexPath = indexPath
cellContent.ringImage.hidden = true
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
tableView.beginUpdates()
tableView.endUpdates()
}
Your help is greatly appreciated!
Thanks
Mikee
It seems that self.selectedCellIndexPath != indexPath does not do the deselect mark effect you want. You may try to use tableView.indexPathsForSelectedRows() to get the currently selected indexPath, compare it with the indexPath in the argument, and then complete your logic without assigning self.selectedCellIndexPath.
(Edited)
As I find that you also need the varialbe self.selectedCellIndexPath to identify the height, you could try to convert it to a counter variable which counts the selected time of currently selected row. If it is odd, it is selected, while when it's even that you know you would deselect it and reset the counter varialbe to zero.
func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
if (indexPath == tableView.indexPathsForSelectedRows()[0] && self.counter % 2 == 1) {
return SelectedCellHeight
}
return UnselectedCellHeight
}
I have to resize a single row of my tableView when clicked. How I can do this? Anybody could help me?
My view controller class:
class DayViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var daysWorkPointTable: UITableView
override func viewDidLoad() {
super.viewDidLoad()
var nipName = UINib(nibName: "daysWorkPointsCell", bundle: nil)
self.daysWorkPointTable.registerNib(nipName, forCellReuseIdentifier: "daysWorkCell")
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView:UITableView!, heightForRowAtIndexPath indexPath:NSIndexPath) -> CGFloat {
return 75
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell = tableView.dequeueReusableCellWithIdentifier("daysWorkCell", forIndexPath: indexPath) as daysWorkPointsCell
return cell
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
}
}
First you have to keep track of the indexPath of currently selected cell in a property:
var selectedCellIndexPath: NSIndexPath?
It should be an optional, because you can have no cell selected.
Next lets declare heights for selected and unselected state (change the values to whatever you want):
let selectedCellHeight: CGFloat = 88.0
let unselectedCellHeight: CGFloat = 44.0
Now you have to implement tableView(_:, heightForRowAtIndexPath:):
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if selectedCellIndexPath == indexPath {
return selectedCellHeight
}
return unselectedCellHeight
}
Now in your tableView(_:, didSelectRowAtIndexPath:) method you have to check wether the selected row or an unselected row has been tapped:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if selectedCellIndexPath != nil && selectedCellIndexPath == indexPath {
selectedCellIndexPath = nil
} else {
selectedCellIndexPath = indexPath
}
tableView.beginUpdates()
tableView.endUpdates()
if selectedCellIndexPath != nil {
// This ensures, that the cell is fully visible once expanded
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .None, animated: true)
}
}
The beginUpdates() and endUpdates() calls are giving you an animated height change.
If you want to change the duration of the height change animation you can wrap the beginUpdates() and endUpdates() calls in an animation block UIView.animationWithDuration(...) and set it to whatever value you want.
You can check out this sample project which demonstrates this code in action.