How to select a UITableview Cell automatically? - ios

I'm working on a Quiz App. I am getting questions from API. I'm using tableview for the Options.
Now when the user selects the answer for the 1st question & presses Next comes to the previous question. Then the selected answer has to remain selected.
I researched a lot and found this:
Programmatically emulate the selection in UITableViewController in Swift
But I can't automatically select the user selected answer in my table view.
This is my Present UI
VC
func getOptions(){
OptionArray.removeAll(keepCapacity: false)
Alamofire.request(.GET, "http://www.wins.com/index.php/capp/get_chapter_answers/\(EID)/\(QuestionID[Qindex])")
.responseJSON { (_, _, data, _) in
println(data)
let json = JSON(data!)
let catCount = json.count
for index in 0...catCount-1 {
let disp = json[index]["DISPLAY_STATUS"].string
if disp == "Y"{
let op = json[index]["ANSWER"].string
self.OptionArray.append(op!)
let ans = json[index]["RIGHT_ANSWER"].string
self.AnswerArray.append(ans!)
}
}
self.OptionTable.reloadData()
println(self.OptionArray.count)
}
}
#IBAction func Previous(sender: AnyObject) {
Qindex--
ShowQuestion()
}
#IBAction func Next(sender: AnyObject) {
Qindex++
ShowQuestion()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.OptionArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.OptionTable.dequeueReusableCellWithIdentifier("Option") as! OptionCell
cell.Optionlabel?.text = self.OptionArray[indexPath.row]
cell.layer.masksToBounds = true;
cell.layer.cornerRadius = 6;
cell.layer.borderWidth = 2.0
cell.layer.borderColor = colorsArray[1].CGColor
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as! OptionCell;
if currentCell.selected == true{
currentCell.layer.borderWidth = 4.0
currentCell.layer.borderColor = colorsArray[6].CGColor
println(currentCell.Optionlabel?.text)
}
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as! OptionCell;
if currentCell.selected == false{
currentCell.layer.borderWidth = 2.0
currentCell.layer.borderColor = colorsArray[1].CGColor
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 70
}
UPDATE
I have over 20 Questions. So i have to save the Selected answer for Each Questions separately.
I can't select the answer using the indexpath position because the options will change it positions randomly when it is accessed for the second time.

You can do it this way :
When you press next, store the selected answer's index into a variable and when you come back to previous, check that index in willDisplayCell method and the set your cell selected.
Take a variable in your controller
var selectedAnsIndexPath:NSIndexPath?
your next button action will be something like
#IBAction func Next(sender: AnyObject) {
self.selectedAnsIndexPath = tableView.indexPathForSelectedRow()
Qindex++
ShowQuestion()
}
and then
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if(indexPath == self.selectedAnsIndexPath)
{
cell.setSelected(true, animated: false)
}
}
Try this, it may work for you!
UPDATE
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.OptionTable.dequeueReusableCellWithIdentifier("Option") as! OptionCell
cell.Optionlabel?.text = self.OptionArray[indexPath.row]
cell.QueID = QuestionID[Qindex]
cell.layer.masksToBounds = true;
cell.layer.cornerRadius = 6;
cell.layer.borderWidth = 2.0
cell.layer.borderColor = colorsArray[1].CGColor
if let val = examDic[cell.QueID]
{
if self.OptionArray[indexPath.row] == val
{
selectedAnsIndexPath = indexPath
cell.setSelected(true, animated: true)
cell.layer.borderWidth = 4.0
cell.layer.borderColor = colorsArray[6].CGColor
}
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if selectedAnsIndexPath != nil{
OptionTable.deselectRowAtIndexPath(selectedAnsIndexPath!, animated: false)
self.tableView(OptionTable, didDeselectRowAtIndexPath: selectedAnsIndexPath!)
println(selectedAnsIndexPath!.row)
}
let indexPath = OptionTable.indexPathForSelectedRow();
let currentCell = OptionTable.cellForRowAtIndexPath(indexPath!) as! OptionCell;
if currentCell.selected == true{
currentCell.layer.borderWidth = 4.0
currentCell.layer.borderColor = colorsArray[6].CGColor
var sqid = QuestionID[Qindex]
var sanswer = currentCell.Optionlabel!.text
examDic[sqid] = sanswer!
println(examDic)
}
}

Related

Tableview header cell expand with same cardView in swift?

I have attached the image click the card view expands the same card inside the table cell dynamically its passible to achieve this?
I have searched a lot but not working
Hear my code added header cell with CardView
added arrow button to click the button expand the cell
its able expand but not in parent card it was showing diff card
I have adde my source code
var hiddenSections = Set<Int>()
let tableViewData = [
["1","2","3","4","5"],
["1","2","3","4","5"],
["1","2","3","4","5"],
]
override func viewDidLoad() {
super.viewDidLoad()
let CustomeHeaderNib = UINib(nibName: "CustomSectionHeader", bundle: Bundle.main)
historyTableView.register(CustomeHeaderNib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.tableViewData.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.hiddenSections.contains(section) {
return 0
}
return self.tableViewData[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = self.tableViewData[indexPath.section][indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return view.frame.width/4
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = self.historyTableView.dequeueReusableHeaderFooterView(withIdentifier: "customSectionHeader") as! CustomSectionHeader
header.setupCornerRadious()
let sectionButton = header.expandBtn
sectionButton?.setTitle(String(section),
for: .normal)
sectionButton?.tag = section
sectionButton?.addTarget(self,action: #selector(self.hideSection(sender:)), for: .touchUpInside)
return header
}
#objc
private func hideSection(sender: UIButton) {
let section = sender.tag
func indexPathsForSection() -> [IndexPath] {
var indexPaths = [IndexPath]()
for row in 0..<self.tableViewData[section].count {
indexPaths.append(IndexPath(row: row,
section: section))
}
return indexPaths
}
if self.hiddenSections.contains(section) {
self.hiddenSections.remove(section)
self.historyTableView.insertRows(at: indexPathsForSection(),
with: .fade)
} else {
self.hiddenSections.insert(section)
self.historyTableView.deleteRows(at: indexPathsForSection(),
with: .fade)
}
}
With out sections also you can achieve this. To do this,
1.Return cell height as section height. If user clicks on the cell then return total content height to the particular cell.
2.You need to take an array, if user selects cell, add indexPath number in to array. If selects already expand cell remove it from array. In height for row at index check indexPath is in array or not.
This is one of the way. With sections also you can do that.
//MARK:- UITableView Related Methods
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrDict.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// var cel = tblExpandedTest.dequeueReusableCellWithIdentifier("expCell", forIndexPath: indexPath) as! CDTableViewCell
var cel : CaseHearingTabTVC! = tableView.dequeueReusableCell(withIdentifier: "caseHearingTabCell") as! CaseHearingTabTVC
if(cel == nil)
{
cel = Bundle.main.loadNibNamed("caseHearingTabCell", owner: self, options: nil)?[0] as! CaseHearingTabTVC;
}
//cell?.backgroundColor = UIColor.white
cel.delegate = self
if indexPath != selctedIndexPath{
cel.subview_desc.isHidden = true
cel.subview_remarks.isHidden = true
cel.lblHearingTime.isHidden = true
}
else {
cel.subview_desc.isHidden = false
cel.subview_remarks.isHidden = false
cel.lblHearingTime.isHidden = false
}
return cel
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectIndex = true;
if(selectedInd == indexPath.row) {
selectedInd = -1
}
else{
let currentCell = tableView.cellForRow(at: indexPath)! as! CaseHearingTabTVC
cellUpdatedHeight = Float(currentCell.lblHearingTime.frame.origin.y + currentCell.lblHearingTime.frame.size.height) + 2;
selectedInd = -1
tblCaseHearing.reloadData()
selectedInd = indexPath.row
}
let previousPth = selctedIndexPath
if indexPath == selctedIndexPath{
selctedIndexPath = nil
}else{
selctedIndexPath = indexPath
}
var indexPaths : Array<IndexPath> = []
if let previous = previousPth{
indexPaths = [previous]
}
if let current = selctedIndexPath{
indexPaths = [current]
}
if indexPaths.count>0{
tblCaseHearing.reloadRows(at: indexPaths, with: UITableView.RowAnimation.automatic)
}
}
func tableView(_ tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowIndexPath indexPath:IndexPath) {
(cell as! CaseHearingTabTVC).watchFrameChanges()
}
func tableView(_ tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowIndexPath indexPath:IndexPath) {
(cell as! CaseHearingTabTVC).ignoreFrameChanges()
}
func tableView(_ TableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
if indexPath == selctedIndexPath{
return CGFloat(cellUpdatedHeight)
}else{
return CaseHearingTabTVC.defaultHeight
}
}
Best approach is to create two different cells for normal card and expanded card.
fileprivate var selectedIndex: Int?
func registerTableViewCells() {
tableView.register(UINib(nibName:Nib.CardCell , bundle: nil), forCellReuseIdentifier: "CardCell")
tableView.register(UINib(nibName:Nib.ExpandedCardCell , bundle: nil), forCellReuseIdentifier: "ExpandedCardCell")
}
override func viewDidLoad() {
super.viewDidLoad()
self.registerTableViewCells()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard let index = selectedIndex else {
return 115
}
if index == indexPath.row{
return 200
}
return 115
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let selected = selectedIndex, selected == indexPath.row{
let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandedCardCell", for: indexPath) as! ExpandedCardCell
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "CardCell", for: indexPath) as! CardCell
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedIndex == indexPath.row{
selectedIndex = nil
}
else{
selectedIndex = indexPath.row
}
UIView.performWithoutAnimation {
tableView.reloadData()
}
}

swift4 uitableview retain selection

I am preparing a small app for online exams for which I need to reload the tableView for every question. Multiple selection and single selection both are needed based on question type. I was able to manage the selection type based on question type but the thing is when i reload the tableview the previous selection is replicating in the next question.
var DictItemSerialNumber = 0
let Dict = [["Question":"How many days in a week?",
"Options":["1","2","3","7"],
"QuestionType":1],
["Question":"How many days in a month?",
"Options":["28","29","30","31","32"],
"QuestionType":2],
["Question":"How many days in a Year?",
"Options":["234","265","365","400"],
"QuestionType":1]]
var SavedOptions = [String:[Any]]()
var selectedIndex:Int? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.Tab.rowHeight = UITableView.automaticDimension
self.Tab.estimatedRowHeight = 300
self.Tab.tableFooterView = UIView()
self.Tab.delegate = self
self.Tab.dataSource = self
self.Tab.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
#IBAction func PrvsBtnActn(_ sender: UIButton) {
if DictItemSerialNumber > 0 {
DictItemSerialNumber -= 1
Tab.reloadData()
}
}
#IBAction func NextBtnActn(_ sender: UIButton) {
if DictItemSerialNumber < Dict.count-1{
DictItemSerialNumber += 1
Tab.reloadData()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let DictObj = Dict[DictItemSerialNumber] as NSDictionary
let optionsCount = DictObj["Options"] as! NSArray
return optionsCount.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Tab.dequeueReusableCell(withIdentifier: "cell")!
let DictObj = Dict[DictItemSerialNumber]
let qstnType = DictObj["QuestionType"] as! Int
if qstnType == 2{
self.Tab.allowsMultipleSelection = true
}else{
self.Tab.allowsMultipleSelection = false
}
QstnLbl.text = DictObj["Question"] as? String
let optionsAns = DictObj["Options"] as! NSArray
cell.textLabel?.text = optionsAns[indexPath.row] as? String
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
}
}
}
question type = 1 means single selection and 2 is multiple selection

UITableView Multi Selection's selected checkmark not remains checked

I have two UITableView in my application.
One is for Category and Second is for SubCategory.
On the basis of selected Category SubCategory UITableView, data will change, and SubCategory UITableView have multi-selection functionality, till this my application is working fine.
Now the problem is when I am on category UITableView and click on suppose Category cell it will redirect to the various subCategory, On that screen, I have selected multiple choices and click on back button appear on top, and when I click again on Category tab my selection(Checkmark) is disappearing.
I want my checkmark to be selected as long as I manually set them as unchecked.
How can I implement that thing?
Sample screenshot of my application attached below.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tblSubCategory.cellForRow(at: indexPath)
if cell!.isSelected
{
cell!.isSelected = false
if cell!.accessoryType == UITableViewCell.AccessoryType.none
{
if strCategoryData == "Category" {
cell!.accessoryType = UITableViewCell.AccessoryType.checkmark
let objectForCell = arrSubCategoryData[indexPath.row]
arrSelectedCetegoryIndex.append(objectForCell)
let defaults = UserDefaults.standard
defaults.set(arrSelectedCetegoryIndex, forKey: "categoryKey")
}
else if strCategoryData == "Brand" {
cell!.accessoryType = UITableViewCell.AccessoryType.checkmark
let objectForCell = arrSubCategoryData[indexPath.row]
arrSelectedBrandIndex.append(objectForCell)
}
else if strCategoryData == "Color" {
cell!.accessoryType = UITableViewCell.AccessoryType.checkmark
let objectForCell = arrSubCategoryData[indexPath.row]
arrSelectedColorIndex.append(objectForCell)
}
else if strCategoryData == "Size" {
cell!.accessoryType = UITableViewCell.AccessoryType.checkmark
let objectForCell = arrSubCategoryData[indexPath.row]
arrSelectedSizeIndex.append(objectForCell)
}
}
else
{
if strCategoryData == "Category" {
cell!.accessoryType = UITableViewCell.AccessoryType.none
let selectedIndex = (tblSubCategory.indexPathForSelectedRow?.row)!
let selectedIndexValue = arrSubCategoryData[selectedIndex]
print(selectedIndexValue)
let index = arrSelectedCetegoryIndex.firstIndex(of: selectedIndexValue)!
arrSelectedCetegoryIndex.remove(at: index)
}
else if strCategoryData == "Brand" {
cell!.accessoryType = UITableViewCell.AccessoryType.none
let selectedIndex = (tblSubCategory.indexPathForSelectedRow?.row)!
let selectedIndexValue = arrSubCategoryData[selectedIndex]
print(selectedIndexValue)
let index = arrSelectedBrandIndex.firstIndex(of: selectedIndexValue)!
arrSelectedBrandIndex.remove(at: index)
}
else if strCategoryData == "Color" {
cell!.accessoryType = UITableViewCell.AccessoryType.none
let selectedIndex = (tblSubCategory.indexPathForSelectedRow?.row)!
let selectedIndexValue = arrSubCategoryData[selectedIndex]
print(selectedIndexValue)
let index = arrSelectedColorIndex.firstIndex(of: selectedIndexValue)!
arrSelectedColorIndex.remove(at: index)
}
else if strCategoryData == "Size" {
cell!.accessoryType = UITableViewCell.AccessoryType.none
let selectedIndex = (tblSubCategory.indexPathForSelectedRow?.row)!
let selectedIndexValue = arrSubCategoryData[selectedIndex]
print(selectedIndexValue)
let index = arrSelectedSizeIndex.firstIndex(of: selectedIndexValue)!
arrSelectedSizeIndex.remove(at: index)
}
}
}
}
You are probably performing a segue to go to the sub category view controller, and every time you perform this segue, tableview delegate and datasource methods are called again and cells are initialized all over again.
For you to show your cells checked you are going to need to save the checked values in the Categories view controller and pass them to the SubCategory View Controller and set the checked values in your cellForRowAtIndexpath method.
Here is an example on how to implement that:
class CategoryViewController: UIViewController {
var checkedValues = [[Bool]]()
var indexSelected = -1
override func viewDidLoad() {
super.viewDidLoad()
// your code here
checkedValues.append(contentsOf: repeatElement([], count: yourCategArray.count))
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// your code here
indexSelected = indexPath.row
self.performSegue(withIdentifier: "yourSegueIdentifierHere", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
(segue.destination as! SubCategoryViewController).parentCategoryVC = self
}
}
Now for the other View Controller:
class SubCategoryViewController: UIViewController {
var parentCategoryVC = CategoryViewController()
override func viewDidLoad() {
super.viewDidLoad()
if parentCategoryVC.checkedValues[parentCategoryVC.indexSelected].count == 0 {
parentCategoryVC.checkedValues[parentCategoryVC.indexSelected].append(contentsOf: repeatElement(false, count: yourSubCategArray.count))
}
// your code here
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourSubCategArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell...
if parentCategoryVC.checkedValues[parentCategoryVC.indexSelected][indexPath.row] { cell.accessoryType = .checkmark } else { cell.accessoryType = .none }
// your code here
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// your code
parentCategoryVC.checkedValues[parentCategoryVC.indexSelected][indexPath.row] = !parentCategoryVC.checkedValues[parentCategoryVC.indexSelected][indexPath.row]
tableView.reloadRows(at: indexPath, with: UITableViewRowAnimation.none)
}
}
For any additional clarification feel free to ask
You need to create one Int type array and then append value on click if not in array and if already exist so you need to remove from array and set checkmark in cellForRowAt method.
Please See complete code
import UIKit
class testViewController: UIViewController {
var selectedRows: [Int] = []
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension testViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "Welcome " + (indexPath.row+1).description
cell.selectionStyle = .none
cell.accessoryType = selectedRows.contains(indexPath.row) ? .checkmark : .none
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if self.selectedRows.contains(indexPath.row) {
if let index = self.selectedRows.firstIndex(of: indexPath.row) {
self.selectedRows.remove(at: index)
}
} else {
self.selectedRows.append(indexPath.row)
}
tableView.reloadData()
}
}

Data loading from core data to table very slowly

I have table in core data with 100 rows, in tableView I load filtered 25 rows. When I open this UIViewController in app it take near 2 seconds, I think it is very slowly. I test my app on iPhone 5. May be I do something wrong?
I see that cellForRowAtIndexPath method call 4 times for all rows: 1-25, then again 1-25 etc. Is it ok?
When I load just 1 row it works fast.
class TipsViewController: UIViewController, UITableViewDelegate
{
#IBOutlet weak var tableView: UITableView!
var tips = [Tips]()
lazy var managedObjectContext : NSManagedObjectContext? =
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
if let managedObjectContext = appDelegate.managedObjectContext
{
return managedObjectContext
}
else {
return nil
}
}()
let defaults = NSUserDefaults.standardUserDefaults()
var languge:String!
override func viewDidLoad()
{
super.viewDidLoad()
languge = defaults.objectForKey("language") as! String
fetchLog()
}
func fetchLog()
{
let fetchRequest = NSFetchRequest(entityName: "Tips")
let sortDescriptor = NSSortDescriptor(key: "id", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate=NSPredicate(format: "language=%#", languge)
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [Tips]
{
tips = fetchResults
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int
{
return tips.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> TipsTableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cellt", forIndexPath: indexPath) as! TipsTableViewCell
cell.tipsTextView?.text = tips[indexPath.row].textShort
cell.tipsTextView.editable=false
cell.tipsTextView.userInteractionEnabled=false
cell.tipsTextView.textColor = UIColor(red: 0x7E/255, green: 0x7A/255, blue: 0x7F/255, alpha: 1.0)
cell.selectionStyle = UITableViewCellSelectionStyle.Default
return cell
}
func tableView(_tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
let cell = tableView(_tableView, cellForRowAtIndexPath: indexPath)
return cell.getHeight()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!)
{
if(segue.identifier == "showDetailx"){
var indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow()!
var detailViewController:TipViewController = segue.destinationViewController as! TipViewController
detailViewController.tip = tips[indexPath.row]
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
self.performSegueWithIdentifier("showDetailx", sender: self)
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath)
{
if cell.respondsToSelector("setSeparatorInset:")
{
cell.separatorInset = UIEdgeInsetsZero
}
if cell.respondsToSelector("setPreservesSuperviewLayoutMargins:")
{
cell.preservesSuperviewLayoutMargins = false
}
if cell.respondsToSelector("setLayoutMargins:")
{
cell.layoutMargins = UIEdgeInsetsZero
}
}
}
You have one problem on your estimatedHeightForRowAtIndexPath. You call there cellForRowAtIndexPath, that's why you get another calls to this method. You can remove that code and do the following:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return self.heightForBasicCellAtIndexPath(indexPath)
}
func heightForBasicCellAtIndexPath( indexPath: NSIndexPath) -> CGFloat
{
var sizingCell: UITableViewCell? = nil;
var token: dispatch_once_t = 0
dispatch_once(&token) {
sizingCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as? TipsTableViewCell
}
return sizingCell!.frame.size.height;
}
This method instantiates a sizingCell using GCD to ensure it’s created only once.
Hope this helps
I delete heightForRowAtIndexPath and estimatedHeightForRowAtIndexPath and add to viewDidLoad() this:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0

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
}

Resources