Switching Custom Cells in UITableView in swift - ios

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)
}

Related

How to use the same UIPickerView for multiple times with different data ? - Swift

I have one UIPickerView and one label inside expandable UITableViewCell and in the code I made 1 section and 3 cells all like first cell.
I did manage the data for every cell but my problem is when I select something from the first picker and I go to second picker and select something , my fist selection will be changed to same row order from the second picker .
To make it short how can I make the 3 pickers separate from each other and change every cell's label to the selected row from the same cell not else
This is a screenshot to my view :
And this my code :
import UIKit
let cellID = "cell"
class callViewController: UITableViewController {
var selectedIndexPath : NSIndexPath?
var tapped = false // when the first cell tapped it will be true
var flag = true
// These strings will be the data for the table view cells
let sections: [String] = ["Department:", "Team:", "Service:"]
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
//number of sections
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
// header for the secation
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Required Info:"
}
//number of rows in section
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
//get the cell
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier(cellID, forIndexPath: indexPath) as! callCustomCell
cell.titleLabel.text=self.sections[indexPath.row]
// now reload the appropriate pickerData
if indexPath.row == 0{
cell.inputArrry=cell.departments
cell.picker.reloadAllComponents()
}
else if indexPath.row == 1{
cell.inputArrry=cell.teams
cell.picker.reloadAllComponents()
}
else if indexPath.row == 2{
cell.inputArrry=cell.services
cell.picker.reloadAllComponents()
}
if indexPath.row != 0{
cell.userInteractionEnabled = tapped
if (!tapped){
cell.backgroundColor=UIColor.clearColor()
}
else {
cell.backgroundColor=UIColor.whiteColor()
}
}
if indexPath.row==0{
cell.backgroundColor=UIColor.whiteColor()
cell.userInteractionEnabled=true
}
return cell
}
// method to run when table view cell is tapped
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 && flag {
flag = false
tapped = true
tableView.reloadRowsAtIndexPaths([
NSIndexPath(forRow: 1, inSection: 0),
NSIndexPath(forRow: 2, inSection: 0)], withRowAnimation: .Automatic)
}
let previousIndexPath = selectedIndexPath
if indexPath == selectedIndexPath {
selectedIndexPath = nil
}
else {
selectedIndexPath = indexPath
}
var indexPaths : Array<NSIndexPath> = []
if let previous = previousIndexPath {
indexPaths += [previous]
}
if let current = selectedIndexPath {
indexPaths += [current]
}
if indexPaths.count > 0 {
tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
(cell as! callCustomCell).watchFrameChanges()
}
override func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
(cell as! callCustomCell).ignoreFrameChanges()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
for cell in tableView.visibleCells as! [callCustomCell] {
cell.ignoreFrameChanges()
}
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath == selectedIndexPath {
return callCustomCell.expandedHeight
} else {
return callCustomCell.defaultHeight
}
}
}
Use tags in the attribute inspector tab in the storyboards and then make if statements to change what is returned
if (pickerView.tag == 0) {
// Do something
} else if (pickerView.tag == 1 {
// Do something
} else {
// Do something
}
You could even use a switch statement too. Might look cleaner.

Hide element on select within table cell - Swift

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

Change the height of a row in UITableView causes strange glitch/blinking

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?

How to add an image when a row is selected and then remove it when it is deselected in UITableView in SWIFT

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
}

Content of cells reordered when accessoryType is changed to Checkmark

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.

Resources