Storing String and Int in a dictionary in swift - json

I'm almost new to Swift. In this URL I'll get some element; one of elements is categoryList which has two elements itself. I set the goodTypeName as the table's cell title, and when a cell is pressed it needs to send the goodType which is number (Int) to be placed in the next Url. I tried to create a dictionary but I failed!
UiTable code :::
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Global.GlobalVariable.names.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if Global.GlobalVariable.names == []
{
self.DisplayMessage(UserMessage: "nothing is available ")
print("server is nil")
}
let cell = UITableViewCell()
let content = Global.GlobalVariable.names[indexPath.row]
cell.textLabel?.text = content
cell.accessoryType = .disclosureIndicator
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.item)
let next = self.storyboard?.instantiateViewController(withIdentifier: "SVC")
self.navigationController?.pushViewController(next!, animated: true)
}
My problem is not with populating them in a table, my problem is when a cell is selected , goodtype is needed to be sent to next page, becuase next page's url has to have the goodtype code.

You can use the "prepareSegue" to pass Data.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "something"{
if let startViewController = segue.destination as? StartViewController{
startViewController.goodtype = Global.GlobalVariable.goodtype[indexPath.row]
}
}
}
And in your StartViewController just assign a variable to receive your data :
var goodtype = String()
Or use the navigation controller but with this line you can access to the another view controller property.
if let startViewController = storyboard.instantiateViewController(withIdentifier: "StartViewController") as? StartViewController {
startViewController.goodtype = Global.GlobalVariable.goodtype[indexPath.row]
let navigationController = UINavigationController()
navigationController.pushViewController(startViewController, animated: true)
}

Related

Nested struct for UITableView Swift/JSON

I need to display topic and description in a UITableView.
struct Topics: Decodable {
let subtopics: [subTopic]
struct subTopic: Decodable {
var topic = ""
var description = ""
}
}
The data for the TableView is fetched with a URL post request and assigned inside a do/try/catch block.
let Topics = Topics()
excerpt from URLRequest:
self.Topics = try JSONDecoder().decode(Topics.self, from: data)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "topicCell") as! topicCell
var subTopic = Topics?.subtopics[indexPath.row]
cell.topicLabel?.text = subTopic.topic
cell.descriptionTextView?.text = subTopic.description
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Topics?.subtopics.count
}
Sample JSON:
{"subtopics":[{"topic":"Computer Science", "description":"beginner course"},{"topic":"Data Analysis", "description":"beginner course"}]}
You give the the variable the same name as the struct.
let Topics = Topics()
Don't do that. It is confusing and can cause unexpected behavior.
There is a reason for the naming convention to name variables lowercase and structs/classes uppercase.
For less confusion name the top object different for example
struct Response: Decodable {
let subtopics: [SubTopic]
struct SubTopic: Decodable {
let topic, description : String
}
}
The default values (and variables) in SubTopic make no sense.
My next recommendation is to omit the top object and declare the data source array
var subTopics = [Response.SubTopic]()
and assign
let response = try JSONDecoder().decode(Response.self, from: data)
self.subTopics = response.subtopics
This will clean up the table view code and gets rid of the ugly optionals
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "topicCell") as! topicCell
let subTopic = subTopics[indexPath.row]
cell.topicLabel?.text = subTopic.topic
cell.descriptionTextView?.text = subTopic.description
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return subTopics.count
}

How to change clicked cell button label with JSON response in swift

In my tableview cell i added awerdedBtnLabel and awardedBtn.. if i tap button on any one cell then i need to change only tapped cell label text
code for tableview: with the below code if i click on any one cell awardedBtn then all cells awerdedBtnLabel text is changing, why?
how to change label text only on clicked cell, please do guide me
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewproposalData?.result?.bids?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewProposalTableVIewCell", for: indexPath) as! ViewProposalTableVIewCell
let bidData = viewproposalData?.result?.bids?[indexPath.row]
cell.propAmt.text = "$\(bidData?.amount ?? "")"
cell.awardedBtn.tag = indexPath.row
cell.awardedBtn.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
let postServData = viewproposalData?.result?.posted_services
if postServData?.is_awarded == "Y"{
cell.awerdedBtnLabel.text = "Rewoke Award"
cell.milestoneView.isHidden = false
}else{
cell.awerdedBtnLabel.text = "Award"
cell.milestoneView.isHidden = true
}
return cell
}
#objc func connected(sender: UIButton){
}
EDIT: according mentioned answer i have tried like below.. but nothing changing now.. i mean not all cells or not a single.. the button label text remains same.. if i tap on any button then no change in label
class ViewProposalTableVIewCell: UITableViewCell, UICollectionViewDelegate,UICollectionViewDataSource {
var onAwardBtn: ((_ isAwarded: String) -> Void)?
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewproposalData?.result?.bids?.count ?? 0//5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewProposalTableVIewCell", for: indexPath) as! ViewProposalTableVIewCell
let postServData = viewproposalData?.result?.posted_services
let isAwarded = postServData?.is_awarded
cell.onAwardBtn = { [weak self] (isAwarded) in
if isAwarded == postServData?.is_awarded {
cell.awerdedBtnLabel.text = "Rewoke Award"
cell.milestoneView.isHidden = false
}
else{
cell.awerdedBtnLabel.text = "Award"
cell.milestoneView.isHidden = true
}
}
2nd Edit:
JSON response from console:
JSON {
jsonrpc = "2.0";
result = {
bids = (
{
amount = 100;
id = 153;
"get_bid_user" = {
address = dfsdffafadf;
};
},
);
"posted_services" = {
"is_awarded" = Y;
};
};
}

Unable to Expand and Collapse TableViewCell in Swift

I have taken two prototype cells called CategoryTableCell and SubCategoryTableCell in tableview
Initially, I need to show all CategoryTableCell data in TableView. Here if I click CategoryTableCell then I need to show that Category's subcategories in SubCategoryTableCell. How to do that?
I need to hide all subcategories. If I click any one category from CategoryTableCell then I need to its subcategories.
Code:
extension CategoryVC: UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return self.activeCategories?.count ?? 0 : 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.activeCategories?[section].sub_categories?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SubCategoryTableCell", for: indexPath) as! SubCategoryTableCell
cell.selectionStyle = .none
let activesubCat = self.activeCategories?[indexPath.section].sub_categories
let indexData = activesubCat?[indexPath.row]
cell.lblTitle?.text = langType == .en ? indexData?.details?.first?.title : indexData?.details?[1].title
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = tableView.dequeueReusableCell(withIdentifier: "CategoryTableCell") as! CategoryTableCell
let indexData = self.activeCategories?[section]
view.btnDetails?.tag = section
view.lblTitle?.text = langType == .en ? indexData?.details?.first?.title : indexData?.details?[1].title
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return isFromFilter ? 40 : 0
}
With the code, I am getting all categories in red colour and subcategories in dark grey colour text. But, here cell is not expanding and collapse. I need to show all red colour text initially. Here if I tap on the cell then it has to expand and show all grey colour text. How to do that? Please do help with the code.
o/p: coming like this with above code
[![screenshot][1]][1]
EDIT
JSON Model:
public class Category_json {
public var id : Int?
public var status : String?
public var sub_categories : Array<Sub_categories>?
var isExpanded = true
required public init?(dictionary: NSDictionary) {
id = dictionary["id"] as? Int
status = dictionary["status"] as? String
if (dictionary["sub_categories"] != nil) { sub_categories = Sub_categories.modelsFromDictionaryArray(array: dictionary["sub_categories"] as? NSArray ?? NSArray()) }
if (dictionary["details"] != nil) { details = Details.modelsFromDictionaryArray(array: dictionary["details"] as? NSArray ?? NSArray()) }
}
public func dictionaryRepresentation() -> NSDictionary {
let dictionary = NSMutableDictionary()
dictionary.setValue(self.id, forKey: "id")
dictionary.setValue(self.status, forKey: "status")
return dictionary
}
}
and public var activeCategories : Array<Category_json>?
EDIT2:
#objc func expandMe() {
print("expand me")
}
Add a property to your activeCategories array 's model
var isExpanded = true
Then inside numberOfRowsInSection
guard let item = self.activeCategories?[section] else { return 0 }
return item.isExpanded ? (item.sub_categories?.count ?? 0) : 0
Then play with isExpanded and refresh the table
Edit
Inside viewForHeaderInSection
let header = tableView.dequeueReusableCell(withIdentifier: "CategoryTableCell") as! CategoryTableCell
button.tag = section
Then inside action
#objc headerClicked(_ sender:UIButton) {
let section = sender.tag
guard let item = self.activeCategories?[section] else { return 0 }
item.isExpanded.toggle()
tableView.reloadData()
}

I can't print to TableView data with Swift JSON

I'm new to using JSON and wanted to start with a simple app to provide an overview of the movie. The following code does not print anything on the tableView, the app remains empty, with no results. He makes no mistakes. In the debug area, however, the data prints them to me. How can I get the results on the tableView?
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var popularMoviesArray = [Results]()
var swiftManager = SwiftManager()
var tableViewCell = TableViewCell()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var labelError: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
swiftManager.delegate = self
swiftManager.fetchUrl()
self.tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return popularMoviesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
let item = popularMoviesArray[indexPath.row]
cell.labelTitle.text = item.title
cell.labelYear.text = item.release_date
cell.labelRate.text = String(item.vote_average ?? 0.0)
cell.labelOreview.text = item.overview
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "goToDetail", sender: indexPath.row)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier != nil else {
return
}
let letRow = sender as? Int
switch segue.identifier {
case "goToDetail":
(segue.destination as! ViewControllerDetail).itemDetail = popularMoviesArray[letRow!]
default:
return
}
}
}
extension ViewController: SwiftManagerDelegate {
func didUpdateStruct(_ swiftManager: SwiftManager, swiftData: SwiftData) {
DispatchQueue.main.async {
self.popularMoviesArray = swiftData.results
print("PRINT ARRAY - \(self.popularMoviesArray)")
}
}
func didFailWithError(error: Error) {
print(error)
}
}
You have to reload the table view in the delegate method because the data is loaded asynchronously
func didUpdateStruct(_ swiftManager: SwiftManager, swiftData: SwiftData) {
DispatchQueue.main.async {
self.popularMoviesArray = swiftData.results
self.tableView.reloadData()
print("PRINT ARRAY - \(self.popularMoviesArray)")
}
}
Reloading the table view in viewDidLoad is pointless.

Trying to reference each item of custom type for tableView cells

I am parsing data from a JSON file and I've created a struct to hold that data. I am trying to display each item of the custom struct in a tableView, but I'm getting stuck on how I should reference each item.
Here's my struct:
struct Country: Codable {
var id: Int
var country: String
var capital: String
var nationalLanguage: [String]
var population: Int
}
And here is my table view controller. Right now I only know how to reference a single item in my custom type. This obviously sets all of the cells to that one item.
class TableViewController: UITableViewController {
var countryItem: Country?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cells", for: indexPath)
if let country = countryItem {
cell.textLabel!.text = String(country.population)
}
return cell
}
}
When I print out my countryItem variable, this is what I get:
Country(id: 1, country: "United States", capital: "Washington D.C.", nationalLanguage: ["English"], population: 328239523)
Do I need to somehow set that as an array so I can refer to each item individually?
UPDATED:
Option1
class TableViewController: UITableViewController {
var countryItem: Country?
var arrayStrings: [String] {
guard let countryItem = countryItem else { return [] }
return [
"\(countryItem.id)",
countryItem.country,
countryItem.capital,
countryItem.nationalLanguage.joined(separator: ", "),
"\(countryItem.population)",
]
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayStrings.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cells", for: indexPath)
cell.textLabel!.text = arrayStrings[indexPath.row]
return cell
}
}
Option2
class TableViewController: UITableViewController {
var countryItem: Country?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return countryItem == nil ? 0 : 5
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cells", for: indexPath)
let text: String
switch indexPath.row {
case 0:
text = "\(countryItem!.id)"
case 1:
text = countryItem!.country
case 2:
text = countryItem!.capital
case 3:
text = countryItem!.nationalLanguage.joined(separator: ", ")
case 4:
text = "\(countryItem!.population)"
default:
break
}
cell.textLabel!.text = text
return cell
}
}
If you all you want is just display string representation of all fields in your struct, then yes, convert them into a single array of string and use reusable cells to render.
If you need different styles for each field, then you may not need an array, just create some custom cells and then assign data for them from the struct.
Or maybe you don't even need a table view here since I see you have only one Country and no reusable needed here. Just create a custom view with 5 (maybe) labels and display your data.
For some reason, you still need a table view, then create a single cell that can display all information for your country.
Hope it can help you.
class TableViewController: UITableViewController {
var countryItem = [Country]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return countryItem.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cells", for: indexPath)
cell.textLabel!.text = countryItem[indexPath.row].country
return cell
}
}