I've got a PFTableViewController that displays a table of items. I then segue to a navigation controller with root view controller that allows the user to enter a new item - and after they press save, I dismiss the view controller and return to the PFTableViewController.
I would like to reload the PFTableViewController at this point - so that its list includes the item that the user just added. Currently my attempts to get it to reload are not working; the user has to pull to refresh which is broken - it feels like the added item was not properly saved because it doesn't automatically show up.
I've put the self.tableView.reloadData() in the viewDidLoad() function - and it clearly fires as I added a println to verify that it triggers when the new item view is dismissed. But the table is not reloading the data.
Here's 1 version of the code that I've tried:
override func viewDidAppear(animated: Bool) {
println("going to reload")
self.tableView.reloadData()
}
And another, based on another example I saw:
override func viewDidAppear(animated: Bool) {
println("going to reload")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}
Neither are working.
Here's the majority of the PFTableViewController file set-up in case it's useful as well:
import UIKit
import Parse
import ParseUI
class CategoryViewController: PFQueryTableViewController {
var currentObject : PFObject?
var candidates: Array<AnyObject>?
override init(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.parseClassName = "CategoryCandidates"
self.textKey = "candidateTitle"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
override func queryForTable() -> PFQuery {
var query = PFQuery(className: "CategoryCandidates")
query.whereKey("categoryID", equalTo: currentObject!)
query.orderByDescending("votes")
return query
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> CandidateTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! CandidateTableViewCell!
if cell == nil {
cell = CandidateTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
cell?.voteButton.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
// Extract values from the PFObject to display in the table cell
if let candidateTitle = object?["candidateTitle"] as? String {
cell?.textLabel?.text = candidateTitle
}
if let votesTotal = object?["votes"] as? Int {
if votesTotal > 1 {
cell?.votesLabel.text = "\(votesTotal.description) votes"
} else if votesTotal == 1 {
cell?.votesLabel.text = "\(votesTotal.description) vote"
}
}
cell.voteButton.tag = indexPath.row
return cell
}
Thanks in advance!
The PFQueryTable vc must do two things to refresh the data: (1) rerun the query, and (2) after the query has run, reload the table view. The method loadObjects runs the query, and the query's completion handler reloads the table view.
You can use a delegate to notify the PFTableViewController
protocol CategoryViewControllerDelegate {
func reloadTable()
}
class CategoryViewController: PFQueryTableViewController, CategoryViewControllerDelegate {
// MARK: CategoryViewControllerDelegate
func reloadTable() {
self.tableView.reloadTable()
}
// method to segue into adding of new item
fund addNewItem() {
let view = NewItemViewController()
view.delegate = self
presentViewController(view, animated: true, completion: nil)
}
}
class NewItemViewController: UIViewController {
var delegate:CategoryViewControllerDelegate?
func handleAddNewItem() {
// handle the adding of new item here
// ...
// then call the delegate to reload the table
delegate.reloadTable()
// dismiss and return to parent view
// ...
}
}
Related
Sorry, I'm beginner to learn IOS.
I have a problem about my tableView and reload data.
When I frequently call "getData", I will crash and get error.
But I don't know where my data make it crash.
I guess I first call reloadData, and then the list.count are already changed in global thread
Have any advice to avoid it?
Thanks.
Crash Log:
fatal error: Index out of range
Model:
class ChatroomList:Model {
var all:[Chatroom] {
var rooms:[Chatroom] = [Chatroom]()
self.chatrooms.forEach({ (id,chatroom) in
if showType.contains(chatroom.type) {
rooms.append(chatroom)
}
})
return rooms
}
}
ViewController:
import RxCocoa
import RxSwift
import Alamofire
class ListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let chatrooms:ChatroomList = ChatroomList()
var list:[Chatroom] = [Chatroom]()
var subscribe:Disposable?
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
subscribe = rooms.notifySubject.subscribe({ json in
self.getData() //here is called frequently
})
self.getData()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
subscribe?.dispose()
}
func getData() {
var idList:[String] = []
self.list.removeAll()
self.list = chatrooms.all
guard self.list.isEmpty == false else {
return
}
DispatchQueue.global().async() {
self.list.sort(by: { (a,b) in
if a.message.datetime.isEmpty {
return false
}
return a.message.datetime > b.message.datetime
})
self.list = self.list.filter { (chatroom) -> Bool in
if chatroom.id.isEmpty {
return true
}
if idList.contains(chatroom.id) {
return false
}
idList.append(chatroom.id)
return true
}
DispatchQueue.main.sync() {
self.tableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if list[indexPath.row].type == .city {
let cell: ChatroomCityTableViewCell = ChatroomCityTableViewCell(style: .default, reuseIdentifier: nil)
cell.loadByCityChatroom(chatroom: list[indexPath.row], cityId: list[indexPath.row].cityId)
return cell
}else{
let cell: ChatroomTableViewCell = ChatroomTableViewCell(style: .default, reuseIdentifier: nil)
cell.loadByChatroom(chatroom: list[indexPath.row])
return cell
}
}
The problem is most likely caused by how you currently use the GCD (Grand central dispatch).
When reloading, the tableView will ask many different questions like the number of rows and the cells for each of these rows. If the data changes between one of these calls it will result in an inconsistency exception because it tried to add or remove a number of row that no longer represents the data.
Reloading the tableView asynchronously on the main thread while your getData function can change the list at any given time will result in the above error.
The solution is not simple, you need to rethink how to update the list so it won't change while the tableView reload its data.
One thing you could try is to do something like:
func getData() {
// You cannot clear or change self.list here
guard !chatrooms.all.isEmpty else { return }
DispatchQueue.global().async() {
let updatedData = process(newData: self.chatrooms.all)
DispatchQueue.main.sync() {
self.list = updatedData
self.tableView.reloadData()
}
}
}
private func process(newData data: [Chatroom]) -> [Chatroom] {
// Do all your logic without making any changes to self.list
}
The key is to never make any change to the data that is used when reloading the tableView except synchronously on the main thread juste before reloading.
Still very much a Swift noob, I have been looking around for a proper way/best practice to manage row deletions in my UITableView (which uses custom UserCells) based on tapping a UIButton inside the UserCell using delegation which seems to be the cleanest way to do it.
I followed this example: UITableViewCell Buttons with action
What I have
UserCell class
protocol UserCellDelegate {
func didPressButton(_ tag: Int)
}
class UserCell: UITableViewCell {
var delegate: UserCellDelegate?
let addButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Add +", for: .normal)
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
addSubview(addButton)
addButton.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -6).isActive = true
addButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
addButton.heightAnchor.constraint(equalToConstant: self.frame.height / 2).isActive = true
addButton.widthAnchor.constraint(equalToConstant: self.frame.width / 6).isActive = true
}
func buttonPressed(_ sender: UIButton) {
delegate?.didPressButton(sender.tag)
}
}
TableViewController class:
class AddFriendsScreenController: UITableViewController, UserCellDelegate {
let cellId = "cellId"
var users = [User]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! UserCell
cell.delegate = self
cell.tag = indexPath.row
return cell
}
func didPressButton(_ tag: Int) {
let indexPath = IndexPath(row: tag, section: 0)
users.remove(at: tag)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
where the Users in users are appended with a call to the database in the view controller.
My issues
The button in each row of the Table View is clickable but does not do anything
The button seems to be clickable only when doing a "long press", i.e. finger stays on it for a ~0.5s time
Will this method guarantee that the indexPath is updated and will not fall out of scope ? I.e. if a row is deleted at index 0, will deleting the "new" row at index 0 work correctly or will this delete the row at index 1 ?
What I want
Being able to click the button in each row of the table, which would remove it from the tableview.
I must be getting something rather basic wrong and would really appreciate if a Swift knight could enlighten me.
Many thanks in advance.
There are at least 3 issues in your code:
In UserCell you should call:
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
once your cell has been instantiated (say, from your implementation of init(style:reuseIdentifier:)) so that self refers to an actual instance of UserCell.
In AddFriendsScreenController's tableView(_:cellForRowAt:) you are setting the tag of the cell itself (cell.tag = indexPath.row) but in your UserCell's buttonPressed(_:) you are using the tag of the button. You should modify that function to be:
func buttonPressed(_ sender: UIButton) {
//delegate?.didPressButton(sender.tag)
delegate?.didPressButton(self.tag)
}
As you guessed and as per Prema Janoti's answer you ought to reload you table view once you deleted a row as your cells' tags will be out of sync with their referring indexPaths. Ideally you should avoid relying on index paths to identify cells but that's another subject.
EDIT:
A simple solution to avoid tags being out of sync with index paths is to associate each cell with the User object they are supposed to represent:
First add a user property to your UserCell class:
class UserCell: UITableViewCell {
var user = User() // default with a dummy user
/* (...) */
}
Set this property to the correct User object from within tableView(_:cellForRowAt:):
//cell.tag = indexPath.row
cell.user = self.users[indexPath.row]
Modify the signature of your UserCellDelegate protocol method to pass the user property stored against the cell instead of its tag:
protocol UserCellDelegate {
//func didPressButton(_ tag: Int)
func didPressButtonFor(_ user: User)
}
Amend UserCell's buttonPressed(_:) action accordingly:
func buttonPressed(_ sender: UIButton) {
//delegate?.didPressButton(sender.tag)
//delegate?.didPressButton(self.tag)
delegate?.didPressButtonFor(self.user)
}
Finally, in your AddFriendsScreenController, identify the right row to delete based on the User position in the data source:
//func didPressButton(_ tag: Int) { /* (...) */ } // Scrap this.
func didPressButtonFor(_ user: User) {
if let index = users.index(where: { $0 === user }) {
let indexPath = IndexPath(row: index, section: 0)
users.remove(at: index)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
Note the if let index = ... construct (optional binding) and the triple === (identity operator).
This downside of this approach is that it will create tight coupling between your User and UserCell classes. Best practice would dictate using a more complex MVVM pattern for example, but that really is another subject...
There is a lot of bad/old code on the web, even on SO. What you posted has "bad practice" written all over it. So first a few pointers:
Avoid an UITableViewController at all cost. Have a normal view controller with a table view on it
Delegates should always be weak unless you are 100% sure what you are doing
Be more specific when naming protocols and protocol methods
Keep everything private if possible, if not then use fileprivate. Only use the rest if you are 100% sure it is a value you want to expose.
Avoid using tags at all cost
The following is an example of responsible table view with a single cell type which has a button that removes the current cell when pressed. The whole code can be pasted into your initial ViewController file when creating a new project. In storyboard a table view is added constraint left, right, top, bottom and an outlet to the view controller. Also a cell is added in the table view with a button in it that has an outlet to the cell MyTableViewCell and its identifier is set to "MyTableViewCell".
The rest should be explained in the comments.
class ViewController: UIViewController {
#IBOutlet private weak var tableView: UITableView? // By default use private and optional. Always. For all outlets. Only expose it if you really need it outside
fileprivate var myItems: [String]? // Use any objects you need.
override func viewDidLoad() {
super.viewDidLoad()
// Attach table viw to self
tableView?.delegate = self
tableView?.dataSource = self
// First refresh and reload the data
refreshFromData() // This is to ensure no defaults are visible in the beginning
reloadData()
}
private func reloadData() {
myItems = nil
// Simulate a data fetch
let queue = DispatchQueue(label: "test") // Just for the async example
queue.async {
let items: [String] = (1...100).flatMap { "Item: \($0)" } // Just generate some string
Thread.sleep(forTimeInterval: 3.0) // Wait 3 seconds
DispatchQueue.main.async { // Go back to main thread
self.myItems = items // Assign data source to self
self.refreshFromData() // Now refresh the table view
}
}
}
private func refreshFromData() {
tableView?.reloadData()
tableView?.isHidden = myItems == nil
// Add other stuff that need updating here if needed
}
/// Will remove an item from the data source and update the array
///
/// - Parameter item: The item to remove
fileprivate func removeItem(item: String) {
if let index = myItems?.index(of: item) { // Get the index of the object
tableView?.beginUpdates() // Begin updates so the table view saves the current state
myItems = myItems?.filter { $0 != item } // Update our data source first
tableView?.deleteRows(at: [IndexPath(row: index, section: 0)], with: .fade) // Do the table view cell modifications
tableView?.endUpdates() // Commit the modifications
}
}
}
// MARK: - UITableViewDelegate, UITableViewDataSource
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myItems?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell", for: indexPath) as? MyTableViewCell {
cell.item = myItems?[indexPath.row]
cell.delegate = self
return cell
} else {
return UITableViewCell()
}
}
}
// MARK: - MyTableViewCellDelegate
extension ViewController: MyTableViewCellDelegate {
func myTableViewCell(pressedMainButton sender: MyTableViewCell) {
guard let item = sender.item else {
return
}
// Delete the item if main button is pressed
removeItem(item: item)
}
}
protocol MyTableViewCellDelegate: class { // We need ": class" so the delegate can be marked as weak
/// Called on main button pressed
///
/// - Parameter sender: The sender cell
func myTableViewCell(pressedMainButton sender: MyTableViewCell)
}
class MyTableViewCell: UITableViewCell {
#IBOutlet private weak var button: UIButton?
weak var delegate: MyTableViewCellDelegate? // Must be weak or we can have a retain cycle and create a memory leak
var item: String? {
didSet {
button?.setTitle(item, for: .normal)
}
}
#IBAction private func buttonPressed(_ sender: Any) {
delegate?.myTableViewCell(pressedMainButton: self)
}
}
In your case the String should be replaced by the User. Next to that you will have a few changes such as the didSet in the cell (button?.setTitle(item.name, for: .normal) for instance) and the filter method should use === or compare some id or something.
try this -
update didPressButton method like below -
func didPressButton(_ tag: Int) {
let indexPath = IndexPath(row: tag, section: 0)
users.remove(at: tag)
tableView.reloadData()
}
Hoping you can help me out...I've been trying to figure this issue out for a couple days now.
I'm using Parse (www.parse.com) as my backend, and have hosted it on my own AWS server.
Structure of the app:
In AppDelegate, if user is logged in, show a ViewController that sets up my SlideMenuControllerSwift (https://github.com/dekatotoro/SlideMenuControllerSwift) and my TabBarController.
[Storyboard][1]
In my tab bar controller, I have a navigation controller that leads to a UITableViewController that segues to another UITableViewController when I click on a row.
Problem:
http://imgur.com/izdCBgt
1) I click on a row and it performs an asynch query to my parse database
2) The data populates the table and then disappears
3) If I change the tab and go back to the main tab, the data reappears
Also
1) I click on a row and it performs an asynch query to my parse database
2) The data populates the table and then disappears
3) If I go back to the original UITableViewController, it does not transition back properly, and I need to change tabs back and forth until it reappears
Code:
I segue to the documents table view controller using the storyboard segue. Here is the relevant code in my DocumentsTableViewController:
class DocumentTableViewController: UITableViewController, UIDocumentInteractionControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, MMCropDelegate {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
print("initDocs")
}
var specificDocs=[Document](){
didSet{
dispatch_async(dispatch_get_main_queue(), {
//we might be called from the parse block which executes in seperate thread
self.loadView()
})
}
}
override func viewDidLoad() {
tableView.dataSource = self
tableView.delegate = self
print("we loadeD")
super.viewDidLoad()
navigationItem.title = "Job Documents"
let cameraButton = UIBarButtonItem(image: UIImage(named: "Camera"), style: .Plain, target: self, action: #selector(self.didPressCamera(_:)))
navigationItem.rightBarButtonItem = cameraButton
self.queryForTable()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
}
// Define the query that will provide the data for the table view
func queryForTable() {
// Run a spinner to show a task in progress
let progressHUD = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
progressHUD.label.text = "Loading..."
//2 using those jobActions to find which documents are mine
let query = PFQuery(className: Document.parseClassName())
query.whereKey(Document.jobActionCol(), containedIn: jobActions)
query.includeKey(Document.documentCategoryCol())
// do {
// let test = try query.findObjects()
// self.specificDocs = test as! [Document]
// progressHUD.hideAnimated(true)
// } catch {
// print("error!")
// }
// FIND WHY THIS DOESNT WORK....WHAT THE F
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
self.specificDocs = objects as! [Document]
print("done")
progressHUD.hideAnimated(true)
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return specificDocs.count
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("cellIdentifier")
if ((cell) == nil){
cell = UITableViewCell.init(style: .Default, reuseIdentifier: "cellIdentifier")
}
cell!.textLabel?.text = specificDocs[indexPath.row].documentCategory!.type!
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
// print(UIApplication.sharedApplication().keyWindow?.performSelector("recursiveDescription"))
return cell!
}
deinit {
print("docs deinit")
}
}
Oh, now I see intention of your codes. But, Do you have some reason that doing? It isn't right way that directly call loadView().
tableView.dataSource = self
tableView.delegate = self
move to first in viewWillAppear and, It will be work except too fast done to receive from parse.
Normal way will be like below:
var specificDocs=[Document](){
didSet{
dispatch_async(dispatch_get_main_queue(), {
//we might be called from the parse block which executes in seperate thread
self.loadView()
})
}
self.loadView() to self.tableView.reloadData().
tableView.dataSource = self
tableView.delegate = self
That doesn't needs.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()}
That doesn't needs too.
Anyway, your codes will work just modify calling self.loadView() to self.tableView.reloadData()
I have a view in my app called JournalViewController that I'm presenting over my PastSessionsViewController. PastSessions has a table view that the user can tap to edit and bring up the journal.
When the user edits an entry and saves it (saving to CoreData), dismissing JournalViewController I'd like for the table view in PastSessions to reflect those changes and show the updated table cell.
I'm calling tableView.reloadData() in PastSessionsViewController viewDidLoad() but that doesn't seem to be working. I've also added a delegate for JournalViewController to interact with PastSessionsViewController ahead of dismissViewController
Here's some code to look at:
In PastSessionsViewController:
class PastSessionsViewController: UIViewController, UITableViewDelegate, JournalVCDelegate {
weak var tableView: UITableView?
weak var backButton: UIButton?
let pastSessionsDataSource: PastSessionsDataSource
init() {
pastSessionsDataSource = PastSessionsDataSource()
super.init(nibName: nil, bundle: nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView()
tableView.backgroundColor = nil
tableView.delegate = self
tableView.dataSource = pastSessionsDataSource
tableView.registerClass(EntryCell.self, forCellReuseIdentifier: "cell")
view.addSubview(tableView)
self.tableView = tableView
}
override func viewDidAppear(animated: Bool) {
tableView?.reloadData()
}
func didFinishJournalVC(controller: JournalViewController) {
var newDataSource = PastSessionsDataSource()
tableView?.dataSource = newDataSource
// tried this ^, but it's causing the app to crash
// tableView?.reloadData() <- this isn't doing the trick either
dismissViewControllerAnimated(true, completion: nil)
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let editJournalVC = JournalViewController(label: "Edit your thoughts")
editJournalVC.delegate = self
presentViewController(editJournalVC, animated: true, completion: nil)
}
}
In JournalViewController:
protocol JournalVCDelegate {
func didFinishJournalVC(controller: JournalViewController)
}
class JournalViewController: UIViewController, UITextViewDelegate {
var delegate: JournalVCDelegate! = nil
func doneJournalEntry(sender: UIButton) {
journalEntryTextArea?.resignFirstResponder()
... do some core data saving ...
delegate.didFinishJournalVC(self)
}
}
In PastSessionsDataSource:
import UIKit
import CoreData
class PastSessionsDataSource: NSObject {
var arrayOfEntries = [Entry]()
var coreDataReturn: [Meditation]?
func prepareEntries() {
// gets stuff from coredata and formats it appropriately
}
override init() {
super.init()
prepareEntries()
}
}
extension PastSessionsDataSource: UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayOfEntries.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! EntryCell
... set up the labels in the cell ...
return cell
}
}
Thanks for looking!
viewDidLoad is called when the view controller load its view at the first time, so basically it will only be called once during the view controller's whole life cycle.
One quick solution is to put tableView.reloadData() in PastSessionsViewController viewWillAppear() or viewDidAppear().
However I do not like this quick solution as every time you dismiss JournalViewController, the table view will be reloaded, even the user has not changed anything on JournalViewController (for example, cancel the edit). So I suggest to use delegate approach between PastSessionsViewController and JournalViewController, when the user actually edit the data on JournalViewController then inform PastSessionsViewController to refresh the table.
You are currently prepare entries only on init of PastSessionsDataSource, but not after you did CoreData changes. So each time when you reloadData for tableView you work with the same data set loaded initially. As a quick hack you can try to updated viewDidAppear in a following way:
override func viewDidAppear(animated: Bool) {
if let tableView = tableView {
let dataSource = tableView.dataSource! as PastSessionsDataSource
dataSource.prepareEntries()
tableView.reloadData()
}
}
Your tableView property is probably nil in viewDidAppear, based on your listed code. The reason is that in viewDidLoad you construct a UITableView as tableView, and that is a local variable. You need to assign that variable to the property:
self.tableView = tableView
This is very likely a simple answer. I am using PFQueryTableViewController to populate a table view. I want to pass the Parse objectId of the current row to my crDetailViewController but can't figure out how. The variable I'm trying to pass is called critterId.
I've updated my code to reflect Race B's #race-b suggestion below and it enables the build to succeed but I get a breakpoint error when I select an actual cell it creates a breakpoint. When I look at the debug code it shows a value of nil for the object, so it's not getting passed somehow since by definition there's an entity selected, otherwise how would the cell be selectable in the first place? Thanks for help!
import UIKit
class crTableViewController: PFQueryTableViewController {
// Initialise the PFQueryTable tableview
override init!(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Configure the PFQueryTableView
self.parseClassName = "CritterType"
self.textKey = "Name"
self.imageKey = "mainPhoto"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
// Define the query that will provide the data for the table view
override func queryForTable() -> PFQuery! {
var query = PFQuery(className: "CritterType")
return query
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 65
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("crDetailSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
if segue.identifier == "crDetailSegue" {
let detailViewController = segue.destinationViewController
as! crDetailViewController
let myIndexPath = self.tableView.indexPathForSelectedRow()
let row = myIndexPath!.row
println(row)
println(myIndexPath)
detailViewController.critterId = self.objectAtIndexPath(myIndexPath)
}
}
}
There should be an objectAtIndexPath that you can use that will return the object at whatever indexPath.row that you want.
Parse puts the data into an array called objects so to pass the object instead of the ID use the following code:
detailViewController.critter = self.objects[indexPath.row]