I followed this app to build a table with custom cells: https://github.com/awseeley/Custom-Table-Cell
I am trying to expand a cell and show different content when the cell is clicked.
Here is what I have in my view controller:
var cellTapped:Bool = true
var currentRow = 0;
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//CODE TO BE RUN ON CELL TOUCH
let selectedRowIndex = indexPath
currentRow = selectedRowIndex.row
let cell:TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TblCell
cell.image.hidden = true
tableView.beginUpdates()
tableView.endUpdates()
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == currentRow {
if cellTapped == false {
cellTapped = true
return 141
} else {
cellTapped = false
return 70
}
}
return 70
}
The cell expands when it is clicked and shrinks when it is clicked again. But the image does not hide. How do I get the image to hide? Is there a better way to show another custom .xib file when the cell is selected and have an expand/collapse effect?
You should add this implement below to your cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == currentRow {
if cellTapped == false {
cell.image.hidden = false
} else {
cell.image.hidden = true
}
}
return cell
}
And didSelectRowAtIndexPath you should remove wrong code:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//CODE TO BE RUN ON CELL TOUCH
let selectedRowIndex = indexPath
currentRow = selectedRowIndex.row
tableView.reloadData()
}
If you want use want use tableView.beginUpdate() for better animation, you can reference to my demo:
Demo hide image on cell
Hope this help!
I'm not familiarized with it, but you can look at Stack View. I think it's can help you doing what you want:
http://www.raywenderlich.com/114552/uistackview-tutorial-introducing-stack-views
Related
How can I disable interaction with all cells except the selected one, and then enable the interaction after cell is selected again?
I tried like this inside didSelectRowAtIndexPath method:
if cell.isExpanded
{
UIView.animateWithDuration(0.3) {
cell.contentView.layoutIfNeeded()
self.tableView.userInteractionEnabled = false
cell.userInteractionEnabled = true
}
}
But this will disable the entire tableView including the currently selected cell.
As of first your tableView need to select cell, after that one of the cell is selected you want other cell not to select except that selected cell, for that you can create one instance property of type NSIndexPath and use this to store inside didSelectRowAtIndexPath, and compare its value inside cellForRowAtIndexPath like this way.
var selectedIndexPath: NSIndexPath?
cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! CustomCell
if (self.selectedIndexpath != nil) {
if self.selectedIndexpath == indexPath {
cell.userInteractionEnabled = true
}
else {
cell.userInteractionEnabled = false
}
}
else {
cell.userInteractionEnabled = true
}
return cell
}
didSelectRowAtIndexPath
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if self.selectedIndexpath != indexPath {
self.selectedIndexpath = indexPath
}
else {
self.selectedIndexpath = nil
}
tableView.reloadData()
}
Note: I have set the cell userInteractionEnabled to true again if you select your expanded cell.
Use
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
to findout the selected cell and store the indexPath selected in a variable and reload the tableview.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
disable all the cells except the one selected by checking the stored indexPath.
Hope this helps .
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 have a UITableView with 3 prototyped cells (ex. 1st cell: image, 2nd cell: Description, 3. Links,...).
I would like to hide them if for a cell the data from the backend is empty (Ex. if there is no image, hide the first cell). In order to do that, I have override the heightForRowAtIndexPath function in this way:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
switch (indexPath.row) {
case 0:
if event?.photo_urls.count == 0{
return 0
}
else{
return 80.0
}
case 1:
if event?.description == ""{
return 0
}
else{
return 90.0
}
default:
return 100.0
}
}
and hidden the cell by doing
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch (indexPath.row) {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("PhotoCell", forIndexPath: indexPath) as! UITableViewCell
if event?.photo_urls.count != 0 {
// ...
}
else{
cell.hidden = true
}
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("DesCell", forIndexPath: indexPath) as! UITableViewCell
if event?.description != "" {
// ...
}
else{
cell.hidden = true
}
return cell
default:
let cell = tableView.dequeueReusableCellWithIdentifier("PhotoCell", forIndexPath: indexPath) as! UITableViewCell
return cell
}
}
Until here no problem, it works properly!
Now, THE PROBLEM is that I would like to make the cells dynamics according to the cell contents (ex. description height). In order to do that, I have used
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 80.0
}
and if I comment the heightForRowAtIndexPath the cells are actually dynamics but I can't hide them anymore.
Do you have any suggestion on how to be able to hide the cells if they are empty and apply the automatic dimension according to their content?
lets say you have dynamic data and you want to show it in tableview so you need to create an array of your data to display.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet
var tableView: UITableView
var items: [String] = ["We", "Heart", "nothing" ,"" ,"imageurl", "", "xyz"]
override func viewDidLoad() {
super.viewDidLoad()
reloadTableAfterSorting()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func reloadTableAfterSorting(){
for var i = 0; i < self.items.count; i++
{
if self.items[i] == ""{
self.items.removeAtIndex(2)
}
}
self.tableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
For that i recommend you to sort the array before displaying it in the table view. Hiding the cell is not a good idea and its not good according to Apple recommendations. So you can do one thing except hiding the cell: remove the index from the array. In this way you can always have data to show in table and it will behave properly. So don’t try to hide the cell just pop the index from array.
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 an issue with TableViewCell height in iOS 7.1 when the checkmark is active in a row. When checkmark is on, the text of a cell is reordered and sometimes is put out of the bottom cell margin (see images), even if I resize the text label in Interface Builder.
Checkmark off:
Checkmark on:
This is my code for cells:
// MARK: Sections
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return arrayDomande.count
}
// MARK: Cells
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// 1 question & 5 answers
return 1 + 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier(QuestionCellIdentifier) as QuestionCellTableViewCell
configureCell(cell, forTableView: tableView, atIndexPath: indexPath)
cell.userInteractionEnabled = false
return cell
default:
let cell = tableView.dequeueReusableCellWithIdentifier(AnswerCellIdentifier) as AnswerCellTableViewCell
configureCell(cell, forTableView: tableView, atIndexPath: indexPath)
return cell
}
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier(QuestionCellIdentifier) as QuestionCellTableViewCell
configureCell(cell, forTableView: tableView, atIndexPath: indexPath)
cell.layoutSubviews()
return (cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 10.0)
default:
let cell = tableView.dequeueReusableCellWithIdentifier(AnswerCellIdentifier) as AnswerCellTableViewCell
configureCell(cell, forTableView: tableView, atIndexPath: indexPath)
cell.layoutSubviews()
return (cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 10.0)
}
}
func configureCell(cell: UITableViewCell, forTableView: UITableView, atIndexPath: NSIndexPath) {
if (cell.isKindOfClass(QuestionCellTableViewCell)){
let domanda = arrayDomande[atIndexPath.section]
var attrs = [NSFontAttributeName: UIFont.boldSystemFontOfSize(19.0)]
var qString = NSMutableAttributedString(string: domanda.numero.stringValue + ". ", attributes:attrs)
var dString = NSMutableAttributedString(string: domanda.domanda)
qString.appendAttributedString(dString)
(cell as QuestionCellTableViewCell).testoLabel.attributedText = qString
} else if (cell.isKindOfClass(AnswerCellTableViewCell)) {
// Answers cache
// Add answers to arrayRisposte only if aren't already present
let domanda = arrayDomande[atIndexPath.section]
if arrayRisposte.indexForKey(atIndexPath.section) == nil {
let rispXDomanda: [Risposta] = domanda.risposte.allObjects as [Risposta]
arrayRisposte[atIndexPath.section] = rispXDomanda.shuffled()
}
let answers: [Risposta] = arrayRisposte[atIndexPath.section]!
(cell as AnswerCellTableViewCell).testoLabel.text = answers[atIndexPath.row - 1].risposta
(cell as AnswerCellTableViewCell).selectionStyle = UITableViewCellSelectionStyle.None
if (forTableView.indexPathsForSelectedRows() != nil) && (contains(forTableView.indexPathsForSelectedRows() as [NSIndexPath], atIndexPath)) {
(cell as AnswerCellTableViewCell).accessoryType = UITableViewCellAccessoryType.Checkmark
} else {
(cell as AnswerCellTableViewCell).accessoryType = UITableViewCellAccessoryType.None
}
}
}
// MARK: Select & Deselect
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
var inpSelectedRow = tableView.indexPathsForSelectedRows()
if inpSelectedRow != nil {
for selectedIndexPath in inpSelectedRow! {
if (selectedIndexPath.section == indexPath.section){
tableView.deselectRowAtIndexPath(selectedIndexPath as NSIndexPath, animated: false)
tableView.cellForRowAtIndexPath(selectedIndexPath as NSIndexPath)?.accessoryType = UITableViewCellAccessoryType.None
}
}
}
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.Checkmark
tableView.cellForRowAtIndexPath(indexPath)?.layoutSubviews()
return indexPath
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.None
}
There is a way, when checkmark is added, to change height of cells based on new cell heights or a way to make text label fixed?
Thank you guys!
Andrea
When checkmark is added. It will get added in accessory view. Accessory view takes some width which reduces the width of your label in the cell. So adjust your labels frame accordingly.