Reloading single row in PFTableViewCell - Returns that PFTableViewCell does not have cellForRowMethod - swift

Ok so I've been trying to make a like button for an app I'm working on and I'm using parse as a backend. So far I can update the like button in parse, however, I can only reload the entire tableview not just the single cell. Reloading the entire tableview sometimes causes the tableview to move up or down I believe because of different sized cells.
#IBAction func topButton(sender: UIButton) {
let uuid = UIDevice.currentDevice().identifierForVendor.UUIDString
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = objectAtIndexPath(hitIndex)
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: hitIndex!) as TableViewCell
//var indexOfCell:NSIndexPath
//indexOfCell = hitIndex!
if object.valueForKey("likedBy") != nil {
var UUIDarray = object.valueForKey("likedBy")? as NSArray
var uuidArray:[String] = UUIDarray as [String]
if !isStringPresentInArray(uuidArray, str: uuid) {
object.addObject(uuid, forKey: "likedBy")
object.incrementKey("count")
object.saveInBackground()
//self.tableView.reloadData()
}
}
}
I've already tried the cellForRow with the proper indexPath and it always returns that PFTableViewCell does not contain the cellForRowAtIndexPath method. I read on a forum that someone was able to create a custom method to reload the cell and Parse's documentation uses PAPCache to do the like button. Lucky for me the documentation for the like button on Parse's page is in Obj-C and I've coded my app in Swift. Here's the link to the page if you want to see: https://www.parse.com/tutorials/anypic#like. Section 6.1.
TD;LR I'm unsure how to update the cell so the entire tableview does not get reloaded without the cellForRowAtIndexPath method. Maybe Custom method? Maybe PAPCache method?
SOLVED
if object.valueForKey("likedBy") != nil {
var UUIDarray = object.valueForKey("likedBy")? as NSArray
var uuidArray:[String] = UUIDarray as [String]
if !isStringPresentInArray(uuidArray, str: uuid) {
object.addObject(uuid, forKey: "likedBy")
object.incrementKey("count")
object.saveInBackground()
let count = object.valueForKey("count") as Int?
//cell.count.text = "\(count)"
self.tableView.reloadRowsAtIndexPaths([hitIndex!], withRowAnimation: UITableViewRowAnimation.None)
This properly reloads only the cell, however, now my entire tableview always scrolls to the top which is not ideal.

I don't understand what are you trying to with the UUID; hitpoint ;hitIndex, and I don't understand what you want your app to do and I don't have and experience with parse ? I would comment on your question,but I need 50 rep to do this.Sorry that this isn't an answer,but a question .And if you are referring about cellForRowAtIndexPath,you need a UITableView not a cell.

Related

I need to pass UITableView data to detailView Controller

I got an issue when I pass the UITableView data to detailViewcontroller like this any one please help me correct this error
Got your problem,
Step : - IN detailViewcontroller, define variable like that
var getImage = UIImage()
var getTitle = String()
As far as I can see newDetails is an array of dictionary with key and value. You can access the image or anything within a dictionary using its key.
if let image = newsDetails[indexPath.row]["yourImageKeyNameFromNewsDetails"] as? String {
Storyboard?.getImage = UIImage(named: image) ?? UIImage(named: "yourPlaceHolderImage")
}
I hope this explains you.
Hello Madushan Senavirathna,
From your provided images, I see that newsDetails is array of [Sting:Any] which will return object of [String:Any].
But you are assigning object to image and string which I observe from given image like :
Storyboard?.getImage = newsDetails[indexPath.row]
Storyboard?.getTitle = newsDetails[indexPath.row]
I found that problem is in above syntax.
Ideal syntax to get images from your object are as below :
if let imageString : String = newsDetails[indexPath.row]["imageKeyFromObject"]
{
Storyboard?.getImage = You need to handle image string as per your requirement (if local file name than handle it with UIImage(named: imageString) or if it is dynamic content from server then you have to handle it with url)
}
Use this code to pass data from tableview diselect to details page view controller and in that detail page view controller need to declare that variable correctly..
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cellData = newsDetails[indexPath.row] {
if let vc = YourDetail page ViewController as? DetailPageController {
vc.details = cellData
self.navigationController?.pushViewController(vc, animated: true)
}
}
}

edit a TextView in a TableView cell in swift

I'm stuck: I have a TableView populated by .xib cells that I made. Each of these cells contains an editable TextView.
I'm trying to save on my Firebase database the text that the user input in those TextViews. I don't want to implement any button, the text should be saved as soon as the TextView editing end.
I tried to connect the TextView from the .xib file to the UITableViewCell class but it doesn't allow me to connect it as an IBAction but only as outlet or outlet connection.
Please Help me, thanks!
screenshot
You need to implement the UITextFieldDelegate in your
UITableViewCell class.
Connect the delegate of the UITextView to the cell class.
Do whatever you want in the func textViewDidEndEditing(UITextView) which you need to implement in your cell class.
Here you can read more: https://developer.apple.com/documentation/uikit/uitextviewdelegate/1618603-textviewshouldendediting?changes=_2
I solved replacing the TextViews with TextFields that could look the same but could be linked to the UITableViewCell.swift as IBAction.
Thus I wrote the code to update the "comments" section of my database inside the IBAction:
#IBAction func commentTextFieldToggle(_ sender: UITextField) {
if commentTextField.text != "" {
let comment = commentTextField.text
// I declared the next 7 constants to retreive the exact position of the string "comment" that I want to change
let date = dateLabel.text!
let time = timeLabel.text!
let year = date.suffix(4)
let day = date.prefix(2)
let partialMonth = date.prefix(5)
let month = partialMonth.suffix(2)
//I use this "chosenDate" constant to retreive the database query that I previously saved using the date in the below format as index:
let chosenDate = "\(year)-\(month)-\(day) at: \(time)"
let commentsDB = Database.database().reference().child("BSL Checks")
commentsDB.child((Auth.auth().currentUser?.uid)!).child(String(chosenDate)).child("Comments").setValue(comment) {
(error, reference) in
if error != nil {
print(error!)
} else {
print("User Data saved successfully")
}
}
}
}

Selecting Multiple Table View Cells At Once in Swift

I am trying to make an add friends list where the user selects multiple table view cells and a custom check appears for each selection. I originally used didSelectRowAtIndexPath, but this did not give me the results I am looking for since you can highlight multiple cells, but unless you unhighlight the original selected row you cannot select anymore. I then tried using didHighlighRowAtIndexPath, but this doesn't seem to work because now I am getting a nil value for my indexPath. Here is my code:
override func tableView(tableView: UITableView, didHighlightRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as! AddedYouCell
let currentUser = PFUser.currentUser()?.username
let username = currentCell.Username.text
print(currentCell.Username.text)
let Friends = PFObject(className: "Friends");
Friends.setObject(username!, forKey: "To");
Friends.setObject(currentUser!, forKey: "From");
Friends.saveInBackgroundWithBlock { (success: Bool,error: NSError?) -> Void in
print("Friend has been added.");
currentCell.Added.image = UIImage(named: "checked.png")
}
}
How can I solve this? Thanks
I'm not going to write the code for you, but this should help you on your way:
To achieve your goal, you should separate the data from your views (cells).
Use an Array (i.e. friendList) to store your friend list and selected state of each of them, and use that Array to populate your tableView.
numberOfCellsForRow equals friendList.count
In didSelectRowAtIndexPath, use indexPath.row to change the state of your view (cell) and set the state for the same index in your Array
In cellForRowAtIndexpath, use indexPath.row to retrieve from the Array what the initial state of the cell should be.

How do I change a switch in CoreData using Dynamic Tables (Swift)

I have an app that I am converting from objective-c to Swift and am also changing it to use dynamic (rather than static) tables. I can load the cells with entity rows but I have been unable to figure out how to reference the UISwitch value in the #IBAction function in order to save it to CoreData.
Can anyone point me to simple example of how to do this?
In storyboard, link the UISwitch in your prototype cell to the #IBAction handler in the view controller. In the handler, determine the core data object and manipulate as desired.
Assuming you have a fetched results controller (recommended):
#IBAction didFlipSwitch(sender: UISwitch) {
let point = sender.convertPoint(CGPointZero, toView:tableView)
let indexPath = tableView.indexPathForRowAtPoint(point)
let object = fetchedResultsController.objectAtIndexPath(indexPath) as! Thing
thing.flag = sender.on
}
Addition:
Pursuant to your question: If you are not using a fetched results controller (though this is not recommended), you are presumably using an Array like [Thing]. You would replace the one line with something like:
let object = dataArray[indexPath.row] // no need to cast to Thing
Second, if you need access to other values in the cell, you can get to the cell and its elements with
let cell = tableView.cellForRowAtIndexPath(indexPath) as! CustomCell
let textToRetrieve = cell.textField?.text
However, this is not a good method, because you are getting data from your UI elements. Instead you should always store the data in your model, not in a table view row!
Thus, the proper text attribute of your Thing should already have been set by the UITextFieldDelegate implementation. (You can get the indexPath and thence the desired object in pretty much the same way as above, with convertPoint and indexPathForRowAtPoint.)
Consequently, when you flip the switch and retrieve the Core Data object as shown above, the text attribute be readily available (though you will probably not need it).
Ultimately I resolved this by using both cellForRowAtIndexPath to load the cells from my data model in my custom UITableViewController class and the following code in my custom UITableViewCell class. (Probably this is what Mundi was suggesting but if so I did not understand him.)
class CREWTableViewCell: UITableViewCell {
#IBOutlet weak var myTextView: UITextView!
#IBOutlet weak var mySwitch: UISwitch!
#IBAction func changedSwitch(sender: UISwitch) {
var newDescription = self.myTextView.text // value from cell
var newSwitchValue = self.mySwitch.on // value from cell
var fetchRequest = NSFetchRequest(entityName: "Switch")
var pred1 = NSPredicate(format: "(viewName = %#)",viewName)
var pred2 = NSPredicate(format: "(switchDescription = %#)",newDescription)
fetchRequest.predicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [pred1, pred2])
// Execute the fetch request
var error: NSError? = nil
if let fetchResults = context.executeFetchRequest(fetchRequest, error: &error) as? [Switch]
{
var recordCount = 0
recordCount = fetchResults.count
if recordCount == 1 {
var appConfig = fetchResults [0]
appConfig.switchValue = newSwitchValue
if !managedObjectContext!.save(nil) {
NSLog("Unresolved error ")
abort()
}
}
}
}

iOS swift tableview cell for parse query data

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
var query = PFQuery(className:"category")
let object = objects[indexPath.row] as String
query.whereKey("type", equalTo:"DRUM")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
NSLog("%#", object.objectId)
let abc = object["link"]
println("the web is \(abc)")
cell.textLabel!.text = "\(abc)"
}
} else {
NSLog("Error: %# %#", error, error.userInfo!)
}
}
return cell
}
after add the let object = objects[indexPath.row] as String can't load the view, delete the line show only one row successfully.
First I advise you to get your cell data outside cellForRowAtIndexPath. This function is not a good place to receive data from parse. Make another function and create a class variable and put handle getting data from there.
let object = objects[indexPath.row] as String
for object in objects
Try not to use same variable names for different stuff, as they will confuse you.
This line is not contributing to anything at the moment it seems. Try deleting it:
let object = objects[indexPath.row] as String
First lets have principles in mind. Don't ever update UI from a separate thread, its behavior is unexpected or undefined. It works or works weird.
Second, the problem you have is the when the VC gets loaded the tableView's datasource is called there and then on the main thread. Now you tried to add something on the cell by doing a Async call in separate thread which will take time and main thread is not waiting when the call to parse is being done. If you have difficulty in Async please take a look at the documentation its really important to get a good grasp of the few terms and the principles.
The thing is your main thread runs top to bottom without waiting each call to server thats async in the cell generation. So the result of that call will post later on and you are not posting on main thread too.
Moreover, i would suggest you don't do this approach for big projects or manageable code base. I generally do is:
when the view loads call the Parse with the needed information
Wait for that on a computed variable which i will observe to reload table views once I'm conformed i have the data.
Initially table view will have 0 rows and thats fine. Ill make a spinner dance during that time.
I hope i made some issues clear. Hope it helps you. Cheers!
//a computed var that is initialized to empty array of string or anything you like
//we are observing the value of datas. Observer Pattern.
var datas = [String](){
didSet{
dispatch_async(dispatch_get_main_queue(), {
//we might be called from the parse block which executes in seperate thread
tableView.reloadData()
})
}
}
func viewDidLoad(){
super.viewDidLoad()
//call the parse to fetch the data and store in the above variable
//when this succeeds then the table will be reloaded automatically
getDataFromParse()
}
//get the data: make it specific to your needs
func getDataFromParse(){
var query = PFQuery(className:"category")
//let object = objects[indexPath.row] as String //where do you use this in this block
var tempHolder = [String]()
query.whereKey("type", equalTo:"DRUM")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil && objects != nil {
for object in objects!{
//dont forget to cast it to PFObject
let abc = (object as! PFObject).objectForKey("link") as? String ?? "" //or as! String
println("the web is \(abc)")
tempHolder.append(abc)
}
} else {
print("error") //do some checks here
}
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.text = datas[indexPath.row]
return cell
}