Tableview contents will not appear until screen is clicked - ios

Developing a swift app at the moment with a table view that is populated using cloud kit.
For some reason when the app opens the tableview appears blank, but once the screen is touched my list of entries suddenly appear.
I've provided my whole Tableview Master Class below, My thinking is that it's something to do with the viewDidLoad() function but I can't seem to figure it out no matter what I try.
import UIKit
import CloudKit
import MobileCoreServices
class MasterViewController: UITableViewController {
//// DB setup (outside DidLoad)
let container = CKContainer.defaultContainer()
var Database: CKDatabase?
var currentRecord: CKRecord?
var detailViewController: DetailViewController? = nil
///Array to save dbrecords
var objects = [CKRecord]()
/// Initialise object of ClipManager class
let MyClipManager = ClipManager()
override func viewDidLoad() {
super.viewDidLoad()
// Database loading on runtime
Database = container.privateCloudDatabase
///Build Query
let query = CKQuery(recordType: "CloudNote", predicate: NSPredicate(format: "TRUEPREDICATE"))
///Perform query on DB
Database!.performQuery(query, inZoneWithID: nil) { (records, error) -> Void in
if (error != nil) {
NSLog("Error performing query. \(error.debugDescription)")
return
}
self.objects = records!
self.tableView.reloadData()
}
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem()
let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
self.navigationItem.rightBarButtonItem = addButton
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
override func viewWillAppear(animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Tableview stuff --- Done
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
/////// Get number of rows
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count
}
//// FIll the table
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let object = objects[indexPath.row]
cell.textLabel!.text = object.objectForKey("Notes") as? String
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
//// Deleting the table
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
//DB call working
MyClipManager.DeleteMethod(Database!, MyRecord:objects[indexPath.row])
objects.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
}

Considering the reload is happening in a completion handler, I assume it's on a background thread. Try dispatching your UI update back to the main thread.
(Typing a sample without Xcode here, so check my syntax.)
Example:
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}

If you try these, it works
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})

Swift 5.2:
DispatchQueue.main.async{
self.tableView.reloadData()
}

Related

Update or reload UITableView after completion of delete action on detail view

I have a uitableview that collects data from mysql through json. Then it has a detail view that has two action edit and delete. Edit works fine. Delete action deletes mysql data but problem is it does not update data from uitableview.
Here is screen shot and code
//Table View Controller
import UIKit
class TableViewController: UITableViewController {
var storeList = [Store]()
//var storeList:Store?
override func viewDidLoad() {
super.viewDidLoad()
/*
if let s = storeList
{
txtName.text = s.storeName
}
*/
// Uncomment the following line to preserve selection between presentations
//self.clearsSelectionOnViewWillAppear = true
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.loadRecords()
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData() // to reload selected cell
//tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return storeList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! StoreTVC
let s = storeList[indexPath.row] as Store
cell.lblName.text = s.storeName
//cell.lblID.text = s.storeId
return cell
}
// for swipe delete
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
storeList.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
/*
if segue.identifier == "details"
{
//if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
if let indexPath = tableView.indexPathForSelectedRow
{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
*/
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
func loadRecords()
{
//The address of the web service
let urlString = "http://localhost/crud/read_for_table_view.php"
// 1 - Create the session by getting the configuration and then crrating the session
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
//2 - Create the URL Object
if let url = NSURL(string: urlString)
{
//3 - Create the request object
let request = NSURLRequest(URL: url)
//4 - execute the request
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
//5 - Do something with the Data back
if(data != nil)
{
//we got some data back
print("\(data)")
/*
var parseError:NSError?
let parsedStores = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as! NSDictionary
*/
do {
if let parsedStores = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("Json Data \n \(parsedStores)")
if let stores:AnyObject = parsedStores["result"]
{
self.parseJSON(stores)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}else
{
//we got an error
print("Error getting stores :\(error!.localizedDescription)")
}
})
taskData.resume()
}
}
func parseJSON(jsonData:AnyObject)
{
if let storeData = jsonData as? [[NSObject:AnyObject]]
{
var store:Store
//we loop through all the recors and everytime we create
// an object of kind store and then add to the store list
for s in storeData
{
store = Store()
// this part is getting the values
if let sId:AnyObject = s["id"]
{
if let storeID = sId as? String
{
print("Store id = \(storeID)")
store.storeId = storeID
}
}
if let sn:AnyObject = s["name"]
{
if let storeName = sn as? String
{
store.storeName = storeName
}
}
storeList += [store]
}
NSOperationQueue.mainQueue().addOperationWithBlock()
{
self.tableView.reloadData()
}
}
}
}
//Detail View
import UIKit
class TableViewController: UITableViewController {
var storeList = [Store]()
//var storeList:Store?
override func viewDidLoad() {
super.viewDidLoad()
/*
if let s = storeList
{
txtName.text = s.storeName
}
*/
// Uncomment the following line to preserve selection between presentations
//self.clearsSelectionOnViewWillAppear = true
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.loadRecords()
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData() // to reload selected cell
//tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return storeList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! StoreTVC
let s = storeList[indexPath.row] as Store
cell.lblName.text = s.storeName
//cell.lblID.text = s.storeId
return cell
}
// for swipe delete
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
storeList.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
/*
if segue.identifier == "details"
{
//if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
if let indexPath = tableView.indexPathForSelectedRow
{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
*/
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
func loadRecords()
{
//The address of the web service
let urlString = "http://localhost/crud/read_for_table_view.php"
// 1 - Create the session by getting the configuration and then crrating the session
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
//2 - Create the URL Object
if let url = NSURL(string: urlString)
{
//3 - Create the request object
let request = NSURLRequest(URL: url)
//4 - execute the request
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
//5 - Do something with the Data back
if(data != nil)
{
//we got some data back
print("\(data)")
/*
var parseError:NSError?
let parsedStores = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as! NSDictionary
*/
do {
if let parsedStores = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("Json Data \n \(parsedStores)")
if let stores:AnyObject = parsedStores["result"]
{
self.parseJSON(stores)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}else
{
//we got an error
print("Error getting stores :\(error!.localizedDescription)")
}
})
taskData.resume()
}
}
func parseJSON(jsonData:AnyObject)
{
if let storeData = jsonData as? [[NSObject:AnyObject]]
{
var store:Store
//we loop through all the recors and everytime we create
// an object of kind store and then add to the store list
for s in storeData
{
store = Store()
// this part is getting the values
if let sId:AnyObject = s["id"]
{
if let storeID = sId as? String
{
print("Store id = \(storeID)")
store.storeId = storeID
}
}
if let sn:AnyObject = s["name"]
{
if let storeName = sn as? String
{
store.storeName = storeName
}
}
storeList += [store]
}
NSOperationQueue.mainQueue().addOperationWithBlock()
{
self.tableView.reloadData()
}
}
}
}
Can you please help ?
So I got answer.
here is the update
import UIKit
class TableViewController: UITableViewController {
var storeList = [Store]()
//var storeList:Store?
override func viewDidLoad() {
super.viewDidLoad()
/*
if let s = storeList {
txtName.text = s.storeName
}
*/
// Uncomment the following line to preserve selection between presentations
//self.clearsSelectionOnViewWillAppear = true
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.loadRecords()
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.loadRecords() // to reload selected cell
//tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return storeList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! StoreTVC
let s = storeList[indexPath.row] as Store
cell.lblName.text = s.storeName
//cell.lblID.text = s.storeId
return cell
}
// for swipe delete
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
storeList.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
/*
if segue.identifier == "details" {
//if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)
if let indexPath = tableView.indexPathForSelectedRow {
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
*/
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell) {
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
}
}
func loadRecords() {
//The address of the web service
let urlString = "http://localhost/crud/read_for_table_view.php"
// 1 - Create the session by getting the configuration and then crrating the session
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
//2 - Create the URL Object
if let url = NSURL(string: urlString) {
//3 - Create the request object
let request = NSURLRequest(URL: url)
//4 - execute the request
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
//5 - Do something with the Data back
if (data != nil) {
//we got some data back
print("\(data)")
/*
var parseError:NSError?
let parsedStores = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as! NSDictionary
*/
do {
if let parsedStores = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("Json Data \n \(parsedStores)")
if let stores:AnyObject = parsedStores["result"] {
self.parseJSON(stores)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else {
//we got an error
print("Error getting stores :\(error!.localizedDescription)")
}
})
taskData.resume()
}
}
func parseJSON(jsonData:AnyObject) {
storeList.removeAll()
if let storeData = jsonData as? [[NSObject:AnyObject]] {
var store:Store
//we loop through all the recors and everytime we create
// an object of kind store and then add to the store list
for s in storeData {
store = Store()
// this part is getting the values
if let sId:AnyObject = s["id"] {
if let storeID = sId as? String {
print("Store id = \(storeID)")
store.storeId = storeID
}
}
if let sn:AnyObject = s["name"] {
if let storeName = sn as? String {
store.storeName = storeName
}
}
storeList += [store]
}
NSOperationQueue.mainQueue().addOperationWithBlock() {
self.tableView.reloadData()
}
}
}
}
Here is the better solution for you rather than reload tableview in viewWillAppear. Approach using delegate.
On your Detail View Controller add.
weak var delegate : TableViewController!
Modify line of code on Table View Controller
if segue.identifier == "details"{
if let indexPath = tableView.indexPathForSelectedRow{
let s = storeList[indexPath.row] as Store
let dvc = segue.destinationViewController as! ViewDetails
dvc.store = s
dvc.delegate = self
}
}
When action complete on your Detail View or before dismiss details view. Execute below line in your Detail View.
self.delegate.tableView.reloadData()
Here I hope it's better way and best practice rather than your approach. Try it. :)
Try Below Code:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
storeList.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
self.loadRecords()
}
}
Hope this will help you
I know this is an old question, but my 2 cents are, if you want to reload your tableview, as long as when you're making any updates your storeList array in any way, you can just make use of didSet when declaring your storeList like this:
var storeList: [Store] = []{
didSet{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
With the above, you are saying, any time the storeList array has content removed, added or updated in any way, then on the main UI, reload the tableView, this way you don't need to manually make changes to your tableView, but changes to the storeList will trigger changes to the tableView

TableViewController does not appear for a few seconds after Transition

I have a tabbarcontroller with four tableviewcontrollers that are connected by navigation controllers. The tableviews are popualted by images and text download from the internet by a XMLParser. When the app loads, after the splash screen, the screen goes black for a few seconds, then the first table view appears. Tab clicks on the other tableviews also lag. How can I display something in place of a black screen or unresponsive interface while the tableview controller's data is downlaoded?
The code of one of the tableviews:
import UIKit
class TopicsTableViewController: UITableViewController, XMLParserDelegate {
var xmlParser : XMLParser!
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://sharontalon.com/feed")
xmlParser = XMLParser()
xmlParser.delegate = self
xmlParser.startParsingWithContentsOfURL(url!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: XMLParserDelegate method implementation
func parsingWasFinished() {
self.tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return xmlParser.arrParsedData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("idCell", forIndexPath: indexPath)
let currentDictionary = xmlParser.arrParsedData[indexPath.row] as Dictionary<String, String>
let url = currentDictionary["enclosure"]
let data = NSData(contentsOfURL: url!.asNSURL) //make sure your image in this url does exist, otherwise unwrap in a if let check
let description = currentDictionary["description"]
cell.selectionStyle = UITableViewCellSelectionStyle.None
cell.textLabel?.text = currentDictionary["title"]
cell.detailTextLabel?.text = String(htmlEncodedString: description!)
cell.detailTextLabel?.numberOfLines = 3;
cell.textLabel?.numberOfLines = 2;
cell.imageView?.image = UIImage(data: data!)
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 80
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let dictionary = xmlParser.arrParsedData[indexPath.row] as Dictionary<String, String>
let tutorialLink = dictionary["link"]
let publishDate = dictionary["pubDate"]
let tutorialViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("idTutorialViewController") as! TutorialViewController
tutorialViewController.tutorialURL = NSURL(string: tutorialLink!)
tutorialViewController.publishDate = publishDate
showDetailViewController(tutorialViewController, sender: self)
}
This may be caused by a simple threading issue, give this a shot and if it doesn't work I'll try to help you further:
First move your resource heavy operation to a background thread:
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://sharontalon.com/feed")
xmlParser = XMLParser()
xmlParser.delegate = self
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
self.xmlParser.startParsingWithContentsOfURL(url!)
})
}
Next, move any code that will update the user interface to the foreground thread:
func parsingWasFinished() {
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
If this doesn't resolve your issue, let me know any I'll remove this answer and rethink your problem.
Reloading table data has to be on the main thread of the table to be renewed immediately.
func parsingWasFinished() {
self.tableView.reloadData()
}
Would be:
func parsingWasFinished() {
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}

How to perform a different segue when in Edit mode (in UITableView)?

I am creating a simple iPhone app using UITableView. I am using the default MasterDetail application template. Right now (in Edit mode) when I press any of the table cells nothing happens. However, when I am in normal mode the detail segue is initiated. How to override the Edit mode so that I initiate a custom segue to go to a different UIViewController.
P.S.: I still want to preserve the inherit delete functionality.
This is my code in my MasterViewController:
class MasterViewController: UITableViewController {
let kFileName: String = "/resolutionData.plist"
var resolutions = [Dictionary<String,String>]()
var achievedResolutions = [Dictionary<String,String>]()
// TO DO create a class to get this array
let iconArray = ["Fish","Fly","Heart","HelpingHand","Melon","Star","Tentacles","Volunteering"]
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem()
let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
self.navigationItem.rightBarButtonItem = addButton
//extract the path
NSLog(dataFilePath())
/**
* Check where is the sandbox of the application
* and if there is read from the data file and story it to "objects" array
*/
if NSFileManager.defaultManager().fileExistsAtPath(dataFilePath()){
var temp = NSArray(contentsOfFile: dataFilePath()) as! [Dictionary<String,String>]
for res in temp{
if res["isAchieved"] == "Y"{
achievedResolutions.append(res)
}else{
resolutions.append(res)
}
}
//... if there is not - create it
} else {
let data = [["name":"Resolution name test","startingDate":"24-11-15","achievingDate":"01-01-2016","icon":iconArray[0],"isAchieved":"N"] as NSDictionary] as NSArray
//if the file does not exist...
if !NSFileManager.defaultManager().fileExistsAtPath(dataFilePath()){
//... create it
NSFileManager.defaultManager().createFileAtPath(dataFilePath(), contents: nil, attributes: nil)
}
//write to it
data.writeToFile(dataFilePath(), atomically: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func insertNewObject(sender: AnyObject) {
performSegueWithIdentifier("editDetails", sender: sender)
}
func saveDateToFile(){
let data = resolutions as NSArray
data.writeToFile(dataFilePath(), atomically: true)
}
func notifyTableViewForNewInsertion(){
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = resolutions[indexPath.row] as Dictionary
(segue.destinationViewController as! DetailViewController).detailItem = object
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Active"
}else{
return "Achieved"
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return resolutions.count
}else{
// TODO replace that with an actual array count
return achievedResolutions.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
if indexPath.section == 0{
let object: AnyObject? = resolutions[indexPath.row] ["name"]
cell.textLabel!.text = object as? String
cell.detailTextLabel!.text = resolutions[indexPath.row]["achievingDate"]
cell.imageView!.image = UIImage(named: resolutions[indexPath.row]["icon"]!)
} else {
let object: AnyObject? = achievedResolutions[indexPath.row] ["name"]
cell.textLabel!.text = object as? String
cell.detailTextLabel!.text = resolutions[indexPath.row]["achievingDate"]
cell.imageView!.image = UIImage(named: resolutions[indexPath.row]["icon"]!)
}
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
resolutions.removeAtIndex(indexPath.row)
saveDateToFile()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
func dataFilePath() -> String{
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
//get the first path and convert it to str
let docDicrectyory: String = paths[0] as! String
return "\(docDicrectyory)\(kFileName)"
}
}
Instead of performing your segue directly from the storyboard, add a UITableViewDelegate method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if tableView.editing {
performSegueWithIdentifier("id_Segue_Editing_VC", sender: tableView.cellForRowAtIndexPath(indexPath))
} else {
performSegueWithIdentifier("id_Segue_Standard_VC", sender: tableView.cellForRowAtIndexPath(indexPath))
}
}
Set the sender to the selected cell -- this matches the default UIKit behavior.
Then in your prepareForSegue method you can add custom value to your view controllers according to the segue identifier.
Override willBeginEditingRowAtIndexPath: function. this willbe call before start editing. there you can initialize a global variable.
#property(nonatomic, String) BOOL editing
and in the
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (editing) {
if([[segue identifier] isEqualToString:#"identifierOne"]){
}else if([[segue identifier] isEqualToString:#"identifierTwo"]){
}
}
}
There is a table view property for that:
self.tableView.allowsSelectionDuringEditing = YES;

Data won't load into my Table View

So I have a UITableViewController class for my notes app where everything is working fine, adding, deleting, and editing of the table view. But now I'm trying to load the data using "Realm" Database. I'm fairly new to iOS (Swift) and especially Realm, so I'm kind of confused. I got it to save into the database perfectly, and it shows up when I click the "Save" button in my navigation bar. But when I restart the app, the table view is completely empty. I've been stuck on this for a while now, tried everything I know so far, but just can not get it to show up whatsoever. Can someone help me out, and maybe tell me what I'm doing wrong, or not doing at all? Thank You very much.
Here is my class as well
import UIKit
import Realm
class NoteTableViewController: UITableViewController {
// MARK: Properties
var notes = [Note]() // Initialized with a default value (an empty array of Note objects).
override func viewDidLoad() {
super.viewDidLoad()
// Get Realm Database location
println(RLMRealm.defaultRealm().path)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return notes.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "NoteTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NoteTableViewCell
// Fetches the appropriate note for the data source layout in the notes array
let note = notes[indexPath.row]
// Configure the cell...
cell.titleLabel.text = note.title
cell.bodyLabel.text = note.body
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
notes.removeAtIndex(indexPath.row)
// Get the default Realm (Will do this part later)
/*
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
realm.deleteObject(notes[Int(indexPath.row)])
realm.commitWriteTransaction()
*/
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" { // Clicked on a cell
let noteDetailViewController = segue.destinationViewController as! NoteViewController
// Get the cell that generated this segue.
if let selectedNoteCell = sender as? NoteTableViewCell {
let indexPath = tableView.indexPathForCell(selectedNoteCell)!
let selectedNote = notes[indexPath.row]
noteDetailViewController.note = selectedNote
}
}
else if segue.identifier == "AddItem" { // Clicked add button
println("Adding new note")
}
}
#IBAction func unwindToNoteList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? NoteViewController, note = sourceViewController.note {
if let selectedIndexPath = tableView.indexPathForSelectedRow() { // User clicked on a row
// Update an existing note.
notes[selectedIndexPath.row] = note
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
else {
// Add a new note.
let newIndexPath = NSIndexPath(forRow: notes.count, inSection: 0)
notes.append(note)
// Persist in database
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
Note.createInRealm(realm, withValue: notes[newIndexPath.row])
realm.commitWriteTransaction()
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
}
}
}
And this one is the Note Object class
import UIKit // Automatically imports Foundation
import Realm
class Note: RLMObject {
// MARK: Properties
dynamic var title: String = ""
dynamic var body: String = ""
// MARK: Initialization
init?(title: String, body: String) { // Failable initializer
// Initialize stored properties.
self.title = title
self.body = body
super.init()
// Initialization should fail if there is no title
if title.isEmpty {
return nil
}
}
// Must have for Realm to work
override init() {
super.init()
}
}
Solved it after a good 2 more days...Here is my updated class
Did as Swinny89 said, and started with a new notes object, of all objects, instead of initializing an "empty" notes array.
import UIKit
import Realm
class NoteTableViewController: UITableViewController {
// MARK: Properties
var notes = Note.allObjects()
override func viewDidLoad() {
super.viewDidLoad()
// Get Realm Database location
println(RLMRealm.defaultRealm().path)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return Int(notes.count)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "NoteTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NoteTableViewCell
let object = notes[UInt(indexPath.row)] as! Note
// Configure the cell...
cell.titleLabel.text = object.title
cell.bodyLabel.text = object.body
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete from database
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
realm.deleteObject(notes[UInt(indexPath.row)] as! RLMObject)
realm.commitWriteTransaction()
// Delete row from table view
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" { // Clicked on a cell
let noteDetailViewController = segue.destinationViewController as! NoteViewController
// Get the cell that generated this segue.
if let selectedNoteCell = sender as? NoteTableViewCell {
let indexPath = tableView.indexPathForCell(selectedNoteCell)!
let selectedNote = notes[UInt(indexPath.row)] as! Note
noteDetailViewController.note = selectedNote
}
}
else if segue.identifier == "AddItem" { // Clicked add button
println("Adding new note")
}
}
#IBAction func unwindToNoteList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? NoteViewController, note = sourceViewController.note {
let uuid = NSUUID().UUIDString // Needed for primary key. see below
var unwindedNote = Note()
if let selectedIndexPath = tableView.indexPathForSelectedRow() { // User clicked on a row
// Updating of the note is done in NoteViewController
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
else {
// Add a new note.
let newIndexPath = NSIndexPath(forRow: Int(notes.count), inSection: 0)
// Persist in database
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
unwindedNote.title = note.title
unwindedNote.body = note.body
unwindedNote.id = uuid // This is done for the primary key that Realm needs, unique for each object created.
realm.addObjects([unwindedNote])
realm.commitWriteTransaction()
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
}
}
}
This is because your Note array is initialised as an empty array and that's what you are using to tell the tableView how many rows there are, which is 0.
Once your Note array has been set and has some data you can then call tableView.reloadData() to reload the array with the data.
Also, looking at the code above, your class is inheriting from UITableViewController rather than implementing UITableViewControllerDelegate and UITableViewControllerDataSource. Once your class implements these you need to make sure that you set the viewController as the datasource and delegate for the tableViewController either in the storyboard or through code.

Type does not have a member names 'objectForKey' and use of unresolved identifiers

Using swift/parse to attempt to populate custom cell in the following table view controller. The pfquery code seems to be going fine, but when I attempt to use the the data to populate cell.something.text with what should be returned results, I receive errors indicating that type does not a have a member named 'objectForKey' and Use of unresolved identifiers. The errors are specifically all occurring under the override func tableView(tableView..cellForRowAtIndexPath....
import UIKit
class TimeLineTableViewController: UITableViewController {
var timelineData:NSMutableArray = NSMutableArray()
override init(style: UITableViewStyle) {
super.init(style: style)
// Custom initialization
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func loadData(){
timelineData.removeAllObjects()
//let predicate = NSPredicate(format: PFuser = PFUser.current)
var findTimelineData:PFQuery = PFQuery(className: "event")
//findTimelineData.whereKey(PFUser.self, equalTo: PFUser.currentUser())
findTimelineData.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
self.timelineData.addObject(object)
println(object.objectId)
}
let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = array as NSMutableArray
self.tableView.reloadData()
}
} else {
// Log details of the failure
println("Error: \(error) \(error.userInfo!)")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return timelineData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:TimeLineTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TimeLineTableViewCell
let event:PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
cell.eventLabel.alpha = 0
cell.dateLabel.alpha = 0
cell.minutesLabel.alpha = 0
cell.eventLabel.text = Category.objectForKey("content") as String
cell.minutesLabel.text = duration.objectForKey
var dataFormatter:NSDateFormatter = NSDateFormatter()
dataFormatter.dateFormat = "yyyy-MM-dd HH:mm"
cell.dateLabel.text = dataFormatter.stringFromDate(category.createdAt)
var findRecorder:PFQuery = PFUser.query()
findRecorder.whereKey("objectId", equalTo: event.objectForKey(user).objectId)
findRecorder.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!)->Void in
if error == nil{
let user:PFUser = (objects as NSArray).lastObject as PFUser
UIView.animateWithDuration(0.5, animations: {
cell.eventLabel.alpha = 1
cell.dateLabel.alpha = 1
cell.minutesLabel.alpha = 1
})
}
}
return cell
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
}
Your problem is that you're using variables you didn't initialize anywhere in your code:
cell.minutesLabel.text = duration.objectForKey
^
cell.dateLabel.text = dataFormatter.stringFromDate(category.createdAt)
^
You never initialize duration or category in your code. So you can't access it. You first need to initialize it.
Also I'm not sure but it looks like that you don't import the Parse framework (maybe you do but it's not in the code you've provided)
So you will need to import it first:
import Parse

Resources