So I have a UITableViewCell and I want to delete the image from the tableView. But I also want to delete the given object from the Parse database.
I have the image object saved in the online Parse framework and in the Local Datastore.
for i in images{
var imageNames = i.thumbnailImage
var imageFile = PFFile(data: UIImagePNGRepresentation(imageNames))
//Pin the objects here!!
var imageObject = PFObject(className: "img")
imageObject["theImg"] = imageFile
//imageObject["user"] = PFUser.currentUser()?.username
imageObject.pinInBackgroundWithName("imgs")
var objID = imageObject.objectId
imageObject.pinInBackgroundWithBlock({ (success, error) -> Void in
if error == nil{
println("Object Saved!!")
imageObject.save()
println(objID)
self.imagesFromParse.append(imageNames!)
self.tableView.reloadData()
} else {
println(error)
}
})
I then have the delete UITableViewCell function called here -
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
var cell:nudeCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! nudeCell
if editingStyle == UITableViewCellEditingStyle.Delete{
if self.imagesFromParse.count >= 1{
self.imagesFromParse.removeAtIndex(indexPath.row)
//Here is where i want to delete the object from the localDatastore and the online framwork
}
}
}
The problem is that when I try to delete the object in the background. I do not know how to get the specific object ID in the given indexPath to delete.
Related
I have a tableView which I created with firebase data.
Here is my load func :
var reservationList = [UserModal]()
private func loadposts() {
reservationList = []
activityIndicator.startAnimating()
Database.database().reference().child("BookReservations").observe(.value) { snapshot in
for case let child as DataSnapshot in snapshot.children {
guard let dict = child.value as? [String:Any] else {
print("Error")
self.activityIndicator.stopAnimating()
return
}
let name = dict["name"] as! String
let date = dict["date"] as? String ?? "nil"
let book = dict["bookName"] as? String ?? "nil"
let FullDate = dict["fullDate"] as? String ?? "nil"
let phoneNumber = dict["phoneNumber"] as? String ?? "nil"
let reservations = UserModal(name: name, dateAndTime: date, choosenBook: bookName , phoneNumber: phoneNumber, tamRandevu: fullDate)
self.reservationList.append(reservations)
print(self.reservationList)
self.activityIndicator.stopAnimating()
self.tableView.reloadData()
}
}
}
extension ReservationListViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return reservationList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reservationCell", for: indexPath) as! reservationCell
cell.bookNameLabel.text = reservationList[indexPath.row].choosenBook
cell.nameLabel.text = reservationList[indexPath.row].userName
cell.dateAndTimeLabel.text = reservationList[indexPath.row].fullDate
cell.phoneButton.setTitle(reservationList[indexPath.row].phoneNumber, for: .normal)
return cell
}
I can get value and show on tableView. But when I try to delete cell :
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Deleted")
activityIndicator.startAnimating()
let reservationTime = reservationList[indexPath.row].fullDate ?? "nil"
print(reservationTime)
if reservationTime != "nil" {
let stationsRef = database.child("BookReservations")
print(reservationList.count)
print("Before deleting Count of reservation list : \(reservationList.count)")
reservationList.forEach { (UserModal) in
print(UserModal.name)
}
stationsRef.child(reservationTime).setValue(nil)
reservationList.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
print("After deleting Count of reservation list : \(reservationList.count)")
reservationList.forEach { (UserModal) in
print(UserModal.name)
}
activityIndicator.stopAnimating()
}else{
activityIndicator.stopAnimating()
print("no reservation found.")
}
}
}
I can succesfully delete data from firebase real time database. But when I delete data , If there is more than one data in the tableView, the non-deleted data will repeat itself.
I try to check my array count it seems good when delete count is decreasing.. I try tableview reload, begin updates and and updates but nothing works. Only works when I refresh tableview with refresh method. This is my print outputs :
Before deleting Count of reservation list : 2
Optional("yakalaaa")
Optional("xyzzz")
After deleting Count of reservation list : 1
Optional("yakalaaa")
this is my tableView which I load with firebase data :
enter image description here
I delete second cell :
enter image description here
and this is the problem :
enter image description here
any suggestions ?
the problem is with these lines:
tableView.beginUpdates()
reservationList.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .bottom)
tableView.endUpdates()
just use instead
reservationList.remove(at: indexPath.row)
tableView.reloadData()
I have a tableview showing data from a Firebase console (JASON structure) like this:
chklst-xxx
- avaliation
-students
-KHZAjBi44eZ8JsaswkKI
-teacher: Paul
-datTime: 09.12.2016 12:25pm
-name: Mary Anne
-mark: 7.5
-preceptor: John
-KHWZlh7aasy78ahsiuKIi0
-teacher: Paul
-datTime: 09.12.2016 12:48pm
-name: Anne Caroline
-mark: 9.4
-preceptor: Peter
These data are shown at a tableview, each one, after node students, except the key. I have one function to delete an especific row, as i do with other apps in CoreData (tableview editingStyle):
// FUNC DELETAR
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
teste.remove(at: indexPath.row)
ref.removeValue() // HERE IS MY DOUBT
tableView.reloadData()
}
}
I can erase the item from the array and from the table, but i just don't know how to do it from Firebase. I have tried other ways with removeValue, but it just didn't work, except an way to erase the whole data, not specifically that row selected.
let ref = FIRDatabase.database().reference().child("avaliation").child("students")
I need some teaching to make this works. Thank you.
Here is my method for Saving this data: (Added 09.12 19:30pm)
func Save(){
let ref = FIRDatabase.database().reference().child("avaliation").child("students").childByAutoId()
referencia.child(“name”).setValue(Name)
referencia.child(“teacher”).setValue(Teacher)
referencia.child("preceptor").setValue(Preceptor)
referencia.child("datTime”).setValue(DateTime)
referencia.child(“mark”).setValue(ResultComFReFC)
}
And here is my Class where I work with the main Array:
import Foundation
import Firebase
struct ChkList {
var name: String?
var teacher: String?
var preceptor: String?
var DateToday: String?
var mark: String?
var key: String!
init(name: String, teacher: String, preceptor: String, mark: String, DateToday: String, key: String = "") {
self.name = name
self.teacher = teacher
self.preceptor = preceptor
self.mark = mark
self.DateToday = DateToday
self.key = key
}
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
name = snapshotValue["name"] as? String
teacher = snapshotValue["teacher"] as? String
preceptor = snapshotValue["preceptor"] as? String
mark = snapshotValue["mark"] as? String
DateToday = snapshotValue["DateToday"] as? String
}
func toAnyObject() -> Any {
return [
"name": name,
"teacher": teacher,
"preceptor": preceptor,
"mark": mark,
"DateToday": DateToday ]}}
UPDATE: Tableview EdityingStyle
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let ref = FIRDatabase.database().reference().child("avaliation").child("students").childByAutoId()
teste.remove(at: indexPath.row) //Array delet item
let userKeyA = ref.key
ref.child(userKeyA).removeValue()
tableView.reloadData()
}
}
My suggestion, store the pushKey of the user by using 'getKey' method when you push the user details.
chklst-xxx
- avaliation
-students
-KHZAjBi44eZ8JsaswkKI
-teacher: Paul
-userKey:KHZAjBi44eZ8JsaswkKI <--------------
-datTime: 09.12.2016 12:25pm
-name: Mary Anne
-mark: 7.5
-preceptor: John
Now you can retrieve the 'key` of the user. Suppose you are listing, users in the list view and would want to delete a row.
let ref = FIRDatabase.database().reference().child("avaliation").child("students").child(students.getUserKey()).removeValue();
Let me know, if it works.
Update: I don't know swift. But i'll give u an abstract idea.
When you create a new student, you refer student node and push() right. Like below,
DatabaseReference studentsRef = FIRDatabase.database().reference().child("avaliation").child("students").push();
And then you put your data like,
studentRef.child("teacher").setValue("Paul");
studentRef.child("userKey").setValue(studentRef.getKey()); <------------
I did it, using suggests above and studying and trying a lot.
The way to the method works is really capture the userkey.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let user: ChkList = teste[indexPath.row]
let uk = user.key
print("Userkey: \(uk)")
ref.child(uk!).removeValue()
}
}
This way you will have removed the data from Firebase and the row form tableview.
This answer moves the Firebase delete behavior behind the scenes so as
to not interfere with the normal tableview loading mechanisms. One
benefit is reducing the number of calls to Firebase and removing
timing issues from the internal tableview animation process.
What worked for me is getting the Firebase stuff out of the way. In my case, I collect all the keys that I want. During the delete, I remove the values from my local object, and when leaving the scene I call my cleanup method from viewDidDisappear() to sync the updated data back to Firebase:
func syncFavorites() {
//ref is the leaf for the favorites
//clear existing
ref?.removeValue()
//temp collection
var faves = [String:Any]()
//dataArrayKeys is my collection of keys on self
dataArrayKeys.keys.forEach { (key) in
faves.updateValue("true", forKey: key)
}
ref?.updateChildValues(faves)
}
I handle my tableview's delete action using the normal method.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
if let key = dataArr[indexPath.row]["key"] as? String {
dataArrayKeys.removeValue(forKey: keyToDelete)
dataArr.remove(at: indexPath.row)
}
tableView.deleteRows(at: [indexPath], with: .fade)
self.tableView.reloadData()
}
}
I have no doubt there is probably a better way. This was the simplest possible thing that worked for me.
Trying to display an array pulled from parse.com in a textview inside of a uitableviewcell. Everything else is showing but I can't seem to get a array to display in a textview. This is the code I have. I'm getting fatal error: Array index out of range for myCell2.feedbacktextview.text = feedback![indexPath.row]
var feedback: [String]?
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className: "Post")
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
if let objects = objects {
if object.objectForKey("Comments") != nil {
self.feedback = object.objectForKey("Comments") as! [String]
}
self.tableView.reloadData()
}}}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell2 = tableView.dequeueReusableCellWithIdentifier("feedcell1", forIndexPath: indexPath) as! YourAdviseControllerCell
myCell2.feedbacktextview.text = feedback![indexPath.row]
return myCell2
}
edit:
self.imageFiles.append(object["imageFile1"] as! PFFile)
self.imageFiles2.append(object["imageFile2"] as! PFFile)
self.usernames.append(object["message"] as! String)
self.usernames2.append(object["declaration"] as! String)
self.usernames3.append(object["whichbutton"] as! String)
Basically what you did is intentionally correct but a fair share of small mistakes are left to correct. The takeaway would be never use forced unwrapping when possible.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell2 = tableView.dequeueReusableCellWithIdentifier("feedcell1", forIndexPath: indexPath) as! YourAdviseControllerCell
//This below line fetches the value from feedback if it has else gives ""
myCell2.feedbacktextview.text = feedback?[indexPath.row] ?? ""
return myCell2
}
That would solve the problem for now but i see if this code gets called when then you might be returning some valid values from the numberOfRowsInCells method without respect to the feedback value. Ideally i would do something like this:
var feedback:[String]?
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return feedback?.count ?? 0
}
Even then there is a slight problem i guess. Don't call tableView.reloadData() from a block which is executing in a separate thread or queue. Do all the work in main queue.
if object.objectForKey("Comments") != nil {
self.feedback = object.objectForKey("Comments") as! [String]
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}
Hope it helps! Cheers!
I need to store the id in cell as well as the text (to display in the listview). So how can I save 2 values in cell?
This is what I have
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("sentence", forIndexPath: indexPath) as! UITableViewCell
//Get list
var request = NSURLRequest(URL: url!)
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
if data != nil {
var dataArray = JSON(data: data!)
var item: NSMutableArray = []
//Get IDs
for (key, data) in dataArray {
var name = data["dutch_sentence"]
println(name)
item.addObject(name.string!)
}
cell.textLabel!.text = item[indexPath.row] as? String
return cell
}
else{
println("Out")
return cell;
}
}
But I want to send the id along too because I want to use it for the JSON array URL in the next view. So I would need something like:
cell.id = 2
To be able to call it in didSelectRowAtIndexPath
How could I do that?
Storing data in you views is very bad design decision. You should save your data in array and then access it based on indexPath.row:
let ids = [5, 10, 20]
public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let id = ids[indexPath.row]
}
I'm using a PFQueryTableViewController and trying to create a videoCell for each item in Parse. I only want to play the current active cell.
This is my cellForRowAtIndexPath method.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell: VideoTableViewCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? VideoTableViewCell
// cell?.parseObject = object
if let pfObject = object {
cell?.nameLabel?.text = pfObject["name"] as? String
print(pfObject["name"])
}
let file: PFFile? = object!.objectForKey("videoFile") as? PFFile
if self.currentActiveIndex == nil && file != nil {
self.currentActiveIndex = indexPath.row
print(file)
let fileUrl: NSURL? = NSURL(string: file!.url!)
let player: AVPlayer? = AVPlayer(URL: fileUrl!)
player?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.Initial, context: &PlayerStatusContext)
cell?.cellVideoView.setPlayer(player!)
cell?.cellVideoView.setVideoFillMode(AVLayerVideoGravityResizeAspectFill)
}
print(objects?.count)
return cell
}
For some reason the video is appearing both in the 1st and 4th cells, every time. I'm curious as to why. Thanks
You need to ensure that the player is removed when the cell is re-used.
Basically, this means you need an else clause for your if. Probably something like -
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell: VideoTableViewCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? VideoTableViewCell
// cell?.parseObject = object
if let pfObject = object {
cell!.nameLabel!.text = pfObject["name"] as! String
print(pfObject["name"])
}
let file: PFFile? = object!.objectForKey("videoFile") as? PFFile
if self.currentActiveIndex == nil && file != nil {
self.currentActiveIndex = indexPath.row
print(file)
let fileUrl: NSURL? = NSURL(string: file!.url!)
let player: AVPlayer? = AVPlayer(URL: fileUrl!)
player?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.Initial, context: &PlayerStatusContext)
cell?.cellVideoView.setPlayer(player!)
cell?.cellVideoView.setVideoFillMode(AVLayerVideoGravityResizeAspectFill)
}
else {
if let player=cell!.cellVideoView.player {
player.removeObserver(self, forKeyPath:"status")
cell!.cellVideoView.setPlayer(nil)
}
}
print(objects?.count)
return cell
}
Also, you have a lot of ? where you can use !. For example, cell won't be nil (and if it is, that is bad and an exception should be thrown)and the cast to VideoTableViewCell or String or even PFFile shouldn't fail and if it does then something has gone wrong an exception is probably appropriate so that you can debug.
Let's think of it this way. When you start loading your screen, you have a your currentActiveIndex as nil right? So what happens when that cell loads? Boom - the video shows up there.
And then of course, you go and actually set the currentActiveIndex when you tap on it and create a new cell, that's why you get this behavior. Here is what you should really do.
Have currentActiveIndex as a global variable or so. When you select a new value or so, just do a self.tableview.reloadData() which refreshes the whole UITableView or a self.tableview.refreshSections("THE SECTION WITH THE VIDEOS"). Decouple the currentActiveIndex logic from the UI Part.