How to remove item from Array? - swift

I have a multi selected tableview.
what I am doing : when user select items, this items append to the array.
When user deselect the item from cell, this deselected items remove from array.
what I did :
My array : var selectedTagList:[Tag] = []
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tagTableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
self.selectedTagList.append(tagList![indexPath.row])
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.tagTableView.cellForRow(at: indexPath)?.accessoryType = .none
self.selectedTagList.remove(at: indexPath.row)
}
Any advice or sample code please ?
//DataSource and Delegate
extension PickVideoViewController : UITableViewDataSource,UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let tlist = self.tagList , !tlist.isEmpty else { return 1}
return tlist.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let tlist = self.tagList , !tlist.isEmpty else {
let cell = UITableViewCell()
cell.selectionStyle = .none
cell.backgroundColor = .clear
cell.textLabel?.textAlignment = .center
cell.textLabel?.textColor = UIColor.black
cell.textLabel?.text = "nodataavaiable".localized()
return cell }
let cell = tableView.dequeueReusableCell(withIdentifier: "TagCell", for: indexPath) as! TagCell
cell.tagName.text = tlist[indexPath.row].tag
cell.accessoryType = cell.isSelected ? .checkmark : .none
cell.selectionStyle = .none // to prevent cells from being "highlighted"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tagTableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
self.selectedTagList.append(tagList![indexPath.row])
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.tagTableView.cellForRow(at: indexPath)?.accessoryType = .none
self.selectedTagList.remove(at: <#T##Int#>)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard let tlist = self.tagList , !tlist.isEmpty else {return tableView.frame.height }
return 40
}
}

self.selectedTagList.remove(at: indexPath.row)
indexPath.row is the wrong value to remove. Since your data source populates based on tagList and not selectedTagList, you need to get the item out of tagList and find the equivalent item in selectedTagList.
You don't show what type of object is in tagList, but you will probably need to make them conform to Equatable so you can do this lookup. Once you do, you should have something like this:
let deselectedTag = self.tagList[indexPath.row]
// You will need the items in `tagList` to conform to `Equatable` to do this
guard let indexOfDeselectedTag = self.selectedTagList.index(of: deselectedTag else) {
// Data inconsistency: the item wasn't found in selectedTagList
return
}
self.selectedTagList.remove(at: indexOfDeselectedTag)

You don't need to maintain a list of selected items. You already have all of the items and the tableView can tell you which rows/items are selected.
https://developer.apple.com/documentation/uikit/uitableview/1614864-indexpathsforselectedrows
Discussion
The value of this property is an array of index-path
objects each identifying a row through its section and row index. The
value of this property is nil if there are no selected rows.
You are trying to reinvent the wheel. Always check the documentation for existing functionality.
If you then want a list of selected items from this you just create an array of the items at those index paths, something like this: (untested)
let selectedItems = tableView.indexPathsForSelectedRows().map {
self.tagList[$0.row]
}
This code will iterate over the index paths and return the item from the array at each one. (this is untested, you may need to use flatMap as you are changing the type)

Related

index is out of range after filtering an array

I am trying to filter a song array so it only has songs where the song artistName is the same as ArtistName(Which is always the selected artist). When I run my app it throws an Fatal error: Index out of range even though in my debug console artistSongs has 4 elements. I don't understand how id be getting this crash.
override func viewDidLoad() {
super.viewDidLoad()
retriveData()
//Register nib
topSongTableView.register(TopSongTableViewCell.nib(), forCellReuseIdentifier: TopSongTableViewCell.topSongCell)
ArtistPicture.image = artistCover
ArtistLabel.text = ArtistName
//Assign TableView to self
topSongTableView.delegate = self
topSongTableView.dataSource = self
}
//Define number of rows in topSongsTableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
// songs of artist
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TopSongTableViewCell", for: indexPath) as! TopSongTableViewCell
let artistSongs = songs.filter{ $0.artistName == ArtistName }
print(artistSongs)
cell.TopSongLabel.text = artistSongs[indexPath.row].cleanName //CASH
cell.SongImage.image = UIImage(named: artistSongs[indexPath.row].cover)
return cell
}
Your problem is that your tableView topSongTableView expects to display 5 rows (you hard coded it) but when you're dequeuing your cells, you also filter artistSongs at the same time (it shouldn't be done here) and this produces in your case an array with less than 5 elements (4 in your case).
So here in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell, you're trying to dequeue more cells than elements in artistSongs.
Let's say let artistSongs = songs.filter{ $0.artistName == ArtistName } produces an array of 3 elements only but your table view expects 5, when you dequeue your cells and try to access the song with artistSongs[indexPath.row], it's gonna work for the first 3 rows and then it will crash (your current crash).
Your solution is to filter the artist songs somewhere else, say in viewDidLoad, and then use this array to populate your table view safely:
// ADD THIS
var filteredSongs: [Song]()
override func viewDidLoad() {
super.viewDidLoad()
[...]
// ADD THIS
self.filteredSongs = songs.filter{ $0.artistName == ArtistName }
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// CHANGE THIS
return self.filteredSongs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TopSongTableViewCell", for: indexPath) as! TopSongTableViewCell
// REMOVE THIS
//let artistSongs = songs.filter{ $0.artistName == ArtistName }
// REMOVE THIS
//print(artistSongs)
cell.TopSongLabel.text = self.filteredSongs[indexPath.row].cleanName
cell.SongImage.image = UIImage(named: self.filteredSongs[indexPath.row].cover)
return cell
}

Weird display behaviour when selecting tableview row

I have this table view, that populates through Core Data. Everything looks fine, even when scrolling, until I press and hold any row - then that row distorts like in the second image below.
(source: staticflickr.com)
(source: staticflickr.com)
Here's the code to display the tableview
override func viewDidLoad() {
super.viewDidLoad()
animateBackground(colors)
tableView.delegate = self
tableView.dataSource = self
tableView.fetchData(fetchedResultsController as! NSFetchedResultsController<NSFetchRequestResult>)
self.tableView.tableFooterView = UIView(frame: .zero)
}
func numberOfSections(in tableView: UITableView) -> Int {
guard fetchedResultsController.sections?.count != nil else { return 0 }
return fetchedResultsController.sections!.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard fetchedResultsController.sections != nil else { return 0 }
let data = fetchedResultsController.sections![section]
return data.numberOfObjects
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SkillCell", for: indexPath) as! SkillCell
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
return cell
}
func configureCell(cell: SkillCell, indexPath: NSIndexPath) {
let skill = fetchedResultsController.object(at: indexPath as IndexPath)
cell.skill = skill
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { }
}
From the code above, everything looks normal, yet this distortion occurs. Please help me to see why and how to stop it happening.
As the comment suggests, it's just a visual effect called selectionStyle of your UITableViewCell. Inside your cellForRowAt indexPath method, you can disable it like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! MyCell
cell.selectionStyle = .none // this removes the effect.
return cell
}
yes you should set tableView selection style to none.
1.Select the tableview in storyboard in the right panel set selection style to none.
or
2.in your tableview cellforrow method set
tableView.selectionstyle = none

Hide Cell when the condition is met

Hello StackOverflowers!
I am trying to hide cells when condition is met - in this case when the user survey comment is empty. The data is loaded from Firebase This is what I have tried so far: hiding the cell as well as selecting it. Then follow with heightForRow for selectedRows. But it's not working. Please help! Thank you~~~
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comment.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentsTableCell", for: indexPath) as! CommentsCell
let surveyCommentList = self.comment[indexPath.row]
cell.selectionStyle = UITableViewCell.SelectionStyle.none
if surveyCommentList.surveyComment == "" {
cell.isHidden = true
cell.isSelected = true
} else {
cell.surveyCommentText.text = surveyCommentList.surveyComment
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.contains(indexPath) {
return 0
} else {
return 50
}
}
This is the correct implementation :
// Define a new filtered array from your comments that filters empty Comments
let filteredComments = comment.filter({!$0.surveyComment.isEmpty)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredComments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentsTableCell", for: indexPath) as! CommentsCell
let surveyCommentList = self.filteredComments[indexPath.row]
cell.selectionStyle = UITableViewCell.SelectionStyle.none
cell.surveyCommentText.text = surveyCommentList.surveyComment
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}

Remove index from sidemenu user type wise

I have implemented side menu in tableview and now my scenario is like, I have to manage sidemenu options as user types
Let me show my code
var items = ["Social Media Post", "Messages", "Manage User","My Account","Information","Logout"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! MenuTableViewCell
cell.lblTitle.text = items[indexPath.row]
cell.imgMenuLogo.image = image[indexPath.row]
print(User_type)
if User_type == 2{
items.remove(at: 0)
}
return cell
}
but now i want . to de like this
if user_type == "3"{
// Social Media , Messages And Manage User options i want to remove
}
I am not able to understand how to remove from index.
Try something like this:
override func viewDidLoad() {
super.viewDidLoad()
getList()
}
func getList(){
switch userType{
case 0:
items = ["UserTypeOne_Home","UserType_One Settings","etc"]
break
case 1:
items = ["UserTypeTwo_Home","UserType_Two Settings","etc"]
break
default:
break
}
self.tableView.reloadData()
}
extension ViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Some ID")
cell?.textLabel?.text = items[indexPath.row]
return cell!
}
}
Try not to change the array from within the cellForRowAt indexPath method using indexPath.row, that will not give you the result you want. Modulate the array from outside the protocol method overrides, and just call the reloadData() method.
Try to use enum of UserType and check type of current user than Make an array with default options available to every user and then append specific data in array according to user type. Hope it clarifies :)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! MenuTableViewCell
cell.lblTitle.text = items[indexPath.row]
cell.imgMenuLogo.image = image[indexPath.row]
print(User_type)
if User_type == 2{
items.remove(at: 0)
}
return cell
}
This will work, But you have done a small mistake here.
You have removed from array after setting label. So you need to remove the item from array first then do set label.
Btw, I will not recommend this method as you need to add/remove from array for every cellForRowAt method.

Assigning an array count to a tableviewcell label

I have a posts array in which I'm returning all the posts from a PHP file. I'm trying to assign the number of posts to a label in a tableview cell. However, when I assign the number of posts to a label, I'm getting a nil value. Can anyone help with this?
var posts = [AnyObject]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return posts.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! InfoCell
print(posts.count)
cell.PostsLbl.text = String(posts.count)
return cell
}
}
It looks like you are getting posts async from PHP file and after you get them you reload only the second section.