Swift/iOS: Collapsing a section in a UITableView - ios

I have a UITableView with about 5 sections. I am trying to collapse and expand one of those section by the click of a button, but I am seeing an issue where the code I'm using to do so results in the collapsing of other sections as well. Specifically, the first row of all visible sections are collapsed.
Here is what that code looks like:
func didClickSectionCollapseButton() {
shouldCollapseSection = !shouldCollapseSection
tableView.beginUpdates()
tableView.reloadSections(NSIndexSet(index: 1), withRowAnimation: .Fade)
tableView.endUpdates()
}
And here is the numberOfRowInSection method:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return 1
case 1:
// collapsible section
return shouldCollapse ? 0 : collapsibleSectionCellCount
case 2:
return getCellCount()
case 3:
return 1
case 4:
return 1
default:
return 0
}
}
Is there anything I'm missing here? I've gone through various tutorials and questions, but I haven't been able to find a solution yet.

Hi after a lot of research, i found a solution which worked for me perfectly using storyboard.
View controller code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tblView: UITableView!
var sections = ["section1","section2","section3"]
var cells = ["cell1","cell2","cell3","cell4"]
var selectedIndx = -1
var thereIsCellTapped = false
override func viewDidLoad() {
super.viewDidLoad()
tblView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return 2
case 1:
return 3
default:
return 4
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == selectedIndx && thereIsCellTapped{
return 50
}else{
return 0
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCell(withIdentifier: "SectionTableViewCell") as! SectionTableViewCell
headerCell.lblHeader.text = sections[section]
headerCell.btnSelection.tag = section
headerCell.btnSelection.addTarget(self, action: #selector(ViewController.btnSectionClick(sender:)), for: .touchUpInside)
return headerCell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandeTableViewCell") as! ExpandeTableViewCell
cell.lblCell.text = cells[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.section)
}
#objc func btnSectionClick(sender:UIButton!){
print("selected index",sender.tag)
if selectedIndx != sender.tag {
self.thereIsCellTapped = true
self.selectedIndx = sender.tag
}
else {
// there is no cell selected anymore
self.thereIsCellTapped = false
self.selectedIndx = -1
}
tblView.reloadData()
}
}
If you don't want to do select and unselect on the same selection then, see code below.
#objc func btnSectionClick(sender:UIButton!){
print("selected index",sender.tag)
selectedIndx = sender.tag
tblView.reloadData()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == selectedIndx{
return 50
}else{
return 0
}
}
It works for me, i referred lot of answers and made it. I hope it will help you.

I've Used this code long time ago it is in Swift 2.3. I don't know if this will help or not but worth to mention it.
class DriversVC : UIViewController , UITableViewDelegate , UITableViewDataSource {
//-----------------------------------------------------------------------
//MARK: - Outlets
#IBOutlet var tvDriverList: UITableView! {
didSet {
tvDriverList.delegate = self
tvDriverList.dataSource = self
}
}
//-----------------------------------------------------------------------
//MARK: - Variables
var arrDriverList : NSArray? //Section data
var arrWorkerList : NSArray? //Section data
var collapseSection0 : Bool = false
var collapseSection1 : Bool = false
var btnSection0Headder : UIButton = UIButton()
var btnSection1Headder : UIButton = UIButton()
//------------------------------------------------------
func btnSection0HeadderTapped () {
if collapseSection0 {
collapseSection0 = false
} else {
collapseSection0 = true
}
tvDriverList.reloadSections(NSIndexSet(index: 0), withRowAnimation: UITableViewRowAnimation.Fade)
}
//------------------------------------------------------
func btnSection1HeadderTapped () {
if collapseSection1 {
collapseSection1 = false
} else {
collapseSection1 = true
}
tvDriverList.reloadSections(NSIndexSet(index: 1), withRowAnimation: UITableViewRowAnimation.Fade)
}
//-----------------------------------------------------------------------------------
//MARK:- Table delegate and data sources
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
//------------------------------------------------------
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 20
}
//------------------------------------------------------
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: 50))
view.backgroundColor = OrangeColor //Set your color
let lbl = UILabel(frame: CGRect(x: 10, y: 5, width: UIScreen.mainScreen().bounds.width - 20, height: 40))
lbl.font = UIFont(name: OpenSansRegular, size: 18) //Set your font
lbl.textColor = UIColor.whiteColor()
view.addSubview(lbl)
if section == 0 {
lbl.text = "D R I V E R"
btnSection0Headder.addTarget(self, action: #selector(self.btnSection0HeadderTapped), forControlEvents: .TouchUpInside)
btnSection0Headder.frame = view.frame
view.addSubview(btnSection0Headder) // uncomment to apply collapse effect
} else {
lbl.text = "W O R K E R"
btnSection1Headder.addTarget(self, action: #selector(self.btnSection1HeadderTapped), forControlEvents: .TouchUpInside)
btnSection1Headder.frame = view.frame
view.addSubview(btnSection1Headder) // uncomment to apply collapse effect
}
return view
}
//------------------------------------------------------
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return UIView()
}
//------------------------------------------------------
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if arrWorkerList != nil && arrWorkerList?.count > 0 {
return 2
}
return 1
}
//------------------------------------------------------
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
if !collapseSection0 {
guard arrDriverList != nil else {return 0}
return arrDriverList!.count
} else {
return 0
}
} else {
if !collapseSection1 {
guard arrWorkerList != nil else {return 0}
return arrWorkerList!.count
} else {
return 0
}
}
}
//------------------------------------------------------
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCellWithIdentifier(NSStringFromClass(DriversCVC).componentsSeparatedByString(".").last!) as? DriversCVC else { fatalError("unexpected DriversCVC dequeued from tableView") }
cell.superViewController = self
if indexPath.section == 0 {
guard let dict = arrDriverList![indexPath.row] as? NSDictionary else {return cell}
cell.data = dict
} else {
guard let dict = arrWorkerList![indexPath.row] as? NSDictionary else {return cell}
cell.data = dict
}
cell.setup()
return cell
}
//----------------------------------------------------------------------
//MARK: - Action Method
#IBAction func btnBackTapped(sender: AnyObject) {
guard self.navigationController != nil else {
self.dismissViewControllerAnimated(true, completion: nil)
return
}
guard self.navigationController?.popViewControllerAnimated(true) != nil else {
guard self.navigationController?.dismissViewControllerAnimated(true, completion: nil) != nil else {
AppDelegate.sharedInstance().loginCall()
return
}
return
}
}
//-----------------------------------------------------------------------
//MARK: - View Life Cycle Methods
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//----------------------------------------------------------------------
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
setUpVC()
}
//----------------------------------------------------------------------
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
} }

You can use:
func didClickSectionCollapseButton() {
shouldCollapseSection = !shouldCollapseSection
tableView.beginUpdates()
tableView.deleteSections(NSIndexSet(index: 1), withRowAnimation: .Fade)
tableView.endUpdates()
}

beginUpdates() and endUpdates() works in pair if you want subsequent insertions, deletion, and selection operations, but not for the reloadData.
In your code, remove beginUpdates() and endUpdates().

Is there a difference between the shouldCollapseSection variable being set in the button action and the shouldCollapse variable used in the numberOfRowsInSection method ?
It would seem that you are not setting the same variable you are using in the data source delegate.

Related

Two tableviews in one view controller, one table view not showing up

I have two TableViews in one ViewController and only one table is able to populate. I have created outlets for both tables in my view controller. Here is my viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
pickerTableView.delegate = self
pickerTableView.dataSource = self
tableView.delegate = self
tableView.dataSource = self
tableView.tableFooterView = UIView()
pickerTableView.reloadData()
tableView.reloadData()
}
It seems that the only table that is read is self.tableView and the pickerTableView is never read in the protocols for UITableView
extension MealViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if tableView == self.tableView {
let nameHeaderView = NameHeaderView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 40))
nameHeaderView.delegate = self
nameHeaderView.sectionIndex = section
nameHeaderView.nameButton.setTitle(Data.userModels[section].name, for: .normal)
return nameHeaderView
} else {
return nil
}
}
func numberOfSections(in tableView: UITableView) -> Int {
if tableView == self.tableView {
return Data.userModels.count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.tableView {
if Data.userModels[section].isExpandable {
return Data.userModels[section].itemModels.count
} else {
return 0
}
} else if tableView == pickerTableView {
return Data.userModels.count
}
else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "nameCells", for: indexPath) as! MealTableViewCell
cell.setup(itemModel: Data.userModels[indexPath.section].itemModels[indexPath.row])
return cell
} else if tableView == pickerTableView {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "pickerCells", for: indexPath) as! NamePickerTableViewCell
cell2.setup(userModel: Data.userModels[indexPath.row])
print(Data.userModels)
return cell2
}
else {
return UITableViewCell()
}
}
}
I have also applied constraints on each tableView so they both appear on the screen. When I run the app, the data for self.tableView is shown however the pickerTableView appears empty. self.tableView requires sections and pickerTableView does not require sections, could this be an issue? Also there is data in the Data.userModels array.
And just for reference, here is my UITableViewCell class:
class NamePickerTableViewCell: UITableViewCell {
#IBOutlet weak var namePickerLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setup(userModel: UserModel) {
namePickerLabel.text = userModel.name
}
}
Thanks for the help!
If you do not need any section in pickerTableView, then also it has to have at least one section.
So return 1 in method numberOfSections when tableview == self. pickerTableView.
func numberOfSections(in tableView: UITableView) -> Int {
if tableView == self.tableView {
return Data.userModels.count
} else {
return 1
}
}

Can't find reason for why tableViewCell is not editable and no default text is showing up

Sorry for the ignorant question but I am trying to follow my school's appdev tutorial and I came upon an error where I am trying to create editable table view cells but neither the default text nor the ability to edit is showing up. I checked in the custom TableViewCell class and the TextView Outlet is there. Any help would be much appreciated :3
import UIKit
class AddVerbViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource{
var isPickerViewOpened : Bool = false
#IBOutlet weak var TableView: UITableView!
#IBAction func DoneButtonTapped(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func ClearButtonTapped(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
TableView.delegate = self
TableView.dataSource = self
print("yes")
// Do any additional setup after loading the view.
}
//MARK: - Table View Methods
func numberOfSections(in tableView: UITableView) -> Int {
print("number of sections : 2")
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (section == 0){
return 2
} else {
return 2
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AddVerbCell", for: indexPath)as! AddVerbTableViewCell
cell.TextView.textContainer.maximumNumberOfLines = 1
cell.TextView.textContainer.lineBreakMode = .byTruncatingTail
cell.PickerView.isHidden = true
if (indexPath.section == 0) {
cell.TextView.isEditable = true
cell.TextView.textColor = UIColor.gray
cell.TextView.isScrollEnabled = false
if (indexPath.row == 0){
print("cell Name")
cell.TextView.text = "Name"
} else {
cell.TextView.text = "TeForm"
}
} else {
if (indexPath.row == 0) {
cell.TextView.isEditable = false
cell.TextView.isSelectable = false
cell.TextView.textColor = UIColor.black
cell.TextView.text = "PotentialForm"
} else {
cell.TextView.isHidden = true
cell.PickerView.isHidden = false
}
}
return cell
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 20
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (indexPath.section == 0) {
return 50
} else {
if (indexPath.row == 0){
return 50
} else {
return 100
}
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
I actually figured it out.
So I needed to add cell.TextView.isUserInteractionEnabled = true to the proper areas. Now it works!
Sorry for the trouble everyone

Swift 3.0 multiple selection with select all cell

I have added data in table view and I have manually added "select all" option to the list at first position, now when the user selects the first option which is 'select all' then the person manually option "Select all" is not selected. Select all, click then work all person or deselect working but signal selection all the person not working "Select all"
I have tried the code below but it's not working so can any one help me to solve this?
var unchecked:Bool = true
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell = ObjTableview.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SelectUserCell
// set the text from the data model
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.lblStudentName.text = getStudentName[indexPath.row]
if UnAll == "unselect" {
if indexPath.row == 0 {
cell.btnCheckbox.setImage(UIImage(named: "unSelectedItem"), for: .normal)
}
if indexPath.row == Int(selectedNumber) {
cell.btnCheckbox.setImage(UIImage(named: "unSelectedItem"), for: .normal)
}
if indexPath.row == Int(unSelectNumber) {
//var j = "\(i)"
cell.btnCheckbox.setImage(UIImage(named: "selectedItem"), for: .normal)
}
}else
{
if(unchecked){
cell.btnCheckbox.setImage(UIImage(named: "unSelectedItem"), for: .normal)
}
else{
cell.btnCheckbox.setImage(UIImage(named: "selectedItem"), for: .normal)
}
}
return cell
}
var UnAll = ""
var selectedNumber = ""
var unSelectNumber = ""
var checkselect:Bool = true
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UnAll.removeAll()
selectedNumber.removeAll()
unSelectNumber.removeAll()
if(indexPath.row == 0){
btnCheckBoxClick(sender: UIButton())
}else
{
UnAll = "unselect"
btnCheckBoxClick(sender: UIButton())
if checkselect {
selectedNumber = "\(indexPath.row)"
checkselect = false
}else
{
unSelectNumber = "\(indexPath.row)"
checkselect = true
}
print("the selected index is : \(indexPath.row)")
}
}
#IBAction func btnCheckBoxClick(_ sender: Any) {
if(unchecked){
unchecked = false
}
else{
unchecked = true
}
ObjTableview.reloadData()
}
Create a struct for model data with a Bool property. You can modify this property by cell selection.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var allCharacters:[Character] = []
override func viewDidLoad() {
super.viewDidLoad()
allCharacters = [Character(name: "All"),Character(name: "Luke Skywalker"),Character(name: "Leia Organa"),Character(name: "Advik Shah"),Character(name: "Aarav Modi")]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allCharacters.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
if cell == nil{
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell")
}
cell?.textLabel?.text = allCharacters[indexPath.row].name
if allCharacters[indexPath.row].isSelected
{
cell?.accessoryType = .checkmark
}
else
{
cell?.accessoryType = .none
}
cell?.selectionStyle = .none
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0
{
allCharacters[indexPath.row].isSelected = !allCharacters[indexPath.row].isSelected
for index in allCharacters.indices
{
allCharacters[index].isSelected = allCharacters[indexPath.row].isSelected
}
}
else
{
allCharacters[indexPath.row].isSelected = !allCharacters[indexPath.row].isSelected
if allCharacters.dropFirst().filter({ $0.isSelected }).count == allCharacters.dropFirst().count
{
allCharacters[0].isSelected = true
}
else
{
allCharacters[0].isSelected = false
}
}
tableView.reloadData()
}
}
struct Character
{
var name:String
// var otherDetails
var isSelected:Bool! = false
init(name:String) {
self.name = name
}
}
Creating Array of Struct objects from array of dictionary
let SubjectArray = json["students"] as! [[String:Any]]
allCharacters = SubjectArray.map({ Character(name: $0["studentName"] as! String) })
allCharacters.insert(Character(name:"All"), at: 0)
I like #Pranil's suggestion of using a separate section for the "All" row, so I have stolen that.
You can use an NSMutableIndexSet for tracking the selected rows. This is simpler than having to create a new struct or array of booleans or something. The only thing you do need to be aware of is if your tableview allows row reordering then the index set needs to be adjusted accordingly.
Here is my implementation. The "all" state is determined by the number of selected rows being equal to the number of rows in the data source array.
I have just used simple table view accessories for the checkmarks, but I am sure you can see how to adopt your image based approach in cellForRow(at:)
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
let names: [String]? = ["Luke Skywalker","Leia Organa","Advik Shah","Aarav Modi"]
var selectedRows = NSMutableIndexSet()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let names = self.names else {
return 0
}
return 0 == section ? 1 : names.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
var text: String
var accessory = UITableViewCellAccessoryType.none
if 0 == indexPath.section {
text = "All"
if self.selectedRows.count == self.names!.count {
accessory = .checkmark
}
} else {
text = names![indexPath.row]
if selectedRows.contains(indexPath.row) {
accessory = .checkmark
}
}
cell.textLabel!.text = text
cell.accessoryType = accessory
return cell
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if indexPath.section == 0 {
if self.selectedRows.count == self.names!.count {
self.selectedRows = NSMutableIndexSet()
} else {
self.selectedRows = NSMutableIndexSet(indexesIn: NSRange(location: 0, length: self.names!.count))
}
tableView.reloadData()
} else {
self.selectedRows.contains(indexPath.row) ? self.selectedRows.remove(indexPath.row) : self.selectedRows.add(indexPath.row)
let rows = [IndexPath(row: 0, section: 0), indexPath]
tableView.reloadRows(at: rows, with: .none)
}
return nil
}
}
I think you are using only one section in the table view. I suggest you use two sections in the table view, so that first section will contain only one row (Select All) and the second section will contain other options. When you click on Select All, that is in the first row of the first section you can make all the rows in the second section as selected while reloading the table view.
// MARK: - struct for cell item
struct CellItem {
var name : String
var isSelected:Bool! = false
init(name: String) {
self.name = name
}
}
class ViewController: UITableViewController {
#IBOutlet var viewTable: UITableView!
// Declare a boolean varaible to toggle the checkbox in the first section of table view
var isSelectAllSelected : Bool = false
var cellData: [CellItem] = []
override func viewDidLoad() {
super.viewDidLoad()
cellData = [CellItem(name: "Luke Skywalker"),CellItem(name: "Leia Organa"),CellItem(name: "Advik Shah"),CellItem(name: "Aarav Modi")]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
}
else
{
return cellData.count
}
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0
}
// MARK: - Table view delegates
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = TableCell()
cell.selectionStyle = .none
if indexPath.section == 0 {
cell.textLabel?.text = "Select All"
if isSelectAllSelected{
cell.accessoryType = .checkmark
}
else{
cell.accessoryType = .none
}
}
else
{
cell.textLabel?.text = cellData[indexPath.row].name
if cellData[indexPath.row].isSelected{
cell.accessoryType = .checkmark
}
else{
cell.accessoryType = .none
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0
{
cellData[indexPath.row].isSelected = !cellData[indexPath.row].isSelected
isSelectAllSelected = cellData[indexPath.row].isSelected
for index in cellData.indices
{
cellData[index].isSelected = cellData[indexPath.row].isSelected
}
}
else
{
cellData[indexPath.row].isSelected = !cellData[indexPath.row].isSelected
if cellData.filter({ $0.isSelected }).count == cellData.count
{
isSelectAllSelected = true
}
else
{
isSelectAllSelected = false
}
}
viewTable.reloadData()
} }
Hello u can take cheboxbutton action method inside view controller with addtarget method and assign tag indexpath.row so u can easily get the indexpath. from below code u can get the idea.
class ViewController:UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var ObjTableview: UITableView!
var arrStudent = ["1","2","3","4","5"]
var arrSelectedStudent :[Int] = []
var selectAll:Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrStudent.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell = ObjTableview.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SelectUserCell
// set the text from the data model
cell.selectionStyle = UITableViewCellSelectionStyle.none
// cell.lblStudentName.text = getStudentName[indexPath.row]
cell.lblStudentName.text = arrStudent[indexPath.row]
cell.btnCheckbox.tag = indexPath.row
cell.btnCheckbox.addTarget(self, action:#selector(btnCheckBoxClick(sender:)), for: .touchUpInside)
if selectAll {
cell.btnCheckbox.setImage(UIImage(named: "selectedItem"), for: .normal)
}else{
if arrSelectedStudent.contains(indexPath.row){
cell.btnCheckbox.setImage(UIImage(named: "selectedItem"), for: .normal)
}else{
cell.btnCheckbox.setImage(UIImage(named: "unSelectedItem"), for: .normal)
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func btnCheckBoxClick(sender: UIButton) {
if sender.tag == 0{
selectAll = true
}else
{
selectAll = false
if let index = arrSelectedStudent.index(of: sender.tag) {
arrSelectedStudent.remove(at: index)
}else{
arrSelectedStudent.append(sender.tag)
}
}
ObjTableview.reloadData()
}}

How can i hide a Section when UISwitch is Changed

Here is my tableView and want to hide the section "eventdays"
how can i do that ?
Hope you can help me.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// set data within item list
self.going = ["You can go?","Whos going"]
self.eventdays = ["Monday", "Sunday", "Wednesday"]
self.others = ["Bread", "Butter", "Paneer"]
// set table view delegate and data source
self.myTableView.delegate = self
self.myTableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Mark: - Table view data source and delegate
// set number of sections within table view
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
// set number for rows for each setion
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return self.going.count
}
if section == 1 {
return self.eventdays.count
}
if section == 2 {
return self.others.count
}
return 0
}
// set header title for each section
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Can Go?"
}
if section == 1 {
return "Days"
}
if section == 2 {
return "Others"
}
return "Default Title"
}
// set cell content for each row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// deque reusable cell
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as UITableViewCell
let row = indexPath.row
//Configure the switches for swipe options
let teilnehmenSwitch = UISwitch(frame:CGRectMake(150, 300, 0, 0));
teilnehmenSwitch.on = false
let eventDaySwitch = UISwitch(frame:CGRectMake(150, 300, 0, 0));
eventDaySwitch.on = false
switch(indexPath.section){
case 0:
cell.textLabel?.text = going[row]
if row == 0 {
cell.accessoryView = teilnehmenSwitch
}
case 1:
cell.textLabel?.text = eventdays[row]
cell.accessoryView = eventDaySwitch
case 2:
cell.textLabel?.text = others[row]
default:
cell.textLabel?.text="Others"
}
// return cell
return cell
}
And how can i find out what switch is changed so i can save the data in a DataBase.
Thanks in advance
you can change switch action by following code to have more optimized reload solution
#IBAction func switchValueChange(sender: UISwitch) {
//tableView.reloadData()
tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .None)
}
Try to change your code this way, Add one action with your UISwitch ValueChanged event and reload the tableView.
#IBAction func switchValueChange(sender: UISwitch) {
tableView.reloadData()
}
Now change your UITableViewDataSource method like this way
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if mySwitch.on {
return 3
}
else {
return 2
}
}
// set number for rows for each setion
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return self.going.count
}
if section == 1 {
if mySwitch.on {
return self.eventdays.count
}
else {
return self.others.count
}
}
if section == 2 {
return self.others.count
}
return 0
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Can Go?"
}
if section == 1 {
if mySwitch.on {
return "Days"
}
else {
return "Others"
}
}
if section == 2 {
return "Others"
}
return "Default Title"
}
var teilnehmenSwitch = UISwitch()
var eventDaySwitch = UISwitch()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
teilnehmenSwitch = UISwitch(frame:CGRectMake(150, 300, 0, 0));
teilnehmenSwitch.addTarget(self, action: #selector(SettingVC.switchDidChangeteilnehmenSwitch(_:)), forControlEvents: .ValueChanged)
teilnehmenSwitch.on = false
eventDaySwitch = UISwitch(frame:CGRectMake(150, 300, 0, 0));
eventDaySwitch.addTarget(self, action: #selector(SettingVC.switcheEventDaySwitch(_:)), forControlEvents: .ValueChanged)
eventDaySwitch.on = false
}
func switchDidChangeteilnehmenSwitch(sender:UISwitch!)
{
if (sender.on == true){
print("on")
}
else{
print("off")
}
}
func switcheEventDaySwitch(sender:UISwitch!)
{
if (sender.on == true){
print("on")
}
else{
print("off")
}
}

How to populate table with headers with associative array data source? Swift XCode 6.4

In my iOS app I have a table a table where I want to display a long list of tasks with 4 categories: open, closed, suspended, expired. So I have created a table with 4 headers and each header has a unique identifier for its cell:
Following a tutorial, after querying a database my tasks are organized in an object whose class is this:
import Foundation
class TaskMenuItems:NSObject {
var sections:[String] = []
var items:[String:[Task]] = [:]
func addSection(section:String, sectionItems:[Task]) {
self.sections = self.sections + [section]
self.items[section] = sectionItems
}
func organize(tasks:[Task]) {
var open:[Task] = []
var closed:[Task] = []
var expired:[Task] = []
var suspended:[Task] = []
for task in tasks {
switch task.stato {
case "Aperto":
open.append(task)
break
case "Chiuso":
closed.append(task)
break
case "Scaduto":
expired.append(task)
break
case "Sospeso":
suspended.append(task)
break
default:
break
}
}
self.addSection("Aperti", sectionItems: open)
self.addSection("Chiusi", sectionItems: closed)
self.addSection("Scaduti", sectionItems: expired)
self.addSection("Sospesi", sectionItems: suspended)
}
}
so it is an associative array where keys are the state of the task while the values are an array of tasks with that state. This is correctly done... the problm is that I'm not able to write the following method to populate the table:
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
So, here is my Swift class view controller associated to the table:
import UIKit
class AllTasksViewController: UITableViewController {
var allTasks = [Task]()
var taskService = TaskService()
var organizedTasks = TaskMenuItems()
override func viewWillAppear(animated: Bool) {
self.tabBarController?.navigationItem.setHidesBackButton(true, animated: true)
self.tabBarController?.navigationItem.rightBarButtonItem = self.editButtonItem()
if (LoggedUser.isLogged) {
self.navigationItem.setHidesBackButton(false, animated: true)
self.taskService.requestAllTasks {
(response) in
self.allTasks = self.taskService.loadTasks(response) as! [Task]
self.organizedTasks.organize(self.allTasks)
/*println(self.organizedTasks.items["Aperti"]?.count)
println(self.organizedTasks.items["Chiusi"]?.count)
println(self.organizedTasks.items["Scaduti"]?.count)
println(self.organizedTasks.items["Sospesi"]?.count)*/
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
/* Number of sections */
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.organizedTasks.sections.count
}
/* Number of rows for each section */
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return self.organizedTasks.items["Aperti"]!.count
case 1:
return self.organizedTasks.items["Chiusi"]!.count
case 2:
return self.organizedTasks.items["Scaduti"]!.count
case 3:
return self.organizedTasks.items["Sospesi"]!.count
default:
return -1
}
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
Actually I'm getting confused by something trivial I know because I think to indexPath as a String and not as an Int...In fact I am supposed to do:
let cell = tableView.dequeueReusableCellWithIdentifier("OpenTask",
forIndexPath: indexPath) as! UITableViewCell//2
cell.textLabel?.text = self.organizedTasks.items[indexPath.section][indexPath.row]
but in my case indexPath.row should be a String... :( Moreover, how can I access the n-th task object of that status in that associative array? something similar
self.organizedTasks.items["Aperti"][0]
won't work...
Would you please give any hint?
In swift you can use this method to display header
func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int)
{
/* if let view = view as? UITableViewHeaderFooterView {
view.backgroundView?.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.8)
let subview = UIView(frame: CGRect(x: 0, y: view.frame.height - 2, width: view.frame.width, height: 2))
subview.backgroundColor = UIColor.whiteColor()
view.addSubview(subview) //according to need
}*/
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?
{
if(section == 0)
{
return " Section 0"// according to need
}
else
{
return " Section 1"// according to need oR make it generic
}
}

Resources