Index of Selected Row in a Section - swift

I have a Food Items separated by sections and I want to be able to press on the food item and go to a food item detail page. My problem is that I am not able to pass the information to the detail because I am not getting the index of the item in the section.
FoodItem is from coredata.
var allFoodItems = [[FoodItem]]()
var foodItemTypes = [
FoodItemType.Appetizer.rawValue,
FoodItemType.SoupSalad.rawValue,
FoodItemType.Main.rawValue]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let foodItem = FoodItem()
guard let section = foodItemTypes.index(of: foodItem.type!) else { return }
let row = allFoodItems[section].count
let selectedIndexPath = IndexPath(row: row, section: section)
let foodItemDetailController = FoodItemDetailController()
foodItemDetailController.foodItem = selectedIndexPath
navigationController?.pushViewController(foodItemDetailController, animated: true)
}

You only need to send indexPath directly
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let foodItemDetailController = FoodItemDetailController()
foodItemDetailController.foodItem = indexPath // if IndexPath is the type
navigationController?.pushViewController(foodItemDetailController, animated: true)
}
//
Or send the item clicked
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let all = allFoodItems[indexPath.section]
let item = all[indexPath.row]
let foodItemDetailController = FoodItemDetailController()
foodItemDetailController.foodItem = item // if foodItem is the type
navigationController?.pushViewController(foodItemDetailController, animated: true)
}

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
}

Swift: How do you pass data to a button from tableview cell?

I am trying to pass tableview cell data on editing did end to a button. I have been using tags but I keep getting an error nil found. I think this is do to the amount of rows I have (over 500). Anyone know the best way to do this?
The Code below is what I use (tags). It only crashes (returns nil) when I have multiple tableviewcell rows and have to scroll off the page.
func numberOfSections(in tableView: UITableView) -> Int { return 1 }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return inventoryArray.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let code : String = (inventoryArray[indexPath.row] as AnyObject).value(forKey: "code") as! String
let name : String = (inventoryArray[indexPath.row] as AnyObject).value(forKey: "name") as! String
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! InventoryCell
cell.unitField.tag = indexPath.row
cell.unitField.addTarget(self, action: #selector(Inventory.submit(_:)), for: UIControl.Event.editingDidEnd)
return cell as InventoryCell
}
...
#IBAction func submit(_ sender: UIButton) {
let tag = sender.tag
let cell = tableView.cellForRow(at: IndexPath.init(row: tag, section: 0)) as! InventoryCell
var unitCount = Int(cell.unitField.text!)!
cell.count.text = String (unitCount)
}

How do I show an appropriate image in dequeueReusable cell?

I have an array which i show as dequeueReusableCell, I'd like to show an appropriate image next to the cell. My images are the same name like items in array located in Assets.
How do I show an appropriate image in dequeueReusable cell?
My code:
import UIKit
class Nicosia: UIViewController, UITableViewDelegate, UITableViewDataSource {
let nicosiaPlaces = ["Famagusta Gate", "Laiki Geitonia", "Ledra Street","Omeriye Hamam","Cyprus Museum","Venetian Walls","The House of Hatjigeorgakis Kornessios","Byzantine Art Museum","Archbishop's Palace","Liberty Monument","The Faneromeni Church","Nicosia International Conference Center"]
var identities = ["Famagusta Gate", "Laiki Geitonia", "Ledra Street","Omeriye Hamam","Cyprus Museum","Venetian Walls","The House of Hatjigeorgakis Kornessios","Byzantine Art Museum","Archbishop's Palace","Liberty Monument","The Faneromeni Church","Nicosia International Conference Center"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nicosiaPlaces.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
let city = nicosiaPlaces [indexPath.row]
cell?.textLabel?.text = city
//cell.imageCellNicosia?.image = UIImage(named: city)
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vcName = identities[indexPath.row]
let viewController = storyboard?.instantiateViewController(withIdentifier: vcName)
self.navigationController?.pushViewController(viewController!, animated: true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
If you have a custom table cell class cast this
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! MyCellClass
cell.imageCellNicosia?.image = UIImage(named: city)
if not use build in imageView property of UITableViewCell
cell.imageView?.image = UIImage(named: city)

How to remove item from Array?

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)

didSelectRowAt from custom cell do nothing

below code supposed to call usernames from an array and user able to tap on the desired username. call to that array is a success and I can see the usernames but won't able to tap into it. and it don't even print "didSelectRowAt". appreciate your help. thanks.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath) as! MentionUserCell
cell.username!.text = autoComplete[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return autoComplete.count
}
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let selectedCell: MentionUserCell = tableView.cellForRow(at: indexPath)! as! MentionUserCell
let selectedWord: String = selectedCell.username!.text! + " "
print("didSelectRowAt")
var string: NSString = NSString(string: self.commentTextView.text!)
string = string.replacingCharacters(in: otoCadang.sharedInstance.foundRange, with: selectedWord) as NSString
// change text field value
commentTextView.text = string as? String
// change cursor position
let positionOriginal = commentTextView.beginningOfDocument
if let cursorLocation: UITextPosition = self.commentTextView.position(from: positionOriginal, offset: otoCadang.sharedInstance.foundRange.location + selectedWord.characters.count)
{
self.commentTextView.selectedTextRange = self.commentTextView.textRange(from: cursorLocation, to: cursorLocation)
}
// remove suggestion
self.autoComplete.removeAll()
self.tableView.reloadData()
tableView.isHidden = true
}
Check List
tableview delegate is set properly
tableview selection mode == No Selection, then change to single selection
function name is correct func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
are you using any tap gesture on this view? remove it.
If you are editing UITextView will can't call to `didselectRowAt. Try to get the Your MentionUserCell, IndexPath in UITextView's methods and handle there. Example:
func textViewDidChange(_ textView: UITextView) {
var parentView = textView as UIView
while !(parentView is UITableViewCell) {
parentView = parentView.superview!
}
if let cell: MentionUserCell = parentView as? MentionUserCell {
if let indexPath = self.tableView.indexPath(for: cell) {
let yourIndexPath = indexPath
}
}
//your code
}
Hope it will help you.
When are using two or more cells the didSelectRow method is not working Well. You can use a button in cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifierName", for: indexPath) as! MentionUserCell
cell.username!.text = autoComplete[indexPath.row]
let button = cell.viewWithTag(101) as! UIButton
button.cellclick.addTarget(self, action: #selector(functionName(sender:)), for: .touchDown)
return cell
}
Now you get the indexPath:-
func functionName(sender:UIButton) {
let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:self.tableview)
let indexPath = self.tableview.indexPathForRow(at: buttonPosition)
print(indexPath.row)
}