UITableView Multi Selection's selected checkmark not remains checked - ios

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

Related

pass text from selected tableView cell.row to fill textbox xcode

I have a tableView that allows users to make multiple selections from an array of data,
When the user clicks done, I would like the selected text to be then transferred over to another tableViews textView
Is there a way to transfer over the selected text and have the text separated by a , ?
I am coding programmatically.
var checked = [Int]()
var items = [String]()
var selectedItems = [String]()
#objc func done() {
let hud = JGProgressHUD(style: .dark)
hud.textLabel.text = "Saving!"
hud.show(in: view)
dismiss(animated: true, completion: nil)
hud.dismiss()
let aCell = aboutCell(style: .default, reuseIdentifier: nil)
aCell.textField3.text = selectedItems.joined(separator: ",")
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRow(at: indexPath)?.accessoryType == UITableViewCell.AccessoryType.checkmark {
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCell.AccessoryType.none
} else {
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCell.AccessoryType.checkmark
if selectedItems.contains(items[indexPath.row]) {
selectedItems.remove(at: selectedItems.firstIndex(of: items[indexPath.row])!)
} else {
selectedItems.append(items[indexPath.row])
}
checked.append(indexPath.row)
}
}
According to my understanding to the question, these are my thoughts:
1. First setup necessary variables
var items = [String]() // data to display in tableview
var selectedItems = [String]() // here all the selected datas are stored
2. Store the selected items data from the didSelectRowAt delegate method
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedItems.contains(items[indexPath.row]) { //check if the selected already contains the items and if contains remove it
selectedItems.remove(at: selectedItems.firstIndex(of: items[indexPath.row])!)
} else { // append the required items
selectedItems.append(items[indexPath.row])
}
// ..... other codes here
}
3. on done button
let requiredText = selectedItems.joined(separator: ",")
// pass this data through delegate method
There is no need to create an array for the selected items. You can simply call tableview method selectRow(at:animated:scrollPosition:) when selecting a row and when you need to get the selected rows just call tableview instance property indexPathsForSelectedRows. Then you just need to join the selected rows with a comma and use the resulting string in your textview or textfield. Don't forget to implement didDeselectRowAt item method as well to deselectRow.
import UIKit
class TableViewController: UITableViewController {
var items: [String] = ["1st", "2nd", "3rd", "4th", "5th"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = true
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
}
func done() {
if let indexPaths = tableView.indexPathsForSelectedRows {
// note that this will preserve the order that the rows where selected. Just sort the indexPaths if you need it sorted.
let string = indexPaths.map { items[$0.row] }.joined(separator: ",")
print(string)
// your code
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCellID", for: indexPath) as! TableViewCell
cell.textLabel?.text = items[indexPath.row]
cell.accessoryType = cell.isSelected ? .checkmark : .none
return cell
}
}

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

Swift: Multiple TableView checkmark - select all rows

It is posible to add a select all option in TableView Multiple checkmarks...?
I have a code ready, and I try to do this function. please help me !
CODE:
struct Area {
let name : String
var isSelected : Bool
init(name : String, isSelected : Bool = false) {
self.name = name
self.isSelected = isSelected
}
}
var areas = [Area(name: "Select all"),Area(name: "a"),Area(name: "b"),Area(name: "c"),Area(name: "d"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return areas.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let area = areas[indexPath.row]
cell.textLabel?.text = area.name
cell.accessoryType = area.isSelected ? .checkmark : .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
areas[indexPath.row].isSelected.toggle()
tableView.reloadRows(at: [indexPath], with: .none)
let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
print(selectedAreas)
}
Thanks.
You have to check if the user selected the first row ("Select all") and update the other rows accordingly:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// toggle the selected area
areas[indexPath.row].isSelected.toggle()
// save the new state for later use
let isSelected = areas[indexPath.row].isSelected
if indexPath.row == 0 {
// "Select all" was selected – update all areas
for i in 1..<areas.count {
areas[i].isSelected = isSelected
}
// update UI
tableView.visibleCells.forEach { $0.accessoryType = isSelected ? .checkmark : .none }
} else {
// update UI
tableView.cellForRow(at: indexPath)?.accessoryType = isSelected ? .checkmark : .none
}
tableView.deselectRow(at: indexPath, animated: true)
}
Recommendation
To separate concerns visually you could also use an own table view section for the "Select all" row. In that case some more changes are necessary:
var areas = [
// you do not need an area for "Select all" any longer
Area(name: "a"),
Area(name: "b"),
Area(name: "c"),
Area(name: "d")
]
var allSelected: Bool {
// helper var to see if all areas are currently selected
return areas.filter({!$0.isSelected}).isEmpty
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 1: return "Areas"
default: return nil
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: return 1 // select all
case 1: return areas.count
default:
// we should never get here
fatalError()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.selectionStyle = .none
if indexPath.section == 0 {
cell.textLabel?.text = "Select all"
cell.accessoryType = allSelected ? .checkmark : .none
} else {
let area = areas[indexPath.row]
cell.textLabel?.text = area.name
cell.accessoryType = area.isSelected ? .checkmark : .none
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
// (de-)select all
let shouldSelect = !allSelected
for i in 0..<areas.count {
areas[i].isSelected = shouldSelect
}
} else {
areas[indexPath.row].isSelected.toggle()
}
tableView.reloadRows(at: tableView.indexPathsForVisibleRows ?? [], with: .automatic)
}
First you need to enable multiple selection for the table view
tableView.allowsMultipleSelection = true
tableView.allowsMultipleSelectionDuringEditing = true
Select all rows
for section in 0..<tableView.numberOfSections {
for row in 0..<tableView.numberOfRows(inSection: section) {
let indexPath = IndexPath(row: row, section: section)
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
}
To enable checkmark whenever a row is selected:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
let area = areas[indexPath.row]
area.isSelected = true
cell.accessoryType = .checkmark
}
}
Also remove checkmark when it is deselected:
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
let area = areas[indexPath.row]
area.isSelected = false
cell.accessoryType = .none
}
}
Please vote this answer if it helps. Thanks.
First change your Area to class so it will be reference type
class Area {
let name : String
var isSelected : Bool
init(name : String, isSelected : Bool = false) {
self.name = name
self.isSelected = isSelected
}
}
Now change your didselect logic
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let area = areas[indexPath.row]
area.isSelected.toggle()
if area.name == "Select all"{
areas = areas.map{$0.isSelected = true}
}
tableView.reloadRows(at: [indexPath], with: .none)
let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
print(selectedAreas)
}
In didSelectRow:
if indexPath.row == 0 { // the first item is the select all button
// find out which areas should be selected
let indicesThatShouldBeSelected = (1..<areas.count).filter { !areas[$0].isSelected }
// update model
indicesThatShouldBeSelected.forEach { areas[$0].isSelected = true }
// update view
tableView.reloadRows(at: indicesThatShouldBeSelected.map { IndexPath(section: 0, row: $0) }, with: .none)
}
You could also just do tableView.reloadData(), which reloads the whole table view instead of just the rows that needs reloading.
If the selectAll row is at index 0 check the row, then set all isSelected members to true and reload the entire table
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
areas.indices.forEach{areas[$0].isSelected = true}
tableView.reloadData()
} else {
areas[indexPath.row].isSelected.toggle()
tableView.reloadRows(at: [indexPath], with: .none)
}
let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
print(selectedAreas)
}
And if you want to exclude the first item to be selected drop the first index
areas.indices.dropFirst().forEach{areas[$0].isSelected = true}

Can't get the data on class uitableview Swift 3

I'm having trouble presenting the data. I have copied the code from https://stackoverflow.com/a/39307841/7118403 because i want to test his method on saving the checkmark. But unfortunately I can't present the data on tableView. When i try to print the "myItems" it shows [tableViewCheckmark.Item]. I can't seem to find the solution. PS. I'm a new to programming. Thank you in advance.
class Item {
let name : String
var selected = false
init(name: String) {
self.name = name
}
}
class TableViewController: UITableViewController {
#IBOutlet var uiTableView: UITableView!
var myItems = [Item]()
override func viewDidLoad() {
uiTableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
let item = Item(name:"Foo")
myItems.append(item)
print(myItems)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
let item = myItems[indexPath.row]
cell.textLabel!.text = item.name
cell.accessoryType = item.selected ? .checkmark : .none
cell.selectionStyle = .none
cell.tintColor = UIColor.green
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
saveDefaults()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return myItems.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let item = myItems[indexPath.row]
item.selected = true
tableView.reloadRows(at: [indexPath as IndexPath], with: .none)
}
func saveDefaults() {
let selectedCells = myItems.filter { $0.selected }.map { $0.name }
let defaults = UserDefaults.standard
defaults.set(selectedCells, forKey:"selectedCells")
}
func readDefaults()
{
let defaults = UserDefaults.standard
let selectedItems = defaults.stringArray(forKey: "selectedCells")!
for item in myItems {
item.selected = selectedItems.contains(item.name)
}
tableView.reloadData()
}
}
Signature of UITableViewDataSource methods is changed in Swift 3 also you are currently passing array count in numberOfSections(in:) remove it and add below methods.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
let item = myItems[indexPath.row]
cell.textLabel!.text = item.name
cell.accessoryType = item.selected ? .checkmark : .none
cell.selectionStyle = .none
cell.tintColor = UIColor.green
return cell
}
override func tableView(tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = myItems[indexPath.row]
item.selected = true
tableView.reloadRows(at: [indexPath], with: .none)
}

Resources