Trying to edit a List in realm - swift

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
}
}

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
}

How to display multiple attributes in a single table view? 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.

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.

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
}