How to append array of value into tableView in iOS Swift? - arrays

TableView consists with list of dynamic label and navigation bar consists of two button called up and down.
here i'm getting data from json and append to model. But i can not able to list in tableview. Array value should be loaded to list of label.
Here is my json appended to model:
[PO_PR.poItemsModel(Material: Optional("RMECC_MOB1"), Qty: Optional("2.000"), UoM: Optional("KG"), totalValue: Optional("1000.000"), Requistor: Optional(""), StorageLocation: Optional("3001"), MatGrp: Optional("00107"), valuationPrice: Optional("1000.000"), ItemCategory: Optional("0"), lineNo: Optional("00010"), UnitofPrice: Optional("1.000")),
PO_PR.poItemsModel(Material: Optional("RMECC_MOB1"), Qty: Optional("2.000"), UoM: Optional("KG"), totalValue: Optional("1000.000"), Requistor: Optional(""), StorageLocation: Optional("3001"), MatGrp: Optional("00107"), valuationPrice: Optional("1000.000"), ItemCategory: Optional("0"), lineNo: Optional("00020"), UnitofPrice: Optional("1.000"))]
Initially array of first index should be loaded to list of label and then when pressing down button next of array value should be loaded to list of label.
Labels in tableview will be in each cell not in a single cell.
Here is my model:
struct poItemsModel {
var Material : String?
var Qty : String?
var UoM : String?
var totalValue : String?
var Requistor : String?
var StorageLocation: String?
var MatGrp : String?
var valuationPrice : String?
var ItemCategory : String?
var lineNo : String?
var UnitofPrice : String?
init(json: JSON) {
Material = json["Material"].stringValue
Qty = json["Quantity"].stringValue
UoM = json["OrderUnit"].stringValue
totalValue = json["NetPrice"].stringValue
Requistor = json["Requistor"].stringValue
StorageLocation = json["StorageLocation"].stringValue
MatGrp = json["MatGroup"].stringValue
valuationPrice = json["NetPrice"].stringValue
ItemCategory = json["ItemCat"].stringValue
lineNo = json["Item"].stringValue
UnitofPrice = json["UnitofPrice"].stringValue
}
}
Here i'm appeding json data to model:
func GetPoItemCount() {
if orderNo != nil {
// Call API
print("orderni::\(orderNo!)")
PoVendorListApiManager.sharedInstance.getPoListWithModel(orderString: orderNo!){ (json:JSON) in
// return json from API
if let results = json["d"]["results"].array {
print("catefory::\(results)")
for category in results {
self.PoItemsArray.append(poItemsModel(json: category))
for (key, value) in category {
print("keyvalue\(key)\(value)")
self.poItemJsonArray.append(value)
self.poItemKeyArray.append(key)
let currency = newPrItemDetailModel(key: key, value: value)
self.newItemDetailsArray.append(currency)
}
}
let elm = self.PoItemsArray
let mirror = Mirror(reflecting: elm)
for child in mirror.children {
print("key: \(String(describing: child.label)), value: \(child.value)")
}
print("self.PoItemsArray array:::\(self.PoItemsArray)")
print("new model array:::\(self.newItemDetailsArray)")
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
}//extension
Here is my tableview code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// PoItems = loadPoItems()
print("count::itemArray::\(self.PoItemsArray.count)")
return PoItemsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! PoItemTableViewCell
let itemDetails = PoItemsArray[indexPath.row]
// for arrayData in
// PoItems = loadPoItems()
// prItem.getProperty(at: indexPath.row)
// cell.materials.text = PoItemsArray[0].key!
// cell.materialsValue.text = itemDetails[indexPath.row]
return cell
}
Here is my screenshot where i'm trying to achieve:
How to load to array of first index into list of dynamic labels and when pressing down button how to load next array index of values into list of dynamic labels.
Any help much appreciated pls....

This can not be done (realistically) with a table view. Table views are for long lists (a variable number) of the same item which can not change. What you have here is a fixed number (9) of totally different items, which do change.
Answer 1:
Do not use a table view for this. It's not what a table view is for.
Simply use a StackView for your layout - it's dead easy, and still scrolls etc.
Done. It will take 2 minutes.
Answer 2:
If as an exercise you want to use a table view, which would be wrong:
To do "synced / dynamic" tables is extremely difficult.
You must have a custom cell class
The button (on the main screen) must signal in some way to all of the cells to make the change in question.
This is a broad field of enquiry, you'd have to look in to the various ways to achieve this.

Related

Swift - Tableview won't reload correctly after loading for the second time

I am a little confused about how my Tableview is reacting. My problem is that the Tableview shows the other order than Dictionary than when I print the Dictionary. When I print the Dictionary everything seems to be fine, but when I look at my phone the tableview is showing the cell at a random order again.
First I fetch the JSON.
var Aanbiedingen_bieren = [Aanbiedingen_bier]()
var Aanbiedingen_bieren_Filter = [Aanbiedingen_bier]()
Fetch data.
func fetchData() {
Aanbiedingen_bieren_Filter.removeAll()
Aanbiedingen_bieren.removeAll()
let url_Aanbiedingen_bier = URL(string: "\(Firebase_url)")
let downloadURL = url_Aanbiedingen_bier
URLSession.shared.dataTask(with: downloadURL!) { data, urlResponse, error in
let data = data
print("downloaded")
do
{
let decoder = JSONDecoder()
let downloadedBiers = try decoder.decode(SheetJS.self, from: data!)
self.Aanbiedingen_bieren = downloadedBiers.SheetJS
for jsonData in self.Aanbiedingen_bieren {
let Soort = jsonData.Title
if self.Soort_Bier == "Alles" {
self.Aanbiedingen_bieren_Filter.append(jsonData)
}else{
if self.Soort_Bier == "Krat" {
if Soort.contains(word: "Krat") {
self.Aanbiedingen_bieren_Filter.append(jsonData)
}
}
if self.Soort_Bier == "Fles" {
if Soort.contains(word: "Fles") {
self.Aanbiedingen_bieren_Filter.append(jsonData)
}
}
}
}
self.Aanbiedingen_bieren_Filter = self.Aanbiedingen_bieren_Filter.sorted(by: {$0.Voor_prijs.compare($1.Voor_prijs, options: .numeric) == .orderedAscending})
print(self.Aanbiedingen_bieren_Filter)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}.resume()
}
The first time that the code runs the Soort_Bier = "Alles". After a button tap the Soort_Bier will change in Soort_bier = "Krat". After the data was loaded for a second time I first wanted to filter the data. I have this done by implementing an if statement that checks if the Title of the JSON has a specific word in the String and if so append it to an other array. After that, I wanted to sort the price. When the sorting is finished I wanted to print the Dictionary to see if the sorting is correct. This still seems to be the case. Then I want to reload the tableView so all of the cells will show and here is something wrong. When I want to load the dictionary for the second time, it doesn't seem to reload the tableView correctly.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Aanbiedingen_bieren_Filter.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Aanbiedingen_bieren_Cell", for: indexPath) as! Aanbiedingen_bieren_Cell
let Aanbiedingen_bier = self.Aanbiedingen_bieren_Filter[indexPath.row]
cell.Aanbiedingen_bier = Aanbiedingen_bier
return cell
}
Struct:
struct SheetJS: Codable {
var SheetJS: [Aanbiedingen_bier]
init(SheetJS: [Aanbiedingen_bier]) {
self.SheetJS = SheetJS
}
}
struct Aanbiedingen_bier: Codable {
let Logo_Image: String
let Van_prijs: String
let Voor_prijs: String
let Beschrijving: String
let Item_Image: String
let Title: String
let Bestel_Online_link: String
let Footer_item: String
let Item_1: String
let Item_2: String
init(Logo_Image: String, Item_Image: String, Van_prijs: String, Voor_prijs: String, Beschrijving: String, Title: String, Item_1: String, Item_2: String, Bestel_Online_link: String, Footer_item: String) {
self.Logo_Image = Logo_Image
self.Title = Title
self.Item_Image = Item_Image
self.Beschrijving = Beschrijving
self.Van_prijs = Van_prijs
self.Voor_prijs = Voor_prijs
self.Item_1 = Item_1
self.Item_2 = Item_2
self.Bestel_Online_link = Bestel_Online_link
self.Footer_item = Footer_item
}
}
JSON:
{
"SheetJS": [
{
"Logo_Image": "https://www.biernet.nl/images/winkel/17335-agrimarkt.gif",
"Van_prijs": "€16,99",
"Voor_prijs": "€10,49",
"Beschrijving": "Krat 24x0,30",
"Item_Image": "https://www.biernet.nl/images/soort/23026-grolsch%20krat%20normale%20flesjes%2030%20cl.png",
"Title": "Grolsch Premium Pilsener",
"Bestel_Online_link": "",
"Footer_item": "t/m zaterdag 3 augustus",
"Item_1": "€6,50 korting (38%)",
"Item_2": "€1,46 per liter"
},//Some more data
]
}
Only price printed of dictionary:
€5,39
€5,94
€6,39
€6,39
€7,64
€16,19
Result
Please let me know if you want more information or code.
It's because items in Dictionary ordered randomly.
If you want to order it, you should use 'Set' another ordered collection type.
Or see the Apple Document below
Apple Docs : The order of key-value pairs in a dictionary is stable between mutations but is otherwise unpredictable. If you need an ordered collection of key-value pairs and don’t need the fast key lookup that Dictionary provides, see the KeyValuePairs type for an alternative.
I think you haven't been using wrongly data collection. You should use array to order. Because A dictionary stores associations between keys of the same type and values of the same type in a collection with no defined ordering. Unlike array, a dictionary do not have a specified order.
Click here! to read about data collection in swift

Populate UITableView with Sections

I'm trying to populate TableView with sections. Where an artist is the Section Name and all songs of that artist are listed below. In total over 100 artists. Like so
FirstArtist
- Song 1
- Song 2
SecondArtist
- Song 1
- Song 2
OneMoreArtist...
I have an array of objects Song
struct Song {
let songName: String?
let album: String?
let artist: String?
}
var songs = [Song]()
And an array of all artists names
var artists = [String]()
As I understand, to populate tableView with sections I need a Dictionary with Artist as key and array of Songs as value, like so
var toShow = [String : [Song]]()
So, then I'm trying to loop
for artist in artists {
for song in songs {
if song.artist == artist {
toShow[artist] = [song]
// toShow[artist]?.append(song)
}
}
}
But it doesn't work.
Probably I'm going wrong way.
What is the solution in this situation?
Thanks
Update
Made a stuct
struct ArtistWithSongs {
let name: String
let songs: [Song]
init(name: String, songs: [Song]) {
self.name = name
self.songs = songs
}
}
and trying to loop
var artistWithSongs = [ArtistWithSongs]()
for artist in artists {
for song in songs {
if artist == song.artist {
artistWithSongs.append(ArtistWithSongs(name: song, songs: [song]))
}
}
}
But apparently, my variant of looping is not correct. Now I'm getting an array of objects contains duplicate keys and only one Song per Key. It looks like
[Atrist1 : [Song1], Artist1 : [Song2], Artist1 : [Song3], ...]
My question is - What is the right way to make a loop, or is it possible somehow to merge objects with an identical key inside a Dictionary to get this [Artist1 :[Song1, Song2, Song3]]?
Thanks to everyone, I found a solution here, it worked for what I was trying to achieve. Roughly like this
let toShow = artistWithSongs.reduce([ArtistWithSongs](), { partialResult, artist in
var dupe = partialResult.filter {$0.songName == group.name }.first
if let dupeArtist = dupe {
dupeArtist.songs?.append(contentsOf: artist.songs ?? [])
return partialResult
} else {
var newPartialResult = partialResult
newPartialResult.append(group)
return newPartialResult
}
})
In addition to your Song struct
struct Song {
let songName: String?
let album: String?
}
create Artist one like
struct Artist {
let name:String
let songs = [Song]()
}
Then create an array of them
var artists = [Artist]()
let s1 = Song(songName:"name1",album:"alb1")
let s2 = Song(songName:"name2",album:"alb2")
let artist1 = Artist(name:"art",songs:[s1,s2])
artists.append(artist1)
In numberOfSections return
artists.count
In numberOfRows return
artists[section].songs.count
In cellForRowAt access
let song = artists[indexPath.section].songs[indexPath.row]
If you want to transform an array of songs ([Song]) to a dictionary ([String : [Song]]), I would recommend using functional style:
self.toShow = songs.reduce([String: [Song]]()) { acc, next in
// mutable copy
var acc = acc
// since artist is optional we need to substitute it with non-optional value if it's nil
let artist = next.artist ?? "Unknown Artist"
// if dictionary already has this artist, then append the `next` song to an array
if acc[artist] != nil {
acc[artist]?.append(next)
} else {
// otherwise create new array
acc[artist] = [next]
}
return acc
}
In your table's data source provide the number of sections:
func numberOfSections(in tableView: UITableView) -> Int {
return self.toShow.keys.count
}
And then you'll face a trouble: for number of rows in section you'll need to know which artist is the section stands for. So, I suggest you to use one more structure, an array of artists so that you keep the order of sections consistent:
self.artists = Array(self.toShow.keys).sorted()
This way, you can provide your table with following numbers of sections and rows:
func numberOfSections(in tableView: UITableView) -> Int {
return self.artists.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let artist = self.artists[section] // get the artist's name
return self.toShow[artist]?.count ?? 0
}

How to update value of an item in Class based on UISwitch action?

I have item class as shown below:
struct Cur: Decodable {
let id: String?
let name: String?
let symbol: String?
let rank: String?
let switchVal: Bool?
}
This class fills an array and array fills UITableView.
Here how I access UISwitch action:
#IBAction func switchBtn(_ sender: UISwitch) {
if sender.isOn {
// See if user is in Search Mode
if inSearchMode {
// if user in search mode get the Cur from filtered array
if let switchRank = filterCur.index(where: {$0.switchVal}) {
print("This is the rank: \(filterCur[switchRank].rank!)")
}
} else {
if let switchRank = Cur.index(where: {$0.switchVal}) {
print("This is where it is: \(Cur[switchRank])")
}
}
} else {
// Handle deleting previously selected Curs
}
}
In order for me to find which item in the array is Switched On/Off I have to have the array already updated (items switchVal = true?). But, to update the array I need to know which item's switch is triggered.
So, where is my mistake?
Thank you
One way to solve this problem is to give every UISwitch the index of Cur by assigning it to the tag attribute.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.switch.tag = indexPath.row
...
}
Since you now know that every switch has the index, you can get the item by accessing your array with the switch's tag.
#IBAction func switchBtn(_ sender: UISwitch) {
let index = sender.tag
let cur = Cur[index]
}

Array returning an empty array outside of PFQuery with Parse

I'm trying to populate the array postCaptions.
I need to return this array to numberOfRowsInSection for my table view, but it returns an empty array every time.
var postCaptions = [String]()
query2.whereKey("userid", equalTo: PFUser.current()?.objectId!)
query2.findObjectsInBackground(block: { (objects, error) in
if let userPosted = objects {
for object in userPosted {
if let userPost = object as? PFObject {
self.postImageFiles.append(userPost["userPostImage"] as! PFFile)
self.postLocation.append(userPost["userPostLocation"] as! String)
self.postCaptions.append(userPost["postCaption"] as! String)
print(self.postCaptions.count) //returns value of 2 for the two posts that I've made
self.tableView.reloadData()
self.refresher.endRefreshing()
}
}
}
})
print(self.postCaptions.count) //Returns empty array
I know that the issue is with the order of the threading and I understand that, but I'm not sure exactly what I can do to make the array remain populated outside of the query. I've seen this as a method for solving this problem, but I really haven't found a straightforward answer for how I can fix this problem in my code. If someone could provide an answer that works with my code that would be amazing!:) I've been stuck with this problem for more than a week now
var postCaptions = [String]() {
didSet {
// Do any execution that needs to wait for postCaptions here.
}
}
**cellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userPhotoFeedCell", for: indexPath) as! FeedCell
if PFUser.current()?.objectId != nil {
cell.userUsername.text = PFUser.current()?.username
}
//downloads images for user profile pic
imageFiles[indexPath.row].getDataInBackground { (data, error) in
if let imageData = data {
let downloadedImage = UIImage(data: imageData)
cell.userProfilePic.image = downloadedImage
}
}
//downloades images and items to populate user post
postImageFiles[indexPath.row].getDataInBackground { (data, error) in
if let userPostImageData = data {
let downloadedUserPostImage = UIImage(data: userPostImageData)
cell.userFeedPhoto.image = downloadedUserPostImage
}
}
cell.postLocation.text = postLocation[indexPath.row]
cell.postCaption.text = postCaptions[indexPath.row]
cell.userFeedPhoto.image = UIImage(named: "OrangeFuego")
cell.userProfilePic.image = UIImage(named: "usericon.png")
return cell
}
If I am understanding your issue it's rather simple. Set var postCaptions = [String]() as a global variable within the desired class. Then append your values from iterations to it the way you've done. Then the postCaptions values can be accessed in other parts of your code and you can use the count property.
Since the method findObjectsInBackground suggests that it is executed in the background, that means it has nothing to do with your UI, therefore your array will remain empty until this function is completed. At the moment you print that array, this function has not yet completed, so postCaptions will be empty for now.
And also that's why the numberOfRows will be zero.
I think what you need to do is to put this query into viewDidLoad or your designated initialiser, and call tableView.reloadData() as you did inside the completion block. Also, you might want to declare a Bool, to determine whether the table is loading or not, and in your completion block, set the isLoading to false after successfully executing the query.
query2.findObjectsInBackground(block: { (objects, error) in
if let userPosted = objects {
self.isLoading = false
for object in userPosted {
if let userPost = object as? PFObject {
self.postImageFiles.append(userPost["userPostImage"] as! PFFile)
self.postLocation.append(userPost["userPostLocation"] as! String)
self.postCaptions.append(userPost["postCaption"] as! String)
print(self.postCaptions.count) //returns value of 2 for the two posts that I've made
}
}
self.tableView.reloadData()
self.refresher.endRefreshing()
}
})
and inside your numberOfRowsInSection :
if isLoading { //you need to declare isLoading
return 1 //maybe a dummy cell to tell user the table is loading
} else {
return postCaptions.count
}

Creating JSON Array in Swift to update MySQL database

I hope this isn't a stupid question, I have been trawling the solutions and haven't been able to work this out..
In my main view controller I get data from a MySQL database using PHP and JSON. The user then selected relevant items from a table view by clicking on a custom button in a custom cell. I am at the point where I can add to/remove from the list based on which buttons are clicked/unclicked...
private var selectedItems = [Int: String]()
func cellButtonTapped(cell: CustomCell) {
let indexPath = self.tableView.indexPathForRowAtPoint(cell.center)!
let selectedItem = postsCollection[indexPath.row]
let item = selectedItem.product
let id = selectedItem.Id
let idItem : [Int:String] = [id:"\(item)"]
if selectedItems[id] == nil {
// now val is not nil and the Optional has been unwrapped, so use it
selectedItems[id] = "\(item)" //item
} else {
selectedItems[id] = nil
}
println("\(selectedItems)")
}
If I select items on the list it is displayed like this as I select the items. When I deselect them, the get removed from the list.
[6: Mybulen, 7: Ecotrin XL, 5: Aspen Simvastatin, 8: Lorien]
I would like to create a JSON Array and POST the array to a PHP file that I have the updates the database.. The resultant JSON Array should look something like this...
[{"Id":"6","repeatflag":"1"},{"Id":"7","repeatflag":"1"},{"Id":"5","repeatflag":"1"}]
So basically, any item I select gets added to the array with a "repeatflag" set to "1". My PHP file has a script that updates the "repeatflag" in the MySQL database. When the user is ready, they will click on a send button that will POST their selection in the the JSON Array. Hope that makes sense?
Thanks.
Well the code from you looks a little bit weird to me, maybe its because I don't know its context... But you can build an JSONArray you want like this:
class ViewController: UIViewController, UITableViewDelegate {
var jsonArrayWithItemsToSendToPHP: [[String: AnyObject]] = [[String: AnyObject]]()
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = postsCollection[indexPath.row]
var jsonObjectToPutToArray: [String: AnyObject] = [String: AnyObject]()
jsonObjectToPutToArray["id"] = selectedItem.Id
jsonObjectToPutToArray["name"] = selectedItem.product
jsonObjectToPutToArray["repeatflag"] = 1
jsonArrayWithItemsToSendToPHP.append(jsonObjectToPutToArray)
}
}
class ViewController: UIViewController, UITableViewDelegate {
var jsonArrayWithItemsToSendToPHP: [[String: AnyObject]] = [[String: AnyObject]]()
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = postsCollection[indexPath.row]
var jsonObjectToPutToArray: [String: AnyObject] = [String: AnyObject]()
jsonObjectToPutToArray["id"] = selectedItem.Id
jsonObjectToPutToArray["name"] = selectedItem.product
jsonObjectToPutToArray["repeatflag"] = 1
jsonArrayWithItemsToSendToPHP.append(jsonObjectToPutToArray)
}
}

Resources