Avoid duplicates in array in swift 3 - ios

I am trying to get the data from the selected rows in my tableView inside an array. I will append the selected row to the array and this does work great. Unfortunately when I deselect the row and select it again it will append it again. I need to write some logic to delete the deselected row from the array.
Does anyone know how I can do this?
Here is the code when I select a row:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) as? exerciseCell
selectedCell?.contentView.backgroundColor = UIColor(red: 84.0 / 255, green: 199.0 / 255, blue: 252.0 / 255, alpha: 1)
rowSelected+=1
if rowSelected >= 1 {
nextBtn.isEnabled = true
nextBtn.backgroundColor = UIColor(red: 84.0 / 255, green: 199.0 / 255, blue: 252.0 / 255, alpha: 1)
}
let exercisesSelected = selectedCell?.exerciseNameLbl.text
exerisenames.append(exercisesSelected!)
print(exerisenames)
}
Here is the code for deselecting a row:
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) as? exerciseCell
selectedCell?.textLabel?.textColor = UIColor.darkGray
rowSelected-=1
if rowSelected == 0 {
nextBtn.isEnabled = false
nextBtn.backgroundColor = UIColor.darkGray
nextBtn.setTitle("Choose an exercise", for: UIControlState.disabled)
}
let exercisesDeselected = selectedCell?.exerciseNameLbl.text
if exerisenames.contains(exercisesDeselected!) {
print("exercise already in the row and has to be deleted before going further")
}
print(exerisenames)
}
Both will refer to an array that I created in the top called: exercisenames

From a quick look at the docs, you probably want something like
if let index = exercisenames.index(of: exercisesDeselected) {
exercisenames.remove(at: index)
}
But I also think that #almas's suggestion of an ordered set might be good.

Related

How to fix UITableView cell repeat in swift 4

When I create a new cell it will automatically mark
or when I marked the first one, I was creating multiple
Cell, there will be duplicates marked
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let itemCellIdentifier = "itemCell"
guard let itemCell = itemCardTableView.dequeueReusableCell(withIdentifier: itemCellIdentifier, for: indexPath) as? ItemCardTableViewCell else {
return UITableViewCell()
}
let itemCard = dataManager.items[indexPath.row]
itemCell.itemTitle.text = itemCard.title
if itemCard.isFinish {
itemCell.itemCellView.backgroundColor = UIColor(red: 0, green: 123, blue: 0)
}
return itemCell
}
Add cell method
let confirm = UIAlertAction(title: "確認", style: .default) { (action: UIAlertAction) in
guard let title = addAlert.textFields?.first?.text else { return }
let newItem = ItemCard(title: title, isFinish: false)
self.dataManager.items.append(newItem)
let indexPath = IndexPath(row: self.itemCardTableView.numberOfRows(inSection: 0), section: 0)
self.itemCardTableView.insertRows(at: [indexPath], with: .left)
When I create new data isFinish = false
How can I fix data duplication?
You should provide else case to set default background color.
if itemCard.isFinish {
itemCell.itemCellView.backgroundColor = UIColor(red: 0, green: 123, blue: 0)
} else {
itemCell.itemCellView.backgroundColor = UIColor.white
}
If I understood your question right, You should hold the isFinish flag somewhere out of the cell, because it's being reused by tableView.
You can create the finished = [Bool]() is your UIViewController and every time check finished[indexPath.row] to see if it's marked or not and pass the boolean to your cell.

UITableView cellForRowAt if statement faulty work

According to the codes below, the first time I run the application.
The first 3 lines are working correctly.
But when I scroll up and down.
Some lines also change.
When I make a little more up and down
It changes to other lines
How can i solve this problem
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = KategoriEkleCell()
var KategoriId :String = ""
if tableView == tableView {
cell.selectionStyle = .none
cell = self.tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath as IndexPath) as! KategoriEkleCell
cell.itemLabel.text = (AltKategoriKayit[indexPath.section][indexPath.row] as? [String:String])?["kategoriadi"]
cell.itemLabel.font = UIFont(name:"OpenSans-Light", size: 14.0)
KategoriId = ((AltKategoriKayit[indexPath.section][indexPath.row] as? [String:String])?["kategoriid"])!
if (KategoriId) == (MenuKategoriKayit[indexPath.row]["kategoriid"]!) {
cell.EkleButton.layer.cornerRadius = 2
cell.EkleButton.layer.borderWidth = 1
cell.EkleButton.layer.borderColor = UIColor(red: 255/255, green: 56/255, blue: 107/255, alpha: 1.0).cgColor
cell.EkleButton.backgroundColor = UIColor(red: 255/255, green: 56/255, blue: 107/255, alpha: 1.0)
cell.EkleButton.setTitleColor(UIColor.white, for: .normal)
cell.EkleButton.clipsToBounds = true
}else {
cell.EkleButton.layer.cornerRadius = 2
cell.EkleButton.layer.borderWidth = 1
cell.EkleButton.layer.borderColor = UIColor(red: 255/255, green: 56/255, blue: 107/255, alpha: 1.0).cgColor
cell.EkleButton.clipsToBounds = true
}
}
return cell
}
Right employee
Faulty employee. when I scroll up and down.
As mentioned in the comments cells are reused.
You have to ensure that any UI element is set to a defined state in cellForRow. I moved the duplicate lines which both are executed in the if and else branch out of the if - else scope for better readability.
By the way the parentheses in the if line are not needed either
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let specialRed = UIColor(red: 1.0, green: 56/255, blue: 107/255, alpha: 1.0)
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath) as! KategoriEkleCell
cell.selectionStyle = .none
cell.itemLabel.text = (AltKategoriKayit[indexPath.section][indexPath.row] as? [String:String])?["kategoriadi"]
cell.itemLabel.font = UIFont(name:"OpenSans-Light", size: 14.0)
let KategoriId = ((AltKategoriKayit[indexPath.section][indexPath.row] as? [String:String])?["kategoriid"])!
cell.EkleButton.layer.cornerRadius = 2
cell.EkleButton.layer.borderWidth = 1
cell.EkleButton.clipsToBounds = true
cell.EkleButton.layer.borderColor = specialRed.cgColor
if KategoriId == MenuKategoriKayit[indexPath.row]["kategoriid"]! {
cell.EkleButton.backgroundColor = specialRed
cell.EkleButton.setTitleColor(UIColor.white, for: .normal)
} else {
cell.EkleButton.backgroundColor = UIColor.white
cell.EkleButton.setTitleColor(specialRed, for: .normal)
}
return cell
}
Please consider that according to the naming convention variable names are supposed to start with a lowercase letter.

Function does not work correctly until after a row has been selected

I have a tableview inside a viewcontroller. I have a little function to select all rows in the tableview. When I first display the viewcontroller and hit the select all button the function does not work. However, if I firstly select a row and then press the select all button, the function works as it should and all rows are selected. I'm not sure why this is happening. The tableview's delegate and data source have been set up in the storyboard.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:myTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! myTableViewCell
cell.accessoryType = .None
if allJobsSelected {
let bgColorView = UIView()
bgColorView.backgroundColor = UIColor(red: 250/255, green: 182/255, blue: 17/255, alpha: 1)
cell.contentView.backgroundColor = UIColor(red: 250/255, green: 182/255, blue: 17/255, alpha: 1)
cell.selectedBackgroundView = bgColorView
cell.accessoryType = .Checkmark
cell.highlighted = false
cell.selected = true
// cell.accessoryType = .Checkmark
self.tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.None)
self.tableView(self.tableView, didSelectRowAtIndexPath: indexPath)
}
var job: Jobs!
job = jobs[UInt(indexPath.row)] as! Jobs
cell.reports2JobTitle.text = job.jobTitle
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.allowsMultipleSelection = true
if let cell:myTableViewCell = tableView.cellForRowAtIndexPath(indexPath) as? myTableViewCell {
let bgColorView = UIView()
bgColorView.backgroundColor = UIColor(red: 250/255, green: 182/255, blue: 17/255, alpha: 1)
cell.contentView.backgroundColor = UIColor(red: 250/255, green: 182/255, blue: 17/255, alpha: 1)
cell.selectedBackgroundView = bgColorView
cell.accessoryType = .Checkmark
cell.highlighted = false
self.tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.Bottom)
}
}
#IBAction func doSelectAll(sender: UIBarButtonItem) {
let totalRows = tableView.numberOfRowsInSection(0)
for row in 0..<totalRows {
tableView.selectRowAtIndexPath(NSIndexPath(forRow: row, inSection: 0), animated: false, scrollPosition: UITableViewScrollPosition.None)
}
}
It would probably be a good idea to move this line:
self.tableView.allowsMultipleSelection = true
to your viewDidLoad
You are not guaranteed that the didSelect is called immediately -- it might be that each select is turning off the previous one and then a single didSelect is being called on the last row.

Handle CellForRowAtIndexPath in SWRevealViewController in swift?

Problem:
IF User "Login" the i want show "Logout" option in cell and vice versa. but i created UI in Storyboard as
I have created custom UITableView class and cellForRowAtIndexPath look like
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// var cell : UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell!
let cell : UITableViewCell? = menuListView.cellForRowAtIndexPath(indexPath)
if indexPath.row == 20
{
let titleLabel = cell?.contentView.viewWithTag(100) as! UILabel
if(NSUserDefaults.standardUserDefaults().integerForKey("UserID") <= 0 ){
//logout
cell?.contentView.backgroundColor = UIColor(red: 6.0/255, green: 46.0/255, blue: 107.0/255, alpha: 1.0)
titleLabel.text = "Login"
}else{
//user is login
cell?.contentView.backgroundColor = UIColor.redColor()
titleLabel.text = "Logout"
}
}
return cell!
}
but i am getting nil cell. i set Datasource,Delegate,table connection.How to fix that?
Fixed by using following code. use tableviews delegate method willDisplayCell.
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 20
{
let titleLabel = cell.contentView.viewWithTag(100) as! UILabel
if(NSUserDefaults.standardUserDefaults().integerForKey("UserID") <= 0 ){
//logout
cell.contentView.backgroundColor = UIColor(red: 6.0/255, green: 46.0/255, blue: 107.0/255, alpha: 1.0)
titleLabel.text = "Login"
}else{
//user is login
cell.contentView.backgroundColor = UIColor.redColor()
titleLabel.text = "Logout"
}
}
}

Swift: Segmented control behaves in a weird way in UITableView Cell

Anytime I tap segmented control in UICell, immediately some other cell gets this segmented control in the same position. It looks like segmented control recognizes that not only this particular one was tapped but also some other one in other cell.
Have you ever encountered issue like this?
this is my custom cell implementation:
class QuestionYesNoCustomCellTableViewCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var segmentControl: ADVSegmentedControl!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
segmentControl.items = ["TAK", "NIE"]
segmentControl.font = UIFont(name: "Avenir-Black", size: 12)
segmentControl.borderColor = UIColor.grayColor()
segmentControl.selectedIndex = 1
segmentControl.selectedLabelColor = UIColor.whiteColor()
segmentControl.unselectedLabelColor = UIColor.grayColor()
segmentControl.thumbColor = UIColor(red: 46.0/255.0, green: 204.0/255.0, blue: 113.0/255.0, alpha: 1.0)
segmentControl.addTarget(self, action: "segmentValueChanged:", forControlEvents: .ValueChanged)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func segmentValueChanged(sender: AnyObject?){
if segmentControl.selectedIndex == 0 {
segmentControl.thumbColor = UIColor(red: 231.0/255.0, green: 76.0/255.0, blue: 60.0/255.0, alpha: 1.0)
segmentControl.selectedLabelColor = UIColor.whiteColor()
segmentControl.unselectedLabelColor = UIColor.grayColor()
}else if segmentControl.selectedIndex == 1{
segmentControl.thumbColor = UIColor(red: 46.0/255.0, green: 204.0/255.0, blue: 113.0/255.0, alpha: 1.0)
segmentControl.selectedLabelColor = UIColor.grayColor()
segmentControl.unselectedLabelColor = UIColor.whiteColor()
}
}
Also, I think it is worth to provide my tableView delegate methods implemented
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (dict2 as NSDictionary).objectForKey(dictKeysSorted[section])!.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: QuestionYesNoCustomCellTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as! QuestionYesNoCustomCellTableViewCell
cell.questionLabel.text = (dict2 as NSDictionary).objectForKey(dictKeysSorted[indexPath.section])![indexPath.row] as? String
if indexPath.row % 2 == 0 {
cell.backgroundColor = UIColor(red: 245.0/255.0, green: 245.0/255.0, blue: 245.0/255.0, alpha: 1.0)
}
else {
cell.backgroundColor = UIColor(red: 225.0/255.0, green: 225.0/255.0, blue: 225.0/255.0, alpha: 0.7)
}
return cell
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dictKeysSorted[section]
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("CellHeader") as! CustomHeaderCell
headerCell.backgroundColor = UIColor(red: 20.0/255.0, green: 159.0/255.0, blue: 198.0/255.0, alpha: 1.0)
headerCell.headerLabel.text = dictKeysSorted[section]
return headerCell
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 70.0
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return dictKeysSorted.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 110.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
To recap what the problem actually is: In every tableView cell there is a segment control. When I change the position of the one located in first row, I scroll down and see that segment control in row 5 also has been moved despite the fact it should be in the default position.
Thanks in advance
EDIT:
I recognized one of the biggest problem in solutions below - they are good as long as you don't use section in tableView. The thing is, from what I have discovered right now, in each sections the rows are counted over from 0.
This might be the cause when you are using reusing the cells, when you scroll the cell you changed will be shown again for another row.
To avoid this when you reuse cell make sure you reset the data in it also
In your case you have to check if the segmented value is changed then change the segmented control value also in cellForRowAtIndexPath
Please let me know if you need more explanation.
Here is a sample project for you sampleTableReuse
It's because of reusable nature of UITableViewCells. You must keep track in your datasource selected segment index for each row. Then in cellForRowAtIndexPath you must set it properly for each cell.
example
define somewhere an enum with possible Answers:
enum Answer {
case Yes
case No
case None
}
then define and init your answers' array:
var answer = [Answer](count: numberOfQuestions, repeatedValue: .None)
in your cell's implementation add a method to configure a cell with Answer
func setupWithAnswer(answer: Answer)
{
var selectedIdex = UISegmentedControlNoSegment
switch answer {
case .Yes: selectedIdex = 0
case .No: selectedIdex = 1
default: break
}
self.segmentedControl.selectedSegmentIndex = selectedIdex
}
and finally, in your cellForRowAtIndex do after dequeuing
cell.setupWithAnswer(answer: self.answers[indexPath.row])

Resources