I'm working on an exercise application where the user is able to do use a UISwitch to set if a exercise is active or not. I have a segmented controller that is used to switch to show "All" or only to show "Active".
Is it possible to get the specific cells with this property within my UISegmentedControl Action?
My code looks like this:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("ChallengeCell") as? ChallengeListCell {
cell.setTitle("Utmaning " + String(indexPath.row + 1))
let active = indexPath.row % 3 == 0 || indexPath.row % 5 == 0
cell.setActive(active)
if active {
cell.setCompleted((Double(indexPath.row % 5) + Double(indexPath.row % 3)) / 6.0)
}
else {
cell.setCompleted(0)
}
return cell
}
return UITableViewCell()
}
and my SegmentedControl function looks like this:
#IBAction func segmentedControlChanged(sender: UISegmentedControl) {
let selectedSegment = segmentedControl!.selectedSegmentIndex
switch selectedSegment{
case 0: //SHOW ALL
print("Selected 0")
case 1: //SHOW ONLY ACTIVE
print("Selected 1")
//case 2: //NOT ACTIVE
//print("Selected 2")
default:
print(sender)
}
}
All of this is in the same Controller.
You should have three datasource arrays: for example allItems activeItems and inactiveItems, and switch between them.
Inside the tableView delegates check the segment selected with the segmentedControl and use the right array as datasource.
Inside segmentedControlChanged call self.tableView.reloadData() and set a variable with the selected segment.
One way would be to keep track of the index paths of "Active" cells in an array that's an instance property of your view controller. Than when the segmented controller is toggled, you can determine what cells to remove/add from the table view just by looking at the array.
Another idea would be to have an active property on the model objects that hold the data for each table view cell. When the segmented control is toggled, you can iterate over all of these objects to determine if they're "Active" or not and proceed accordingly.
From your code provided, it sounds like my first suggestion would be most appropriate.
Related
How do I make an UITableView which allows sections to either have single or multiple selection?
I have a single UITableView with multiple sections. I want some sections to allow multiple selections and some to only allow a single selection. This is what I currently have:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
// Get the current question
if var question = self.questions[indexPath.section] as? MultipleChoiceQuestion {
// If question allows multiple selection set a checkmark and update question with selected answer
if question.hasMultiValue {
cell?.accessoryType = .checkmark
question.givenAnswer = question.answers[indexPath.row]
} else {
// Question is single selection only. This entire part goes wrong.
if let givenAnswerIsEmpty = question.givenAnswer {
cell?.accessoryType = .none
} else {
self.questions[indexPath.section].givenAnswer = question.answers[indexPath.row]
cell?.accessoryType = .checkmark
}
}
}
}
My objects all have the property hasMultiValue which indicates if a section should allow multiple selections or not. They also have the property givenAnswer which could be seen as a "isSelected" flag. The code above only works for multi selection.
I've been searching around for a solution. There are a few questions like this one but the solution involves using the delegate method didDeselectRowAt. This method won't get called unless I physically deselect the current cell which is not what I want.
What I do want is for example:
If you select row1 and change your mind to row2, you should be able to select row2 which automatically deselects row1.
You didn't try willSelectRowAt?
You can try this code, this is tested on a table view controller
var selectedItemInSection1: Int?
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
// Just return the same index path if the section was not the second one
guard indexPath.section == 1 else { return indexPath }
// Get the previously selected item, and deselect it
if let prev = selectedItemInSection1 {
tableView.deselectRow(at: IndexPath.init(row: prev, section: 1), animated: true)
}
// Save the newly selected item index, to be deselected when other is selected in the same section
selectedItemInSection1 = indexPath.row
// Select the item
return indexPath
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
guard indexPath.section == 1 else { return }
// If it is in the second section, indicate that no item is selected now
selectedItemInSection1 = nil
}
I have a table with 3 rows each with check button.What I am doing is when I select all the three buttons I want to click my cancel button which is on view not table on same controller to reload all 3 rows the call goes to custom cell class where uncheck is set to true and rows are reloaded.For the first attempt it works fine I can see correct index to be reloaded.On the second time again when I select all 3 check buttons and click cancel again I can see correct index to be reloaded but the call is not going to custom cell class again the check box still remains checked.Any idea why?
I am always getting correct index in my array.
Cancel button code-:
#IBAction func cancelDataItemSelected(_ sender: UIButton) {
for index in selectedButtonIndex{
let indexPath = IndexPath(item: index, section: 0)
print(selectedButtonIndex)
filterTableViewController.reloadRows(at: [indexPath], with: UITableViewRowAnimation.none)
}
selectedButtonIndex .removeAll()
print(selectedButtonIndex)
}
Table code-:
extension filterControllerViewController:UITableViewDataSource,UITableViewDelegate
{
// NUMBER OF ROWS IN SECTION
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return ControllerData.count
}
// CELL FOR ROW IN INDEX PATH
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let Cell = tableView.dequeueReusableCell(withIdentifier: "filterCell", for: indexPath) as! ControllerCellTableViewCell
Cell.filterTableMenu.text = ControllerData[indexPath.item]
Cell.radioButtonTapAction = {
(cell,checked) in
if let radioButtonTappedIndex = tableView.indexPath(for: cell)?.row{
if checked == true {
self.selectedButtonIndex.append(radioButtonTappedIndex)
}
else{
while self.selectedButtonIndex.contains(radioButtonTappedIndex) {
if let itemToRemoveIndex = self.selectedButtonIndex.index(of: radioButtonTappedIndex) {
self.selectedButtonIndex.remove(at: itemToRemoveIndex)
}
}
}
}
}
return filterCell
}
Custom Class-:
var radioButtonTapAction : ((UITableViewCell,Bool)->Void)?
//MARK-:awakeFromNib()
override func awakeFromNib() {
super.awakeFromNib()
filterTableSelectionStyle()
self.isChecked = false
}
// CHECKED RADIO BUTTON IMAGE
let checkedImage = (UIImage(named: "CheckButton")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal))! as UIImage
// UNCHECKED RADIO BUTTON IMAGE
let uncheckedImage = (UIImage(named: "CheckButton__Deselect")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal))! as UIImage
// Bool STORED property
var isChecked: Bool = false {
didSet{
// IF TRUE SET TO CHECKED IMAGE ELSE UNCHECKED IMAGE
if isChecked == true {
TableRadioButton.setImage(checkedImage, for: UIControlState.normal)
} else {
TableRadioButton.setImage(uncheckedImage, for: UIControlState.normal)
}
}
}
// FILTER CONTROLLER RADIO BUTTON ACTION
#IBAction func RadioButtonTapped(_ sender: Any) {
isChecked = !isChecked
radioButtonTapAction?(self,isChecked)
}
Fundamental misunderstanding of how "reusable" table cells work.
Let's say your table view is tall enough that only 8 cells are ever visible. It seems obvious that 8 cells will need to be created, and they will be reused when you scroll.
What may not be obvious is that the cells also are reused when they are reloaded. In other words, every time .reloadData is called - even if you are only reloading one cell that is currently visible - that cell is reused. It is not re-created.
So, the key takeaway point is: Any initialization tasks happen only when the cell is first created. After that, the cells are reused, and if you want "state" conditions - such as a checked or unchecked button - it is up to you to "reset" the cell to its original state.
As written, your cellForRowAt function only sets the .filterTableMenu.text ... it ignores the .isChecked state.
You can mostly fix things just by setting the cell's .isChecked value, but you're also tracking the on/off states in a much more complicated manner than need be. Instead of using an Array to append / remove row indexes, use an Array of Booleans, and just use array[row] to get / set the values.
Then your cellForRowAt function will look about like this:
// CELL FOR ROW IN INDEX PATH
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let filterCell = tableView.dequeueReusableCell(withIdentifier: "filterCell", for: indexPath) as! ControllerCellTableViewCell
// set the label in filterCell
filterCell.filterTableMenu.text = ControllerData[indexPath.item]
// set current state of checkbox, using Bool value from out "Tracking Array"
filterCell.isChecked = self.selectedButtonIndex[indexPath.row]
// set a "Callback Closure" in filterCell
filterCell.radioButtonTapAction = {
(checked) in
// set the slot in our "Tracking Array" to the new state of the checkbox button in filterCell
self.selectedButtonIndex[indexPath.row] = checked
}
return filterCell
}
You can see a working example here: https://github.com/DonMag/CheckBoxCells
Remember that the cells are reused and that reloadRows just tells the rows to redraw. When a checkbox in a cell is checked by the user, the new checked state should be saved in the underlying data source, and the state marked in the cell in cellForRowAtIndexPath. Otherwise the cell checkbox shows the state for the last time it was set by the user for all indices and not the state for the underlying data source.
I have two UITableViews. The first UITableView contains an array of data, and the second is empty. How can I get the index path of the first table to use it in the second table to view different data? This is an example of what I want to do in the second table:
This is in the second table and like this function where I want to get the indexPath.row of first table.
func loadData() {
if indexPath.row == 0 { // (of first table)
// add code
} else if indexPath.row == 1 { // (of first table)
// add code
}
}
Implement didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let selctedIndex = indexPath.row
self.setIndexToSecondTableView(selctedIndex) //Create your function to get selected index of first table view
}
Hope You get solution.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if tableView.isEqual(firstTableView) {
self.updateScecondTableView(index: IndexPath.row) //make a function to retrieve selected index from first table view
} else {
// acode
}
}
func updateScecondTableView(index:NSIndexPath) {
switch index.row {
case 0:
// add a code
break
default:
// add a code
break
}
}
My first iOS app works with simple custom cells, but enhancement to filter tableView rows is causing delays and frustration. Searched online for help on filter rows, read dataSource and delegate protocols in Apple Developer guides, no luck so far.
Using slider value to refresh table rows. Extracted data from line array (100 items) to linefilter array (20). Then want to refresh/reload the tableview.
Slider is declared with 0 and all line array items show up. moving the slider does not alter display. If slider is declared with say 1, then 20 filter items show.
Quite new to Apple/Xcode/Swift so have no Objective C knowledge.
Any answers will probably help me get there.
Jim L
Relevant selection of code :
#IBAction func moveSlider(sender: AnyObject) {
// Non-continuous ******
_ = false
// integer 0 to 5 ******
let slider = Int(lineSlider.value)
}
}
// Global Variable ******
var slider = 0
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if slider == 0 {
return self.line.count
} else {
return self.linefilter.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! myTableViewCell
if slider == 0 {
cell.myCellLabel.text = line[indexPath.row]
} else {
cell.myCellLabel.text = linefilter[indexPath.row]
}
cell.myImageView.image = UIImage(named: img[indexPath.row])
return cell
}
tableView.reloadata()
try to put
tableView.reloadData()
like this
let slider = Int(lineSlider.value)
tableView.reloadData()
}
in your moveSlider function
I have a table with two sections. I can select row at index - but I cannot differentiate between the sections.
the table is a pullout menu - which is created solely in code.
the delegate method for the selection is
func sideBarControlDidSelectRow(indexPath: NSIndexPath) {
delegate?.sideBarDidSelectButtonAtIndex(indexPath.row)
sectionis = indexPath.section
NSLog("Index Path Section %d", indexPath.section)
}
this gives me the section in the console.
but on the view controller all I have is
func sideBarDidSelectButtonAtIndex(index: Int){
switch(index) {
etc.
}
}
What I need to be able to do is something like
func sideBarDidSelectButtonAtIndex(index: Int){
if section == 0 {
switch(index){
etc.
}
}
}
but if I try that then Binary operator cannot be applied to operands of type section
this has got to be something obvious as tables have sections all the time - but the answer is eluding me.
Just update declaration of method sideBarDidSelectButtonAtIndex in delegate protocol to accept NSIndexPath value, change:
func sideBarDidSelectButtonAtIndex(index: Int)
to
func sideBarDidSelectButtonAtIndex(indexPath: NSIndexPath)
then you can just remove this method
func sideBarControlDidSelectRow(indexPath: NSIndexPath) {
delegate?.sideBarDidSelectButtonAtIndex(indexPath.row)
}
and replace its call just with
delegate?.sideBarDidSelectButtonAtIndex(indexPath)
Then you'll receive full indexPath and could do what you want