How to display multiple attributes in a single table view? Swift - swift

I want to display three different entitys from core data on a table view. I can do this with one entity and tried to carry that logic over to displaying three on the same table view. I used the same entity in the code below to test this. I am getting blank sections in my table view. I must have the cellForRowAt method wrong? Here is my code.
var word: [NSManagedObject] = []
let sections = ["Custom Library", "Mastered Words", "Library"]
var array = [
[NSManagedObject](),
[NSManagedObject](),
[NSManagedObject]()
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell2")
array = [
word,
word,
word
]}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array[section].count
}
func numberOfSections(in tableView: UITableView) -> Int {
sections.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customWord = array[indexPath.row][indexPath.section]
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell2", for: indexPath)
cell.textLabel?.text = customWord.value(forKeyPath: "title") as? String
return cell

Based on the code you've shown, your array property is an array of empty arrays as word is an empty array itself. If you're using array to determine the number of rows in each section based on the array at that section index, then you're going to get 0 rows for each section.

Related

Duplicates in tableview section Swift

I amm trying to put some datas into categories into a tableView. Pizzas into pizza category, burgers into burger etc, but i have every item duplicate ( Look here Picture . What can be the problem ?
Struct :
struct Food {
var photoKeyRestaurant: String
var foodName: String
var foodDescription: String
var restaurantName: String
var priceFood: Int
var typeFood: String
}
Table View
func numberOfSections(in tableView: UITableView) -> Int {
return food.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return food[section].foodName.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FoodTableViewCell", for: indexPath) as! FoodTableViewCell
// cell.delegate = self
let mancare = food[indexPath.section]
let storageRef = Storage.storage().reference()
let photoRef = storageRef.child(mancare.photoKeyRestaurant)
cell.foodImage.sd_setImage(with: photoRef)
cell.descriptionLabel.text = mancare.foodDescription
cell.foodNameLabel.text = mancare.foodName
cell.priceLabel.text = "\(mancare.priceFood) lei"
cell.foodImage.layer.borderWidth = 1
cell.foodImage.layer.masksToBounds = false
cell.foodImage.layer.borderColor = UIColor.black.cgColor
cell.foodImage.layer.cornerRadius = cell.foodImage.frame.height/2
cell.foodImage.clipsToBounds = true
//Fac ca imaginea sa fie cerc - finish
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return food[section].typeFood
}
If somone will ask, im retrieving the data from the firestore, but there s no problem there, because the data is retrieved succesfuly.
Anyone know what can be the problem ? Thanks
It is interesting that your code for cellForRowAt never actually mentions the indexPath.row. It refers only to the indexPath.section. How can you get and display data corresponding to this row, which you are supposed to configure in cellForRowAt if you do not in some way refer to the question of what row this is?
Perhaps there is some other mechanism going on that I don't see, but it seems to me that this one fact alone explains why each section just shows the same data over and over rather than different data for each row.
You have 2 big problems with this code.
You have the wrong array of data fo sections.
in the func cellForRowAt you cant return indexPath.section.
With first. You need to use 2-dimensional arrays like:
var foodCategorys: [FoodCategory] = [[categoryName, Food],[categoryName, Food]]
So you need another struct to contain this 2-dimensional array:
struct FoodCategory {
var categoryName: String
var listFood: [Food] = [] // Food is the struct that you currently have
}
So the data will display in tableView is an array FoodCategory. With numberOfSections will return a count of an array FoodCategory. And numberOfRowsInSection will return a count of a listFood into FoodCatogory.
func numberOfSections(in tableView: UITableView) -> Int {
return foodCategorys.count //array of listFood
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return foodCategorys[section].listFood.count
}
And second, in cellForRowAt, when you want to get the index of list data. you need to call foods[indexPath.section].listFood[indexPath.row]. The func need row not section
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FoodTableViewCell", for: indexPath) as! FoodTableViewCell
//cell.delegate = self
let mancare = foods[indexPath.section].listFood[indexPath.row] //Remeber that, the array food we are using in this line is array of FoodCategory
I think you need to research with key word "section in uitableview swift", or watch this video https://www.youtube.com/watch?v=AHY09z-XS9s
So, that all. I hope you will understand how tableview and section work

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
}

Trying to edit a List in realm

I'm trying to make a List with realm:
class TripsList : Object {
let trips = List<Trip>()
}
Then, inside my ViewController class:
var trips : Results<TripsList>?
override func viewDidLoad() {
super.viewDidLoad()
trips = realm.objects(TripsList.self)
}
When someone moves a UITableViewRow, I want to update my realm database.
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let movedObject = self.realm.objects(Trip.self)[sourceIndexPath.row]
trips.remove(at: sourceIndexPath.row)
trips.insert(movedObject, at: destinationIndexPath.row)
}
Here are my TableView Datasource methods:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return realm.objects(Trip.self).count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.font = UIFont.systemFont(ofSize: 17)
cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
cell.textLabel?.text = nameData.names[realm.objects(Trip.self)[indexPath.row].tripID]
return cell
}
The problem is there is no option to do trips.remove(at:) or trips.insert(_:at:).
My overall goal is the ability to insert and remove when someone moves a UITableViewRow and update my realm database.
You cannot directly modify a Results instance. Results are auto-updating collections, meaning that they always reflect the current state of the query you used to initialise them. You need to modify the Realm in order to modify the Results instance.
Moreover, a Results instance is only guaranteed to keep its ordering in case you sort it. So you'll need to introduce a property on Trip that you use to sort the objects and modify that property when the user moves a row.
Your TripsList class seems to be unnecessary, since it seems that you simply want to store a number of Trip objects in Realm, then retrieve them without actually doing any grouping. Even if you needed to group them, you could do so using Realm queries. Keeping this in mind, this is how I'd modify your current code to allow the user to sort their Trips and save the sorting to Realm.
class Trip: Object {
// Your existing code for Trip
...
// Sorting property
#objc dynamic var sortingIndex = 0
}
In your table view controller:
var trips : Results<Trip>?
override func viewDidLoad() {
super.viewDidLoad()
trips = realm.objects(Trip.self).sorted(byKeyPath: "sortingIndex")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return trips?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.font = UIFont.systemFont(ofSize: 17)
cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
if let tripID = trips?[indexPath.row].tripID {
cell.textLabel?.text = nameData.names[tripID]
}
return cell
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
guard let movedObject = trips?.[sourceIndexPath.row] else { return }
// Depending on your exact needs, you might want to update the `sortingIndex` property of your other rows as well, whose position in the table view was affected by the reordering
try! realm.write {
movedObject.sortingIndex = destinationIndexPath.row
}
}

UITABLE view sections with different prototype cells in each section

I have a table view with different sections and for each section it has different prototype cells. I have created the prototype cell in storyboard but I could not make more than one cell in cellForRowAtIndexPath. I have attached the whole code for reference.
This is my storyboard design
This is the final output screen which it should look like
import UIKit
class PlacePeopleViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var friendlistTableView: UITableView!
var sectionHead = ["Friends", "Others"]
override func viewDidLoad() {
super.viewDidLoad()
friendlistTableView.delegate = self
friendlistTableView.dataSource = self
// Do any additional setup after loading the view.
}
// MARK: - tableview datasource & delegate
func numberOfSections(in tableView: UITableView) -> Int {
return sectionHead.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sectionHead[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 5
}else {
return 10
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! RecentVizzTableViewCell
cell.placeName.text = "poland"
cell.placeAddress.text = "Simon Albania"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 79
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 15
}
}
Note : You can do this using only one cell. just hide/show the connect Button by checking some condition.
If you want to use different cells for different sections, inside your cellForRowAt indexPath method return your cells based on your section like below .
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! RecentVizzTableViewCellOne
return cell
default:
let defaultcell = tableView.dequeueReusableCell(withIdentifier: "defaultcell") as! RecentVizzTableViewCellDefatult
return defaultcell
}
hope this will help to you. good luck.

Swift calling a class from a view controller

I am a beginner in swift coming from java. I have a task class and a taskcollection class where it is a collection of tasks. I am trying to put the collection into a table view but I dont know how.
This is task class:
class Task{
private var description : String
init(description: String){
self.description = description
}
func getDescription() -> String{
return self.description
}
func setDescription(description: String){
self.description = description
}}
This is taskCollection class:
class TaskCollection{
private var tasks: Array<Task>
init(){
self.tasks = [Task]()
}
func getTasks() -> Array<Task>{
return self.tasks
}
func addTask(task: Task){
self.tasks.append(task)
}}
To get the taskcollection into the tableview what do i do? In java I know I have to make something like new TaskCollection(), but i dont know how to get it in the tableviews:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ?
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableviewTasks.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
// set the text from the data model
cell.textLabel?.text = self.___?_____[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
You would create an instance of the TaskCollection class somewhere in your ViewController file scope. Then you populate it somewhere else (I did this is viewDidLoad as an example). Number of rows will simply be the number of tasks in the taskCollection instance. You can access each task by using indexPath.row to get the index of the array of tasks.
class TableViewController: UITableViewController {
let taskCollection = TaskCollection()
override func viewDidLoad() {
super.viewDidLoad()
let task = Task(description: "Some Description")
taskCollection.addTask(task: task)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return taskCollection.getTasks().count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
let task = taskCollection.getTasks()[indexPath.row]
cell.textLabel?.text = task.getDescription()
return cell
}
}
First there is no need to create a TaskCollection class as it's functionality is already build in arrays so, you need to create an array of tasks
var tasksArr = [Task]()
//
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tasksArr.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableviewTasks.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
// set the text from the data model
cell.textLabel?.text = self.tasksArr[indexPath.row].description
return cell
}
OR according to current implementation
var colc = TaskCollection()
//
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.colc.tasks.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableviewTasks.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
// set the text from the data model
cell.textLabel?.text = self.colc.tasks[indexPath.row].description
return cell
}