I followed this Tutorial
And it works fine. Now if I try to take the sections data from a JSON this also works fine, but I have problems displaying everything.
This is what i did:
// TestVC.swift
// WinterdienstTest
//
// Created by zwipf on 09.02.17.
// Copyright © 2017 Squirrel. All rights reserved.
//
import UIKit
class TestVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
let sections: [String] = ["Tour 1", "Tour 2", "Tour 3"]
var adressTour1: [String] = []
var adressTour2: [String] = []
var adressTour3: [String] = []
var adressTourALL: [Int: [String]] = [:]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// URL abrufen
let url = NSURL(string: "https://api.myjson.com/bins/mml65")
let request = NSMutableURLRequest(url: url! as URL)
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
// JSON parsen und Ergebins in eine Liste von assoziativen Arrays wandeln
let responseString = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
// Daten auslesen
if let Liegenschaften = responseString["Liegenschaften"] as? [AnyObject] {
for liegenschaft in Liegenschaften {
if let Winterdienst = liegenschaft["Winterdienst"] as? [AnyObject] {
for winterdienst in Winterdienst {
let tour = winterdienst["Tour"] as! String
if tour == "1" {
let adresse = liegenschaft["Adresse"] as! String
self.adressTour1.append(adresse)
}
if tour == "2" {
let adresse = liegenschaft["Adresse"] as! String
self.adressTour2.append(adresse)
}
if tour == "3" {
let adresse = liegenschaft["Adresse"] as! String
self.adressTour3.append(adresse)
}
}
}
}
}
self.adressTourALL = [0:self.adressTour1, 1:self.adressTour2, 2:self.adressTour3]
}
// UI-Darstellung aktualisieren
//OperationQueue.main.addOperation {
// self.tableView.reloadData()
//}
// task.resume()
print(self.adressTourALL)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return adressTourALL.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell");
}
print(adressTourALL[indexPath.section]![indexPath.row])
cell!.textLabel!.text = adressTourALL[indexPath.section]![indexPath.row]
return cell!
}
}
Hope somebody can help me!
Among the others, you need to put this inside your code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section{
case 0:
return self.adressTour1.count
case 1:
return self.adressTour2.count
case 2:
return self.adressTour2.count
default:
return 1
}
Related
Hello good afternoon to everyone.
I hope you are doing very well despite the situation we are going through.
I tell you my problem.
I am trying to save in an array, a series of data that is selected from a tableView through a JSON.
That is, the tableView shows some data available to select, the ones that are selected I want to save them in an array but when I select a data in the tableView my app crashes and shows me a breakpoint "Thread 1: signal SIGABRT"
And in the console I get this:
Could not cast value of type 'MallConcierge.DetallesIntereses' (0x10ca9be10) to 'NSString' (0x7fff86d8bbb0).
I hope you can help me, I attach the classes from where I download the data, the details and the class where I connect the tableView.
InteresesModelo.swift (in this class is where I download the data in JSON)
import UIKit
protocol InteresesModeloProtocol: class{
func interesesDownload (interest: NSArray)
}
class InteresesModelo: NSObject {
weak var delegate: InteresesModeloProtocol!
let urlPath = "http://localhost:8888/mallconcierge/API-movil/interests.php"
func interestDownload(){
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.ephemeral)
URLCache.shared.removeAllCachedResponses()
let task = defaultSession.dataTask(with: url){
(data, response, error) in
if error != nil{
print("Error al descargar datos")
}else{
print("Datos descargados")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data){
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! NSArray
}catch let error as NSError{
print(error)
}
var jsonElement = NSDictionary()
let detalles = NSMutableArray()
for i in 0 ..< jsonResult.count{
jsonElement = jsonResult[i] as! NSDictionary
let detalle = DetallesIntereses()
let idInteres = jsonElement["idInteres"]
let nombreInteres = jsonElement["interesNombre"]
detalle.idInteres = idInteres as? String
detalle.nombreInteres = nombreInteres as? String
detalles.add(detalle)
}
DispatchQueue.main.async(execute: { ()-> Void in
self.delegate.interesesDownload(interest: detalles)
})
}
}
DetallesIntereses.swift
import UIKit
class DetallesIntereses: NSObject {
var idInteres: String?
var nombreInteres: String?
override init() {
}
init(idInteres: String, nombreInteres:String) {
self.idInteres = idInteres
self.nombreInteres = nombreInteres
}
override var description: String{
return "idInteres: \(idInteres), nombreInteres: \(nombreInteres)"
}
}
InteresesViewController.swift
import UIKit
class InteresesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, InteresesModeloProtocol {
var selectIntereses = [String]()
var feedInterests: NSArray = NSArray()
// var selectInterests: DetallesIntereses = DetallesIntereses()
var items=[String]()
#IBOutlet var listaInteresesTableView: UITableView!
func interesesDownload(interest: NSArray) {
feedInterests = interest
self.listaInteresesTableView.reloadData()
}
override func viewDidLoad() {
self.listaInteresesTableView.isEditing = true
self.listaInteresesTableView.allowsMultipleSelectionDuringEditing = true
self.listaInteresesTableView.delegate = self
self.listaInteresesTableView.dataSource = self
let interesesModelo = InteresesModelo()
interesesModelo.delegate = self
interesesModelo.interestDownload()
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return feedInterests.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "celInterests", for: indexPath) as! InteresesTableViewCell
let interest: DetallesIntereses = feedInterests[indexPath.row] as! DetallesIntereses
cell.lblNombreIntereses!.text = interest.nombreInteres
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectDeselectCell(tableView: listaInteresesTableView, indexPath: indexPath)
print("Seleccionado")
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.selectDeselectCell(tableView: listaInteresesTableView, indexPath: indexPath)
print("Deseleccionado")
}
func selectDeselectCell(tableView: UITableView, indexPath: IndexPath){
self.selectIntereses.removeAll()
if let arr = listaInteresesTableView.indexPathsForSelectedRows{
for index in arr{
selectIntereses.append(feedInterests[indexPath.row] as! String)
}
}
print(selectIntereses)
}
#IBAction func seleccionarIntereses(_ sender: Any){
print(selectIntereses)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
The JSON
[
{
"idInteres": "1",
"interesNombre": "Moda Mujer"
},
{
"idInteres": "3",
"interesNombre": "Moda Hombre"
},
{
"idInteres": "4",
"interesNombre": "Belleza"
},
{
"idInteres": "5",
"interesNombre": "Relojes y Joyería"
},
{
"idInteres": "6",
"interesNombre": "Hogar/Interiorismo"
},
{
"idInteres": "7",
"interesNombre": "Gastronomía"
},
{
"idInteres": "8",
"interesNombre": "Entretenimiento"
},
{
"idInteres": "9",
"interesNombre": "Wellness"
}
]
I hope you can help me, please.
I thank you all
There's lots of little stuff going on here. I've tried to highlight as I make edits with //<-- Here.
By far the biggest issue is using the untyped Objective-C NSArray instead of using a typed Swift array. By switching to that, you can get useful type errors.
I had to make a couple of assumptions about what you wanted to do, but this should get you started.
I've left the original JSON decoding for the most part, but I'd strongly suggest you look into using Codable instead of JSONSerialization unless you have a compelling reason not to.
protocol InteresesModeloProtocol : AnyObject {
func interesesDownload (interest: [DetallesIntereses]) //<-- Here
}
class InteresesModelo: NSObject {
weak var delegate: InteresesModeloProtocol!
let urlPath = "http://localhost:8888/mallconcierge/API-movil/interests.php"
func interestDownload(){
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.ephemeral)
URLCache.shared.removeAllCachedResponses()
let task = defaultSession.dataTask(with: url){
(data, response, error) in
if error != nil{
print("Error al descargar datos")
}else{
print("Datos descargados")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data){
var jsonResult = [[String:String]]()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [[String : String]]
}catch let error as NSError{
print(error)
}
let detalles = jsonResult.map { jsonElement -> DetallesIntereses in
let detalle = DetallesIntereses(idInteres: jsonElement["idInteres"], nombreInteres: jsonElement["interesNombre"])
return detalle
}
DispatchQueue.main.async {
self.delegate.interesesDownload(interest: detalles)
}
}
}
struct DetallesIntereses { //<-- Here -- no reason for this to be an `NSObject`
var idInteres: String?
var nombreInteres: String?
}
class InteresesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, InteresesModeloProtocol {
var selectIntereses = [DetallesIntereses]() //<-- Here
var feedInterests: [DetallesIntereses] = [] //<-- Here
var items = [String]()
#IBOutlet var listaInteresesTableView: UITableView!
func interesesDownload(interest: [DetallesIntereses]) { //<-- Here
feedInterests = interest //<-- Here
self.listaInteresesTableView.reloadData()
}
override func viewDidLoad() {
self.listaInteresesTableView.isEditing = true
self.listaInteresesTableView.allowsMultipleSelectionDuringEditing = true
self.listaInteresesTableView.delegate = self
self.listaInteresesTableView.dataSource = self
let interesesModelo = InteresesModelo()
interesesModelo.delegate = self
interesesModelo.interestDownload()
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return feedInterests.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "celInterests", for: indexPath) as! InteresesTableViewCell
let interest: DetallesIntereses = feedInterests[indexPath.row] //<-- Here
cell.lblNombreIntereses!.text = interest.nombreInteres
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectDeselectCell(tableView: listaInteresesTableView, indexPath: indexPath)
print("Seleccionado")
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.selectDeselectCell(tableView: listaInteresesTableView, indexPath: indexPath)
print("Deseleccionado")
}
func selectDeselectCell(tableView: UITableView, indexPath: IndexPath){
self.selectIntereses.removeAll()
selectIntereses.append(feedInterests[indexPath.row]) //<-- Here
print(selectIntereses)
}
#IBAction func seleccionarIntereses(_ sender: Any){
print(selectIntereses)
}
}
Essentially I have am using JSON data to create an array and form a tableview.
I would like the table cells to be grouped by one of the fields from the JSON array.
This is what the JSON data looks like:
[{"customer":"Customer1","number":"122039120},{"customer":"Customer2","number":"213121423"}]
Each number needs to be grouped by each customer.
How can this be done?
This is how I've implemented the JSON data using the table:
CustomerViewController.swift
import UIKit
class CustomerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, FeedCustomerProtocol {
var feedItems: NSArray = NSArray()
var selectedStock : StockCustomer = StockCustomer()
let tableView = UITableView()
#IBOutlet weak var customerItemsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//set delegates and initialize FeedModel
self.tableView.allowsMultipleSelection = true
self.tableView.allowsMultipleSelectionDuringEditing = true
self.customerItemsTableView.delegate = self
self.customerItemsTableView.dataSource = self
let feedCustomer = FeedCustomer()
feedCustomer.delegate = self
feedCustomer.downloadItems()
}
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.customerItemsTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
print("item feed loaded")
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cell = tableView.dequeueReusableCell(withIdentifier: "customerGoods", for: indexPath) as? CheckableTableViewCell
let cellIdentifier: String = "customerGoods"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
// Get the stock to be shown
let item: StockCustomer = feedItems[indexPath.row] as! StockCustomer
// Configure our cell title made up of name and price
let titleStr = [item.number].compactMap { $0 }.joined(separator: " - ")
return myCell
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
let cellIdentifier: String = "customerGoods"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
myCell.textLabel?.textAlignment = .left
}
}
FeedCustomer.swift:
import Foundation
protocol FeedCustomerProtocol: class {
func itemsDownloaded(items: NSArray)
}
class FeedCustomer: NSObject, URLSessionDataDelegate {
weak var delegate: FeedCustomerProtocol!
let urlPath = "https://www.example.com/example/test.php"
func downloadItems() {
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error")
}else {
print("stocks downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let stocks = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let stock = StockCustomer()
//the following insures none of the JsonElement values are nil through optional binding
if let number = jsonElement[“number”] as? String,
let customer = jsonElement["customer"] as? String,
{
stock.customer = customer
stock.number = number
}
stocks.add(stock)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: stocks)
})
}
}
StockCustomer.swift:
import UIKit
class StockCustomer: NSObject {
//properties of a stock
var customer: String?
var number: String?
//empty constructor
override init()
{
}
//construct with #name and #price parameters
init(customer: String) {
self.customer = customer
}
override var description: String {
return "Number: \(String(describing: number)), customer: \(String(describing: customer))"
}
}
You can achieve this by making an array of array. So something like this
[[{"customer": "customer1", "number": "123"}, {"customer": "customer1", "number": "456"}], [{"customer": "customer2", "number": "678"}, {"customer": "customer2", "number": "890"}]]
This is not the only data structure you can use to group. Another possibility is:
{"customer1": [{"customer": "customer1", "number": "123"}, {"customer": "customer1", "number": "456"}], "customer2": [{"customer": "customer2", "number": "678"}, {"customer": "customer2", "number": "890"}]}
Then you can use UITableView sections to group by customers. Section count would be the number of inside arrays and each section would contain as many rows as there are numbers in that inside array.
You can group a sequence based on a particular key using one of the Dictionary initializer,
init(grouping:by:)
The above method init will group the given sequence based on the key you'll provide in its closure.
Also, for parsing such kind of JSON, you can easily use Codable instead of manually doing all the work.
So, for that first make StockCustomer conform to Codable protocol.
class StockCustomer: Codable {
var customer: String?
var number: String?
}
Next you can parse the array like:
func parseJSON(data: Data) {
do {
let items = try JSONDecoder().decode([StockCustomer].self, from: data)
//Grouping the data based on customer
let groupedDict = Dictionary(grouping: items) { $0.customer } //groupedDict is of type - [String? : [StockCustomer]]
self.feedItems = Array(groupedDict.values)
} catch {
print(error.localizedDescription)
}
}
Read about init(grouping:by:) in detail here: https://developer.apple.com/documentation/swift/dictionary/3127163-init
Make the feedItems object in CustomerViewController of type [[StockCustomer]]
Now, you can implement UITableViewDataSource methods as:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customerGoods", for: indexPath) as! CheckableTableViewCell
let items = self.feedItems[indexPath.row]
cell.textLabel?.text = items.compactMap({$0.number}).joined(separator: " - ")
//Configure the cell as per your requirement
return cell
}
Try implementing the approach with all the bits and pieces and let me know in case you face any issues.
My entire table view is being written programmatically and the data is coming from JSON. I am trying to group the cells by the customer the code seems to be correct but no sections are showing up at all.
Here is the code:
Portfolios.swift
import UIKit
struct Portfolios: Codable {
let customer, serial, rma, model: String
let manufacturer: String
}
PortfolioController.swift
import UIKit
class PortfolioController: UITableViewController {
var portfolios = [Portfolios]()
var portfoliosDic = [String:[Portfolios]]()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
view.backgroundColor = UIColor.blue
navigationItem.title = "Customer"
fetchJSON()
}
func fetchJSON(){
let urlString = "https://www.example.com/example/example.php"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, error) in
DispatchQueue.main.async {
if let error = error {
print("Failed to fetch data from url", error)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try JSONDecoder().decode([Portfolios].self, from: data)
self.portfoliosDic = Dictionary(grouping: res, by: { $0.customer})
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.portfolios = try decoder.decode([Portfolios].self, from: data)
self.tableView.reloadData()
} catch let jsonError {
print("Failed to decode json", jsonError)
}
}
}.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return portfoliosDic.keys.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let keys = Array(portfoliosDic.keys)
let item = portfoliosDic[keys[section]]!
return item.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let keys = Array(portfoliosDic.keys)
let arr = portfoliosDic[keys[indexPath.section]]!
let customer = arr[indexPath.row]
let titleStr = [customer.serial, customer.manufacturer, customer.model].compactMap { $0 }.joined(separator: " - ")
//cell.textLabel?.text = titleStr
print(titleStr)
// Get references to labels of cell
cell.textLabel!.text = customer.serial
return cell
}
}
UPDATE:
Because it is a UIViewController Xcode told me to remove the override func
and I added #IBOutlet weak var tableView: UITableView!
(The end results is an empty table for some reason)
Using a UITableViewController instead:
import UIKit
class CustomerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var sections = [Section]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellId")
navigationController?.navigationBar.prefersLargeTitles = true
fetchJSON()
}
func fetchJSON(){
let urlString = "https://www.example.com/example/example.php"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, error) in
DispatchQueue.main.async {
if let error = error {
print("Failed to fetch data from url", error)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode([Portfolios].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.customer })
let keys = grouped.keys.sorted()
self.sections = keys.map({Section(name: $0, items: grouped[$0]!)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Failed to decode json", error)
}
}
}.resume()
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = sections[section]
return section.items.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].name
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath)
let section = sections[indexPath.section]
let item = section.items[indexPath.row]
let titleStr = "\(item.serial) - \(item.manufacturer) - \(item.model)"
cell.textLabel!.text = titleStr
return cell
}
}
First of all why do you decode the JSON twice?
No sections are displayed because the method titleForHeaderInSection is not implemented.
The code is not reliable anyway because the order of the sections is not guaranteed. I recommend to create another struct for the sections.
struct Section {
let name : String
let items : [Portfolios]
}
struct Portfolios: Decodable {
let customer, serial, rma, model: String
let manufacturer: String
}
Delete portfolios and portfoliosDic and declare the data source array
var sections = [Section]()
Group the JSON, sort the keys and map the dictionaries to Section instances
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode([Portfolios].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.customer })
let keys = grouped.keys.sorted()
self.sections = keys.map({Section(name: $0, items: grouped[$0]!)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Failed to decode json", error)
}
The table view datasource and delegate methods are
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = sections[section]
return section.items.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].name
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath)
let section = sections[indexPath.section]
let item = section.items[indexPath.row]
let titleStr = "\(item.serial) - \(item.manufacturer) - \(item.model)"
cell.textLabel!.text = titleStr
return cell
}
Note:
Always dequeue cells in cellForRowAt
I am showing data like expendable tableview with a header section. Header is showing fine. but When I am showing rows . (Like in a product 5 addons then data is showing like in expendable Tableview Every Row with a Same Type Name Header). I am attaching screenshot. What type of data is showing.
Image Data Showing Like : https://imgur.com/a/JXqUbJD
I am attaching I current code which I am using . I already tried too many times but it is not working fine.
import UIKit
import Reachability
import Alamofire
class ExpendableTableview: UIViewController, UITableViewDelegate, UITableViewDataSource, CollapsibleTableViewHeaderDelegate {
#IBOutlet weak var tableview: UITableView!
var catid:Int!
var reachability = Reachability()!
var arraySubCategory = [structSubCategory]()
struct structSubCategory {
var id:Int
var minimum_people:String
var title:String
var package_price:String
var package_image:String
var package_label:String
var categoryId:Int
// var addons: [cateringAddOns]
// var collapsed: Bool
}
var arraySection = [section]()
struct section
{
var title: String
var addons : [cateringAddOns]
var collapsed: Bool
}
var arrayCateringAddOns = [cateringAddOns]()
struct cateringAddOns {
var no_of_items:Int
var add_on_type:Int
var package_item:String
var upgrade_price:String
}
var NormalArr = ["Balance", "Sales", "Order Status", "Social Media Complaints", "Logout"]
override func viewDidLoad() {
super.viewDidLoad()
print(catid)
SubCateringAPI(cateringId: catid)
// Do any additional setup after loading the view.
}
func numberOfSections(in tableView: UITableView) -> Int {
return arraySection.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arraySection[section].collapsed ? 0 : arraySection[section].addons.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellRID") as! ExpendableAddOnsCell
let item : cateringAddOns = arraySection[indexPath.section].addons[indexPath.row]
cell.lblAddon.text = item.package_item
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header") as? CollapsibleTableViewHeader ?? CollapsibleTableViewHeader(reuseIdentifier: "header")
header.titleLabel.text = arraySection[section].title
header.arrowLabel.text = ">"
header.setCollapsed(arraySection[section].collapsed)
header.section = section
header.delegate = self
return header
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44.0
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1.0
}
func toggleSection(_ header: CollapsibleTableViewHeader, section: Int) {
let collapsed = !arraySection[section].collapsed
// Toggle collapse
arraySection[section].collapsed = collapsed
header.setCollapsed(collapsed)
tableview.reloadSections(NSIndexSet(index: section) as IndexSet, with: .automatic)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
// func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// return arraySection[section].title
// }
//
#IBAction func btnDismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
func SubCateringAPI(cateringId:Int)
{
if (reachability.connection == .wifi) || (reachability.connection == .cellular)
{
SwiftLoader.show(animated: true)
let url = BaseUrl + ViewController.sharedInstance.subCatering + "cateringId=\(cateringId)"
print(url)
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default).responseJSON { response in
switch response.result {
case .success:
let json = response.result.value
print(json)
let code = (json as AnyObject).object(forKey: "code") as! Int
print(code)
if code == 200
{
let data = (json as AnyObject).object(forKey: "data") as? NSArray
for alldata in data!
{
let id = (alldata as AnyObject).object(forKey: "id") as! Int
let minimum_people = (alldata as AnyObject).object(forKey: "minimum_people") as! String
let title = (alldata as AnyObject).object(forKey: "title") as! String
let package_price = (alldata as AnyObject).object(forKey: "package_price") as! String
let package_image = (alldata as AnyObject).object(forKey: "package_image") as! String
let package_label = (alldata as AnyObject).object(forKey: "package_label") as! String
let categoryId = (alldata as AnyObject).object(forKey: "categoryId") as! Int
let arr = structSubCategory(id: id, minimum_people: minimum_people, title: title, package_price: package_price, package_image: package_image, package_label: package_label, categoryId: categoryId)
self.arraySubCategory.append(arr)
}
for i in self.arraySubCategory
{
let id = i.categoryId
let title = i.package_label
self.cateringAddOndsAPI(adonsId: id, title: title)
}
SwiftLoader.hide()
}
else
{
}
case .failure:
print("error")
}
}
}
else
{
alert(title: "", message: "Please Check Your Internet Connection")
}
}
func cateringAddOndsAPI(adonsId:Int, title: String)
{
if (reachability.connection == .wifi) || (reachability.connection == .cellular)
{
arraySection.removeAll()
arrayCateringAddOns.removeAll()
SwiftLoader.show(animated: true)
let url = BaseUrl + ViewController.sharedInstance.cateringAddOnds + "adonsId=\(adonsId)"
print(url)
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default).responseJSON { response in
switch response.result {
case .success:
let json = response.result.value
print(json)
let code = (json as AnyObject).object(forKey: "code") as! Int
print(code)
if code == 200
{
let data = (json as AnyObject).object(forKey: "data") as? NSArray
for alldata in data!
{
let no_of_items = (alldata as AnyObject).object(forKey: "no_of_items") as! Int
let add_on_type = (alldata as AnyObject).object(forKey: "add_on_type") as! Int
let package_item = (alldata as AnyObject).object(forKey: "package_item") as! String
let upgrade_price = (alldata as AnyObject).object(forKey: "upgrade_price") as! String
let arr = cateringAddOns(no_of_items: no_of_items, add_on_type: add_on_type, package_item: package_item, upgrade_price: upgrade_price)
self.arrayCateringAddOns.append(arr)
let expandData = section(title: title, addons: [arr], collapsed: false)
self.arraySection.append(expandData)
}
self.tableview.reloadData()
SwiftLoader.hide()
}
else
{
}
case .failure:
print("error")
}
}
}
else
{
alert(title: "", message: "Please Check Your Internet Connection")
}
}
}
you should call override func prepareForReuse() inside the class CollapsibleTableViewHeader.
override func prepareForReuse() {
super.prepareForReuse()
//TODO: set default values
}
Could you provide code of CollapsibleTableViewHeader class?
I have a problem when accessing the variable from another class.
I am initializing and modifying variable here
TheResponse Class
import Foundation
import Alamofire
class TheResponse {
typealias JSONStd = [[String : AnyObject]]
var jsonAnswer = [[String:String]]()
var getRes: [[String : String]] {
return jsonAnswer
}
func callAmo(){
let urladdress = "https://api.github.com/users"
Alamofire.request(urladdress).responseJSON(completionHandler: {
response in
//print(response)
self.parseData(JSONData: response.data!)
})
}
func parseData(JSONData : Data){
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStd
let counter = readableJSON.count
//Filling the dictionary
for item in 0...counter - 1 {
let login = readableJSON[item]["login"]
let avatar_url = readableJSON[item]["avatar_url"]
let html_url = readableJSON[item]["html_url"]
let id = String(describing: readableJSON[item]["id"])
var dict:[String:AnyObject] = ["login": login!,
"avatar": avatar_url!,
"profile": html_url!,
"id": id as AnyObject]
jsonAnswer.append(dict as! [String : String])
}
}
catch{
print(error)
}
}
}
If I will print somewhere here the jsonAnswer variable, everything looks fine.
But I want to use this variable in my second class
import UIKit
import Alamofire
class TableViewController: UITableViewController {
var resObj = TheResponse()
var jsonData = [[String:String]]()
override func viewDidLoad() {
super.viewDidLoad()
resObj.callAmo{ (jsonAns) -> () in
self.jsonData = jsonAns
// print(self.jsonData)
}
print(self.jsonData)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
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: "MyCell", for: indexPath) as! MainViewCell
return cell
}
}
The problem is that when I want to print the variable in TableViewController class, the variable is empty. As print out I am getting only [].
How should I access the variable to have it as it should be?
The problem is you haven't call callAmo method also, you need to create completion closure with your method, so change your code like this.
func callAmo(completion: ([[String : String]]) -> ()) {
let urladdress = "https://api.github.com/users"
Alamofire.request(urladdress).responseJSON(completionHandler: {
response in
//print(response)
let dic = self.parseData(JSONData: response.data!)
completion(dic)
})
}
Now return the dictionary from parseData methods like this.
func parseData(JSONData : Data) -> [[String: String]]{
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStd
let counter = readableJSON.count
//Filling the dictionary
for item in 0...counter - 1 {
let login = readableJSON[item]["login"]
let avatar_url = readableJSON[item]["avatar_url"]
let html_url = readableJSON[item]["html_url"]
let id = String(describing: readableJSON[item]["id"])
var dict:[String:AnyObject] = ["login": login!,
"avatar": avatar_url!,
"profile": html_url!,
"id": id as AnyObject]
jsonAnswer.append(dict as! [String : String])
}
}
catch{
print(error)
}
return jsonAnswer
}
Now call this method in viewDidLoad of TableViewController like this.
resObj.callAmo{ (jsonAns) -> () in
print(jsonAns)
}
Edit: To get the data in TableViewController do like this
var jsonData = [[Strin:String]]()
override func viewDidLoad() {
super.viewDidLoad()
resObj.callAmo{ (jsonAns) -> () in
self.jsonData = jsonAns
self.tableView.reloadData()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.jsonData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MainViewCell
return cell
}