Store second value in cell - ios

I need to store the id in cell as well as the text (to display in the listview). So how can I save 2 values in cell?
This is what I have
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("sentence", forIndexPath: indexPath) as! UITableViewCell
//Get list
var request = NSURLRequest(URL: url!)
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
if data != nil {
var dataArray = JSON(data: data!)
var item: NSMutableArray = []
//Get IDs
for (key, data) in dataArray {
var name = data["dutch_sentence"]
println(name)
item.addObject(name.string!)
}
cell.textLabel!.text = item[indexPath.row] as? String
return cell
}
else{
println("Out")
return cell;
}
}
But I want to send the id along too because I want to use it for the JSON array URL in the next view. So I would need something like:
cell.id = 2
To be able to call it in didSelectRowAtIndexPath
How could I do that?

Storing data in you views is very bad design decision. You should save your data in array and then access it based on indexPath.row:
let ids = [5, 10, 20]
public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let id = ids[indexPath.row]
}

Related

Retrieve photos from firebase storage SWIFT

I have a problem since yesterday morning but I can't figure it out how can I resolve this issue.
I'm having a table view which is using prototype cells, 2 labels and 1 photo. For the labels I used Firestore and for the picture firebase storage.
The problems is that the only way I know how to retrieve photos from my firebase storage is this code
let storage = Storage.storage()
let storageRef = storage.reference()
let ref = storageRef.child("Mancare/Mancare3.jpg")
testImage.sd_setImage(with: ref)
I want to retrieve the photos into my table view, but I do not know how can I can accomplish that.
This is what im using for retrieving the labels with Firestore . I'll paste only the necessary parts of the code :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return labels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewTest.dequeueReusableCell(withIdentifier: "CountryTableViewCell", for: indexPath) as! CountryTableViewCell
let storage = Storage.storage()
let storageRef = storage.reference()
let ref = storageRef.child("Mancare/Mancare3.jpg")
let label = labels[indexPath.row]
cell.labelTest.text = label.firstLabel
cell.labelLaba.text = label.secondLabel
return cell
}
func getDatabaseRecords() {
let db = Firestore.firestore()
labels = [] // Empty the array
db.collection("labels").getDocuments { (snapshot, error) in
if let error = error {
print(error)
return
} else {
for document in snapshot!.documents {
let data = document.data()
let newEntry = Labels(
firstLabel: data["firstLabel"] as! String,
secondLabel: data["secondLabel"] as! String)
self.labels
.append(newEntry)
}
}
DispatchQueue.main.async {
self.tableViewTest.reloadData()
}
}
}
This is how I declared the labels:
struct Labels {
let firstLabel: String
let secondLabel: String
}
var labels: [Labels] = []
If someone can help me , ill be forever grateful . Thanks
First, you need to fix your model so it can help you. Add the bucket name to the model like this:
Struct Labels {
let firstLabel: String
let secondLabel: String
let photoKey: String // This will store the bucket name for this `Labels`
}
Now in your getDatabaseRecords change:
let newEntry = Labels(firstLabel: data["firstLabel"] as! String,
secondLabel: data["secondLabel"] as! String),
photoKey: data["photoKey"] as! String) // Added Line
Then in cellForRow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewTest.dequeueReusableCell(withIdentifier: "CountryTableViewCell", for: indexPath) as! CountryTableViewCell
let label = labels[indexPath.row]
let storageRef = Storage.storage().reference()
let photoRef = storageRef.child(label.photoKey)
cell.labelTest.text = label.firstLabel
cell.labelLaba.text = label.secondLabel
cell.imageView.sd_setImage(with: photoRef) // Assuming the image view in your cell is named this
return cell
}
Last, make sure your document structure matches the new Labels Model in the firebase console, and you have images as well in the root of your storage that match with all the photoKeys. Btw, Labels is not a very good model name, I just went with it for consistency

Multiple Table View with different arrays

I have a detail view that shows the details of an event, the people who participate and the people who asked to participate. I have created two arrays of different types but they have the same fields, only that a first structure represents the users with the 'status_confirm' field equal to 1 (therefore Accepted Users), while the other has as 'status_confirm' equal to 0 (Users awaiting acceptance). I declared two arrays, the first one: var arrayUserAccepted = [User_accepted] ().
The second one: var arrayUserWaiting = [User_waiting] (). Struct Image
Next step: I populate these structures via a php script
func getData(){
let url = URL(string: “MYURL”)
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String:AnyObject]
print("JSON: \n\(json)\n")
let waiting = json["waiting"] as! [AnyObject]
let accepted = json["accepted"] as! [AnyObject]
DispatchQueue.main.async {
for list_user_waiting in waiting {
let id_user_waiting = list_user_waiting["id_user”] as! String
let name_user_waiting = list_user_waiting[“name_user”] as! String
let email_user_waiting = list_user_waiting["email"] as! String
var photo_user_waiting = list_user_waiting[“photo”]
let status_user_waiting = list_user_waiting["status”] as! String
if photo_user_waiting is NSNull {
photo_user_waiting = ""
}
let listUserWaiting = User_waiting(id_user_waiting: id_user_waiting, name_user_waiting: name_user_waiting, email_user_waiting: email_utente_attesa, foto_waiting: photo_user_waiting as! String, status_waiting: status_user_waiting)
self.arrayUserWaiting.append(listUserWaiting)
self.tableViewListUserWaiting.reloadData()
}
for list_user_accepted in accepted {
let id_user_accepted = list_user_accepted["id_utente"] as! String
let name_user_accepted = list_user_accepted["name_utente"] as! String
let email_user_accepted = list_user_accepted["email"] as! String
var photo_user_accepted = list_user_accepted[“photo"]
let status_user_accepted = list_user_accepted["status”] as! String
if photo_user_accepted is NSNull {
photo_user_accepted = ""
}
let listUserAccepted = User_accepted(id_user: id_user_accepted, nome_utente: name_user_accepted, email: email_user_accepted, foto: photo_user_accepted as! String, stato: status_user_accepted)
self.arrayUserAccepted.append(listUserAccepted)
self.tableViewListUserAccepted.reloadData()
}
}
} catch let error as NSError {
print(error)
}
}).resume()}
This above is a function that I call in the viewDidLoad(). The next step would be to use the functions of the table view and it is here that I think there is the injunction
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == self.tableViewListUserAccepted {
count = arrayUserAccepted.count
}
if tableView == self.tableViewListUserWaiting {
count = arrayUserWaiting.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
if tableView == self.tableViewListUserAccepted {
cell.imageProfileUserAccepted.image = UIImage(named: "imageDefault")
cell.valueSliderUserAccepted.value = Float(50) //JUST FOR POPULATE THE INTERFACE
cell.name_user_accepted.text = arrayUserAccepted[indexPath.row].name_user
}
if tableView == self.tableViewListUserWaiting {
cell.imageProfileUserWaiting.image = UIImage(named: "imageDefault")
cell.valueSliderUserWaiting.value = Float(23) //JUST FOR POPULATE THE INTERFACE
cell.name_user_waiting.text = arrayUserWaiting[indexPath.row].name_user_waiting
}
return cell
}
Once done all this round, I start the application but nothing. The tables are empty. In the console the script answers me correctly and so I can not figure out where the error could be. Needless to say, I have declared the .delegate and .dataSource of both tables, both in the Main.Storyboard and in the code.
Everything is fine just change the format of IF condition and it will work.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == self.tableViewListUserAccepted {
count = arrayUserAccepted.count
} else {
count = arrayUserWaiting.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableViewListUserAccepted {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.imageProfileUserAccepted.image = UIImage(named: "imageDefault")
cell.valueSliderUserAccepted.value = Float(50) //JUST FOR POPULATE THE INTERFACE
cell.name_user_accepted.text = arrayUserAccepted[indexPath.row].name_user
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.imageProfileUserWaiting.image = UIImage(named: "imageDefault")
cell.valueSliderUserWaiting.value = Float(23) //JUST FOR POPULATE THE INTERFACE
cell.name_user_waiting.text = arrayUserWaiting[indexPath.row].name_user_waiting
return cell
}
}
Also check if the datasource and delegate of both of your tableView are set. Finally call the tableView.reloadTable() method on both of your tableviews after you populate your arrays in the viewDidLoad() method.

How to return a cell in tableview if it is nested inside an if statement

This is my code —- I am getting error when returning cell1 inside the if statement as it says ” Cannot return a non void return value in void function.I want to return the cell in tableview .. and i have 3 kind of posts .. one for status one for image one for video post. How can i return the cell for each.
P.S. : I have just provided the code for one post type only as if one is solved then all other can be solved.
import UIKit
import Alamofire
class ViewController: UIViewController , UITableViewDelegate ,
UITableViewDataSource{
#IBOutlet weak var feedTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
feedTable.dataSource = self
feedTable.delegate = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 376
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
Alamofire.request("https://new.postpfgsdfdsgshfghjoves.com/api/posts/get_all_posts").responseJSON { response in
let result = response.result
if let dict = result.value as? Dictionary<String,AnyObject> {
if let successcode = dict["STATUS_CODE"] as? Int {
if successcode == 1 {
if let postsArray = dict["posts"] as? [Dictionary<String,AnyObject>]
{
for i in 0..<postsArray.count
{
let posttype = postsArray[i]["media_type"] as! String
if posttype == "image"
{
let cell1 : ImageTableViewCell = self.feedTable.dequeueReusableCell(withIdentifier: "imageReuse") as! ImageTableViewCell
cell1.fullName = postsArray[i]["full_name"] as? String
cell1.profileImageURL = postsArray[i]["profile_pic"] as? String
cell1.location = postsArray[i]["location"] as? String
cell1.title = postsArray[i]["title"] as? String
cell1.postTime = postsArray[i]["order_by_date"] as? String
cell1.likes = postsArray[i]["liked_count"] as? Int
cell1.comments = postsArray[i]["comment_count"] as? Int
cell1.imageURL = postsArray[i]["profile_pic"] as? String
cell1.imageLocation = postsArray[i]["location"] as? String
cell1.content = postsArray[i]["content"] as? String
cell1.profileFullName.text = cell1.fullName
cell1.titleImagePost.text = cell1.title
cell1.postLocation.text = cell1.location
cell1.profileUserLocation.text = cell1.location
cell1.numberOfLikes.text = "\(cell1.likes!) Likes"
cell1.numberOfComments.text = "\(cell1.comments!) Comments"
cell1.postTimeOutlet.text = postsArray[i]["posted_on"] as? String
let url = URL(string: cell1.imageURL!)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell1.profileImage.image = UIImage(data: data!)
let url1 = URL(string: cell1.imageURL!)
let data1 = try? Data(contentsOf: url1!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell1.postedImage.image = UIImage(data: data1!)
// return cell1
}
else if posttype == "status"
{
let cell1 : StatusTableViewCell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "statusReuse") as! StatusTableViewCell
cell1.fullName = postsArray[i]["full_name"] as? String
cell1.profileImageURL = postsArray[i]["profile_pic"] as? String
cell1.location = postsArray[i]["location"] as? String
cell1.title = postsArray[i]["title"] as? String
cell1.postTime = postsArray[i]["order_by_date"] as? String
cell1.likes = postsArray[i]["liked_count"] as? Int
cell1.comments = postsArray[i]["comment_count"] as? Int
cell1.postContent = postsArray[i]["content"] as? String
cell1.profileFullName.text = cell1.fullName
cell1.titleStatusPost.text = cell1.title
cell1.postLocation.text = cell1.location
cell1.profileUserLocation.text = cell1.location
cell1.content.text = cell1.postContent
cell1.numberOfLikes.text = "\(cell1.likes!) Likes"
cell1.numberOfComments.text = "\(cell1.comments!) Comments"
cell1.postTimeOutlet.text = "\(cell1.postTime!)"
let url = URL(string: cell1.profileImageURL!)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell1.profileImage.image = UIImage(data: data!)
// return cell1
}
else if posttype == "video"
{
let cell1 : VideoTableViewCell = self.feedTable.dequeueReusableCell(withIdentifier: "videoReuse") as! VideoTableViewCell
cell1.fullName = postsArray[i]["full_name"] as? String
// cell1.profession = postsArray[i]["profession"] as? String
cell1.profileImageURL = postsArray[i]["profile_pic"] as? String
cell1.location = postsArray[i]["location"] as? String
cell1.title = postsArray[i]["title"] as? String
cell1.postTime = postsArray[i]["order_by_date"] as? String
cell1.likes = postsArray[i]["liked_count"] as? Int
cell1.comments = postsArray[i]["comment_count"] as? Int
cell1.videoURL = postsArray[i]["profile_pic"] as? String
cell1.profileFullName.text = cell1.fullName
cell1.titleVideoPost.text = cell1.title
cell1.postLocation.text = cell1.location
cell1.profileUserLocation.text = cell1.location
// return cell1
}
}
}
}
}
}
}
}
}
My answer isn't any different from the others but let me be a little more specific. I'll use a generic example and you'll need to tailor this to your specific needs.
1) Define a model somewhere for your data such as:
class MyDataItem {
var name: String
var title: String
var location: String
init(name: String, title: String, location: String) {
self.name = name
self.title = title
self.location = location
}
}
2) Define an array in your Viewcontroller such as:
var dataArray = [MyDataItem]()
3) Load the data which you could do from the viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
feedTable.dataSource = self
feedTable.delegate = self
loadData()
}
4) Implement loadData() function:
func loadData() {
// Here put in your alamo enclosure to retrieve the data and store it into the array you've defined
// When done, call reload data
feedTable.reloadData()
}
5) Your cellForRowAt function will need to be modified to retrieve the data from the array. For example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell1 : ImageTableViewCell = tableView.dequeueReusableCell(withIdentifier: "imageReuse") as! ImageTableViewCell
cell1.fullName = dataArray[indexPath.row].name
cell1.title = dataArray[indexPath.row].title
cell1.location = dataArray[indexPath.row].location
return cell1
}
Anyway, this is the general idea on how to do what you are attempting. When reloadData is called from your loadData function, it will cause the tableview to reload from the array data correctly.
Hope this helps!
The problem is you do not return the cell, you simply make some async request with alamofire and return an instance of the cell from the closure.
func foo() -> Int { return 1 } ≠ func bar() -> Int { someClosure { return 1 } }
Firstly you need load the the data from https://www.example.com/api/posts/get_all_posts into some data model.
var models: [SomeTypeYouCreate] = []
func loadData() {
Alamofire.request(...).responseJSON { response in
self.models = /* Create array of `SomeTypeYouCreate` objects from response */
self.tableView.reloadData()
}
}
func func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = self.models[indexPath.row]
// configure cell with model
return cell
}
You cannot do it the way you're trying to. You're not returning a cell from cellForRowAt method, you're returning it in Alamofire callback closure. What you should do is to return the cell in your cellForRowAt method, and implement some sort of setup method for your UITableViewCell subclass and make your calls in there
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell1 : ImageTableViewCell = self.feedTable.dequeueReusableCell(withIdentifier: "imageReuse") as! ImageTableViewCell
// put your Alamofire code inside such function in your UITableViewCell subclass
cell.setup()
return cell
}
First and foremost, you are returning value in closure Alamofire.request. If you wanna use cell after you confirm cell values, you want to pass over completion handler to the function and use it in that Alamofire.reqeust...
But if I were you, I would create another function which is called before/after tableView function.
If it is Before then trigger tableview initialization upon alamofire completion.
If it is After then reload when values are loaded correctly in Alamofire.
EDITED:
Like other suggested,it is bad idea to load data in tableView function. Also, by using Alamofire, it means you use Closure. That is, whatever you wanna do in Alamofire happens asynchronously, meaning by the time what you want to achieve in Alamofire is done, your program can be out of the table view function. Also, since it is closure, returning value in Alamofire does not satisfy your tableView return type.
So basically, if you need data via API and verify, you declare function such that do whatever you doing Alamofire and then reload the tableView.
So flow is like this:
1) Make an empty array and put array.count to # of rows.
2) Since it is empty, when tableView first try to generate cells, it doesn't do anything.
3) You call the function which uses Alamofire. If returned values are good, then add the cell(model) to the array.
4) After you are done loading models, do tableView.reload().
5) Tableview calls tableView function now it finds value in array so that will create cells.

Deleting certain objects in background from UITableViewCell With Swift and Parse

So I have a UITableViewCell and I want to delete the image from the tableView. But I also want to delete the given object from the Parse database.
I have the image object saved in the online Parse framework and in the Local Datastore.
for i in images{
var imageNames = i.thumbnailImage
var imageFile = PFFile(data: UIImagePNGRepresentation(imageNames))
//Pin the objects here!!
var imageObject = PFObject(className: "img")
imageObject["theImg"] = imageFile
//imageObject["user"] = PFUser.currentUser()?.username
imageObject.pinInBackgroundWithName("imgs")
var objID = imageObject.objectId
imageObject.pinInBackgroundWithBlock({ (success, error) -> Void in
if error == nil{
println("Object Saved!!")
imageObject.save()
println(objID)
self.imagesFromParse.append(imageNames!)
self.tableView.reloadData()
} else {
println(error)
}
})
I then have the delete UITableViewCell function called here -
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
var cell:nudeCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! nudeCell
if editingStyle == UITableViewCellEditingStyle.Delete{
if self.imagesFromParse.count >= 1{
self.imagesFromParse.removeAtIndex(indexPath.row)
//Here is where i want to delete the object from the localDatastore and the online framwork
}
}
}
The problem is that when I try to delete the object in the background. I do not know how to get the specific object ID in the given indexPath to delete.

duplicate content in uitableviewcells

I'm using a PFQueryTableViewController and trying to create a videoCell for each item in Parse. I only want to play the current active cell.
This is my cellForRowAtIndexPath method.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell: VideoTableViewCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? VideoTableViewCell
// cell?.parseObject = object
if let pfObject = object {
cell?.nameLabel?.text = pfObject["name"] as? String
print(pfObject["name"])
}
let file: PFFile? = object!.objectForKey("videoFile") as? PFFile
if self.currentActiveIndex == nil && file != nil {
self.currentActiveIndex = indexPath.row
print(file)
let fileUrl: NSURL? = NSURL(string: file!.url!)
let player: AVPlayer? = AVPlayer(URL: fileUrl!)
player?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.Initial, context: &PlayerStatusContext)
cell?.cellVideoView.setPlayer(player!)
cell?.cellVideoView.setVideoFillMode(AVLayerVideoGravityResizeAspectFill)
}
print(objects?.count)
return cell
}
For some reason the video is appearing both in the 1st and 4th cells, every time. I'm curious as to why. Thanks
You need to ensure that the player is removed when the cell is re-used.
Basically, this means you need an else clause for your if. Probably something like -
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell: VideoTableViewCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? VideoTableViewCell
// cell?.parseObject = object
if let pfObject = object {
cell!.nameLabel!.text = pfObject["name"] as! String
print(pfObject["name"])
}
let file: PFFile? = object!.objectForKey("videoFile") as? PFFile
if self.currentActiveIndex == nil && file != nil {
self.currentActiveIndex = indexPath.row
print(file)
let fileUrl: NSURL? = NSURL(string: file!.url!)
let player: AVPlayer? = AVPlayer(URL: fileUrl!)
player?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.Initial, context: &PlayerStatusContext)
cell?.cellVideoView.setPlayer(player!)
cell?.cellVideoView.setVideoFillMode(AVLayerVideoGravityResizeAspectFill)
}
else {
if let player=cell!.cellVideoView.player {
player.removeObserver(self, forKeyPath:"status")
cell!.cellVideoView.setPlayer(nil)
}
}
print(objects?.count)
return cell
}
Also, you have a lot of ? where you can use !. For example, cell won't be nil (and if it is, that is bad and an exception should be thrown)and the cast to VideoTableViewCell or String or even PFFile shouldn't fail and if it does then something has gone wrong an exception is probably appropriate so that you can debug.
Let's think of it this way. When you start loading your screen, you have a your currentActiveIndex as nil right? So what happens when that cell loads? Boom - the video shows up there.
And then of course, you go and actually set the currentActiveIndex when you tap on it and create a new cell, that's why you get this behavior. Here is what you should really do.
Have currentActiveIndex as a global variable or so. When you select a new value or so, just do a self.tableview.reloadData() which refreshes the whole UITableView or a self.tableview.refreshSections("THE SECTION WITH THE VIDEOS"). Decouple the currentActiveIndex logic from the UI Part.

Resources