问题
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))"
}
}
回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/56142037/how-to-group-tableview-cells-based-on-field-in-json-array