SWIFT: Difficultly displaying data in tableView - ios

I am attempting to display data from Parse onto the following tableView controller. For some reason, the data is not displaying on the tableView (i.e. the rows are blank). I do not think that the data queried from Parse is being appended to the arrays. I am wondering what I'm doing wrong here.
Here's the current output:
I am using a custom prototype cell with identifier "CellTrack" class "TrackTableViewCell" and as shown below:
Here is my code in the TableViewController file:
import UIKit
import Parse
class MusicPlaylistTableViewController: UITableViewController {
var usernames = [String]()
var songs = [String]()
var dates = [String]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
var query = PFQuery(className:"PlaylistData")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
self.usernames.removeAll()
self.songs.removeAll()
self.dates.removeAll()
for object in objects {
let username = object["username"] as? String
self.usernames.append(username!)
print("added username")
let track = object["song"] as? String
self.songs.append(track!)
let date = object["createdAt"] as? String
self.dates.append(date!)
self.tableView.reloadData()
}
}
} else {
print(error)
}
}
}
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 usernames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.CreatedOn.text = dates[indexPath.row]
return cell
}
}
And here is my code in the "TrackTableViewCell.swift" class:
import UIKit
class TrackTableViewCell: UITableViewCell {
#IBOutlet weak var songTitle: UILabel!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var CreatedOn: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

Execute your tableView.reloadData() in main thread.
dispatch_async(dispatch_get_main_queue(), {
self.tableViewCell.reloadData()
})

Try doing a guard let to see if those values are actually coming back as string or not. My guess would be that the value for created at never came back. Try it out and let me know.
guard let username = object["username"] as? String else {
print ("could not get username")
}
self.usernames.append(username)
print("added username")
guard let track = object["song"] as? String else {
print ("could not get song")
return
}
self.songs.append(track)
guard let date = object["createdAt"] as? String else {
print ("could not get createdAt")
return}
self.dates.append(date!)

func dequeueReusableCellWithIdentifier(_ identifier: String) -> UITableViewCell?
Return Value
A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
if cell == nil {
// create a new cell here
cell = TrackTableViewCell(...)
}
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.CreatedOn.text = dates[indexPath.row]
return cell
}

Related

Make a tableView that shows previous user inputs (in other views)

I'm stack doing my first app, I searched a lot of tutorials about tableviews, arrays and segues but I can't even figure it out how to resolve my problem, here I go:
I need that the app store a value in an array (class) so I can access it latter (not in the next segue), I did a different app more simple than the last one, just with a UITextfield input and a button to add it to the class. When I move from the user input part to the tableView, the tableView is empty. I will put the code here:
TABLE VIEWCONTROLLER
import UIKit
class NameTableViewController: UITableViewController {
var names = [Name]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in 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 names.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "NameTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath) as? NameTableViewCell else {
fatalError("The dequeueReusable cell is not an instance of NameTableViewCell")
}
let name = names[indexPath.row]
cell.nameLabel.text = name.name
return cell
}
USER INTERFACE VIEWCONTROLLER:
import UIKit
class ViewController: UIViewController {
var name = [Name]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBOutlet weak var nameTextField: UITextField!
#IBAction func addingButton(_ sender: UIButton) {
let writtenName = nameTextField.text ?? "No name written"
let name1 = Name(name: writtenName)
name.append(name1)
}
}
<!-- end snippet -->
VIEWCELL:
class NameTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
<!-- end snippet -->
NAME CLASS METHOD:
class Name {
var name: String
init(name: String) {
self.name = name
}
}
!-- end snippet -->
TableView
User Input
Sorry if this is a dumb question, as you may have notice I'm new programming and swift is the first language that I'm learning.
You can use nsuserdefaults https://developer.apple.com/documentation/foundation/nsuserdefaults and store a key decodable struct and later on call it everywhere.
// Save Data
struct People: Codable {
let name: String?
}
var peopleArray = [People]()
let mike = People(name: "mike")
peopleArray.append(mike)
UserDefaults.standard.set(peopleArray, forKey: "people")
// Request Stored Data
func getPeople() -> [People]?{
let myPeople = UserDefaults.standard.data(forKey: "people")
if myPeople == nil {
return nil
}
let peopleArray = try! JSONDecoder().decode([People].self, from: myPeople!)
return peopleArray
}
let people = getPeople()
if(people != nil){
for person in people {
print(person.name)
}
}

Parse Values in TableViewCell not updating

Very big thanks in advance to anyone who can help me, very much appreciated!
I am building a dating app and I am trying to have my matches load in a table after a parse query. The expected result is that the table view contains the match image and the match ID. Right now I have code for that working perfectly below.
import UIKit
import Parse
class MyListViewController: UIViewController, UITableViewDataSource,
UITableViewDelegate {
var images = [UIImage]()
var userIds = [String]()
#IBOutlet weak var tView: UITableView!
#IBAction func toSwiperButton(_ sender: Any) {
performSegue(withIdentifier: "ToSwiper", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
let query = PFUser.query()
query?.whereKey("objectId", containedIn: PFUser.current()?["accepted"]
as! [String])
query?.findObjectsInBackground(block: { (objects, error) in
if let users = objects {
for object in users {
if let user = object as? PFUser {
let imageFile = user["photo"] as! PFFile
imageFile.getDataInBackground(block: { (data, error) in
if let imageData = data {
self.images.append(UIImage(data: imageData)!)
self.userIds.append(user.objectId!)
self.tView.reloadData()
}
})
}
}
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
internal func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return images.count
}
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PDPLIstViewTableViewCell
cell.Image.image = images[indexPath.row]
cell.id.text = userIds[indexPath.row]
return cell
}
}
The problem arises when I try to include two additional labels onto the table view for "name" and for "age" - I cant seem to figure the correct way to call them in the query in Parse along with the working photo query.
The result I want is for every cell in the table to have an image (code is working) Id (code is working) Name (code not working) and age (code not working)
By "not working" what I mean is I get a ton of errors when I try o create the variable for age from the parse data so I can pass it into the array so that my tableview can display the text next to the image.
Here is what I have been using for non working code on the "Age" label, I believe the error is where I am trying to pull the name/age using "= data" and I have to use a different term?
import UIKit
import Parse
class MyListViewController: UIViewController, UITableViewDataSource,
UITableViewDelegate {
var images = [UIImage]()
var userIds = [String]()
var name = [String]()
var age = [String]()
#IBOutlet weak var tView: UITableView!
#IBAction func toSwiperButton(_ sender: Any) {
performSegue(withIdentifier: "ToSwiper", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
let query = PFUser.query()
query?.whereKey("objectId", containedIn: PFUser.current()?["accepted"]
as! [String])
query?.findObjectsInBackground(block: { (objects, error) in
if let users = objects {
for object in users {
if let user = object as? PFUser {
let ageFile = user["age"] as! PFFile
ageFile.getDataInBackground(block: { (data, error) in
if let ageData = data {
self.age.append(UILabel(data: ageData)!)
}
let imageFile = user["photo"] as! PFFile
imageFile.getDataInBackground(block: { (data, error) in
if let imageData = data {
self.images.append(UIImage(data: imageData)!)
self.userIds.append(user.objectId!)
self.age.append(String(data: ageFile))
self.tView.reloadData()
}
})
}
}
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
internal func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return images.count
}
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for:
indexPath) as! PDPLIstViewTableViewCell
cell.image.image = images[indexPath.row]
cell.id.text = userIds[indexPath.row]
cell.name.text = name[indexPath.row]
cell.age.text = age[indexPath.row]
return cell
}
}
You are reloading the tableview in the loop (a lot), also you do not reload when ageData is complete. Try reloading the tableview once, once the query is done. In the:
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
guard let ageFile = age[indexPath.row] as? PFFile else { return }
ageFile.getDataInBackground(block: { (data, error) in
if let ageData = data {
cell.age.text = ageData
}
}

NSUserDefault synchronize on back button swift

I have a problem with my UITableView. I have 3 views with navigation controller. The second one have a table, the third is a search with some values. I want to click on button search (third view) and open second view and update the table. But not update. To work I have to return to first view and open again second view.
Second view code:
class Empresa: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate{
let textCellIdentifier = "cell"
var menu:[[String]] = [[]]
var buscaEmp:BuscadorEmpresa = BuscadorEmpresa()
#IBOutlet weak var tablaVista: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
recuperaEmpresas()
tablaVista.delegate = self
tablaVista.dataSource = self
}
func recuperaEmpresas(){
menu = buscaEmp.getEmpresas()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menu.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! CustomTableViewCell
let row = indexPath.row
cell.nombreEmp.text = menu[row][0]
return cell
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
recuperaEmpresas()
self.tablaVista.reloadData()
}
As you can see I reload my tableView (tablaVista) on viewWillAppear but don't update.
RecuperaEmpresa() call this method:
func getEmpresas() -> [[String]]{
NSUserDefaults.standardUserDefaults().synchronize()
var array = [[""]]
if((NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")) != nil){
array = NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")! as! [[String]]
return array
}
return array }
I was debugging and it's like getEmpresa don't update first time
EDIT:
GetEmpresa not return the last value the first time, but if I print return correct value. The problem must be NSUSERDEFAULT
EDIT WITH IMAGES
EDITED FOR Alessandro Ornano
Finally my tableView reload but I see an instant my last values and change it with news. I use that:
Second view:
let textCellIdentifier = "cell"
var menu:[[String]] = [[]]
//var buscaEmp:BuscadorEmpresa = BuscadorEmpresa()
#IBOutlet weak var tablaVista: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
recuperaEmpresas()
tablaVista.delegate = self
tablaVista.dataSource = self
}
func recuperaEmpresas(){
self.menu = NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")! as! [[String]]
//self.menu = buscaEmp.getEmpresas()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menu.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! CustomTableViewCell
let row = indexPath.row
cell.nombreEmp.text = menu[row][0]
return cell
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
recuperaEmpresas()
viewDidLoad()
self.tablaVista.reloadData()
}
Third view button call this methods:
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil
{
print("error=\(error)")
return
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
var err: NSError?
do{
let myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
let jsonEmpresas = myJSON["TablaEmp"]!!["item"]
let empresas: [[String: AnyObject]]
if let multipleEmpresas = jsonEmpresas as? [[String: AnyObject]] {
empresas = multipleEmpresas
} else if let singleEmpresa = jsonEmpresas as? [String: AnyObject] {
empresas = [singleEmpresa]
} else {
empresas = []
}
for empresa in empresas{
let zcif = empresa["Zcif"] as? String
let zcccp = empresa["Zcccp"] as? String
let zfax = empresa["Zfax"] as? String
let zdocu = empresa["Zdocu"] as? String
self.arrayEmpresas.append([zcif!, zcccp!, zfax!, zdocu!])
}
NSUserDefaults.standardUserDefaults().setObject(self.arrayEmpresas, forKey:"ARRAYEMPRESA")
NSUserDefaults.standardUserDefaults().synchronize()
}catch { print(error)}
}
task.resume()
And
func back(){
navigationController?.popViewControllerAnimated(true)
}
getMethod:
func getEmpresas() -> [[String]]{
NSUserDefaults.standardUserDefaults().synchronize()
var array = [[""]]
if((NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")) != nil){
array = NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")! as! [[String]]
return array
}
return array
}
When you have this kind of issue many times there are involved synchronism between sampling data and UI update. In your code I dont know where you get this data, how long it takes to arrive and how you set this array on NSUserDefaults. So, if you can't see immediatly your UI updates probably you launch reloadData (main thread) too early because there are still operations to the background thread (http server queries, data fetching...)
You can also write a more elegant:
func getEmpresas() -> [String] {
NSUserDefaults.standardUserDefaults().synchronize()
if let array = NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")! as! [[String]] {
return array
}
return [String]()
}
UPDATE:
I've seen your update code. I want to focus your attention in this method:
func recuperaEmpresas(){
// Are my data ready to read??? who can say to me this in main thread??
menu = buscaEmp.getEmpresas()
}
So you cant call self.tablaVista.reloadData() until you are pretty sure your data can be exists. To do it you can use a completion handler (click me if you want to know how to build it)
func recuperaEmpresas(url: NSURL,completionHandler: CompletionHandler){
callMyNetwork() { //do call and after finish do...
menu = buscaEmp.getEmpresas()
self.tablaVista.reloadData()
}
}
Try to put tableVista.reloadData() in your prepareForSegue or unwindToHome function (whichever you are using to go back to second view). If this is not possible, you can always set a bool var which determines if the view is loaded from the first view or the second view and then reload data accordingly.
Hope this helps. :)

Swift parse query results not appearing in tableview

I am running into difficulty displaying the data from the query I made in the individual cells of my tableview. I believe that my logic is correct, but I'm not seeing the console.log's that I am calling within my function that contains the Parse queried data. This might be a simple fix, but it isn't coming to me at the moment. The console log I should be seeing to validate that my query is coming through correctly is the println("\(objects.count) users are listed"), it should then be displayed within the usernameLabel.text property.
import UIKit
class SearchUsersRegistrationViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var userArray : NSMutableArray = []
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
loadParseData()
}
func loadParseData() {
var query : PFQuery = PFUser.query()
query.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]!, error:NSError!) -> Void in
if error == nil {
if let objects = objects {
println("\(objects.count) users are listed")
for object in objects {
self.userArray.addObject(object)
}
self.tableView.reloadData()
}
} else {
println("There is an error")
}
}
}
let textCellIdentifier = "Cell"
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.userArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! SearchUsersRegistrationTableViewCell
let row = indexPath.row
let cellDataParse : PFObject = self.userArray.objectAtIndex(row) as! PFObject
//cell.userImage.image = UIImage(named: usersArr[row])
cell.usernameLabel.text = cellDataParse.objectForKey("_User") as! String
return cell
}
}
I fixed the issue. I needed to cast the index path row in the users array as a PFUser and then cast the user's username property as a String and then set that as the label text.
let row = indexPath.row
var user = userArray[row] as! PFUser
var username = user.username as String
cell.usernameLabel.text = username

Swift Displaying Parse Image in TableView Cell

I am attempting to display the users image that is saved to parse property "image". I have been able to display my username with no issue, but I can't seem to be able to get my image to appear. Should I be casting this information as UIImage? Am I correctly calling where the file is stored?
Here is my code:
import UIKit
class SearchUsersRegistrationViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var userArray : NSMutableArray = []
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
loadParseData()
var user = PFUser.currentUser()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadParseData() {
var query : PFQuery = PFUser.query()
query.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]!, error:NSError!) -> Void in
if error == nil {
if let objects = objects {
println("\(objects.count) users are listed")
for object in objects {
self.userArray.addObject(object)
}
self.tableView.reloadData()
}
} else {
println("There is an error")
}
}
}
let textCellIdentifier = "Cell"
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.userArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! SearchUsersRegistrationTableViewCell
let row = indexPath.row
var individualUser = userArray[row] as! PFUser
var username = individualUser.username as String
var profilePicture = individualUser["image"] as? UIImage
cell.userImage.image = profilePicture
cell.usernameLabel.text = username
return cell
}
#IBAction func finishAddingUsers(sender: AnyObject) {
self.performSegueWithIdentifier("finishAddingUsers", sender: self)
}
}
The photos are saved in a PFFile and not as a UIImage..
What makes your code the following:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! SearchUsersRegistrationTableViewCell
let row = indexPath.row
var individualUser = userArray[row] as! PFUser
var username = individualUser.username as String
var pfimage = individualUser["image"] as! PFFile
pfimage.getDataInBackgroundWithBlock({
(result, error) in
cell.userImage.image = UIImage(data: result)
})
cell.usernameLabel.text = username
return cell
}
See more in the docs
fileprivate func getImage(withCell cell: UITableViewCell, withURL url: String) {
Alamofire.request(url).responseImage { (image) in
/* Assign parsed Image */
if let parsedImage = image.data {
DispatchQueue.main.async {
/* Assign Image */
cell.imageView?.image = UIImage(data: parsedImage)
/* Update Cell Content */
cell.setNeedsLayout()
cell.layoutIfNeeded()
}
}
}
}

Resources