I had my tableView populating as it should for a little while but then made some tweaks to how I get my data from Parse and since then I can't get my tableView to display without crashing with fatal error: Array index out of range
Here is my code:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var vaccineEntry: Array<Array<Dictionary<String,String>>> = [[
["name" : "Rabies 1-yr", "detail": "None"],
["name" : "Rabies 3-yr", "detail": "None"],
["name" : "Distemper", "detail": "None"],
["name" : "Parvovirus", "detail": "None"],
["name" : "Adenovirus", "detail": "None"]],
[
["name" : "Parainfluenza", "detail": "None"],
["name" : "Bordetella", "detail": "None"],
["name" : "Lyme Disease", "detail": "None"],
["name" : "Leptospirosis", "detail": "None"],
["name" : "Canine Influenza", "detail": "None"]
]]
var sections = ["Core Vaccines", "Non-Core Vaccines"]
var titles = [["Rabies 1-yr", "Rabies 3-yr", "Distemper", "Parvovirus", "Adenovirus"], ["Parainfluenza", "Bordetella", "Lyme Disease", "Leptospirosis", "Canine Influenza"]]
var textCellIdenifier = "vaccineCell"
// Section Headers
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.sections.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.titles[section].count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdenifier, forIndexPath: indexPath) as UITableViewCell
let object = vaccineEntry[indexPath.section][indexPath.row]
cell.textLabel?.text = object["name"]
cell.detailTextLabel?.text = object["detail"]
return cell
}
}
This line crashes with the fatal error:
let object = vaccineEntry[indexPath.section][indexPath.row]
Or I can not make an object variable and just go:
cell.textLabel?.text = vaccineEntry[indexPath.section][indexPath.row]["name"]
cell.textLabel?.text = vaccineEntry[indexPath.section][indexPath.row]["detail"]
But same deal, just crashes on the ["name"] line. What am I doing wrong? Any help is much appreciated!
Works perfectly fine for me without any errors or warnings!
PFB the code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var vaccineEntry: Array<Array<Dictionary<String,String>>> = [[
["name" : "Rabies 1-yr", "detail": "None"],
["name" : "Rabies 3-yr", "detail": "None"],
["name" : "Distemper", "detail": "None"],
["name" : "Parvovirus", "detail": "None"],
["name" : "Adenovirus", "detail": "None"]],
[
["name" : "Parainfluenza", "detail": "None"],
["name" : "Bordetella", "detail": "None"],
["name" : "Lyme Disease", "detail": "None"],
["name" : "Leptospirosis", "detail": "None"],
["name" : "Canine Influenza", "detail": "None"]
]]
var sections = ["Core Vaccines", "Non-Core Vaccines"]
var titles = [["Rabies 1-yr", "Rabies 3-yr", "Distemper", "Parvovirus", "Adenovirus"], ["Parainfluenza", "Bordetella", "Lyme Disease", "Leptospirosis", "Canine Influenza"]]
var textCellIdenifier = "vaccineCell"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: self.textCellIdenifier)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.sections.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.titles[section].count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdenifier, forIndexPath: indexPath) as UITableViewCell
let object = vaccineEntry[indexPath.section][indexPath.row]
cell.textLabel?.text = object["name"]
cell.detailTextLabel?.text = object["detail"]
return cell
}
}
The running code on simulator:
Here's the work-around that made the code simpler and work better!
var sections = ["Core Dog Vaccines", "Non-Core Dog Vaccines"]
var titles = [["Rabies 1-yr", "Rabies 3-yr", "Distemper", "Parvovirus", "Adenovirus"], ["Parainfluenza", "Bordetella", "Lyme Disease", "Leptospirosis", "Canine Influenza"]]
var details = [["No Dogument", "No Dogument", "No Dogument", "No Dogument", "No Dogument"], ["No Dogument", "No Dogument", "No Dogument", "No Dogument", "No Dogument"]]
var textCellIdenifier = "vaccineCell"
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdenifier, forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = self.titles[indexPath.section][indexPath.row]
cell.detailTextLabel?.text = self.details[indexPath.section][indexPath.row]
return cell
}
Instead of having a complicated Array< Array< Dictionary< String,String>>> I just made a separate array for the details.. Ugh making it complicated to begin with sucks but still learning everyday! Now I can load data from parse like so:
let rabies1Query = PFQuery(className: "Vaccine")
rabies1Query.whereKey("userId", equalTo: (PFUser.currentUser()?.objectId)!)
rabies1Query.whereKey("newID", equalTo: self.dogObject)
rabies1Query.whereKey("vaccineType", equalTo: "Rabies 1-yr")
rabies1Query.findObjectsInBackgroundWithBlock({ (object, error) -> Void in
if error == nil {
if let object = object {
for object in object {
if object["expired"] as! Bool == true {
self.details[0][0] = self.expired
print("printing rabies 1-yr - EXPIRED")
} else if object["expired"] as! Bool == false {
self.details[0][0] = self.upToDate
print("printing rabies 1-yr - UP TO DATE")
}
}
}
} else {
print("printing rabies 1-yr - No Document")
}
})
And so on for each title changing the location of the details array location. Hope someone else can see and learn something!
Related
i am call api using Alamofire and SwiftyJSOn and i am creating model class for display data in tableview here is my response
Response
{
"inspections_todays_data" : [
],
"message" : "Successfully.",
"success" : "1",
"inspections_future_data" : [
],
"inspections_overdue_data" : [
{
"notes" : "Test",
"surveyor_id" : "8",
"longitude" : null,
"time" : "08:00",
"address3" : null,
"county" : "carlow",
"client_id" : "3",
"house_num" : "test street",
"eircode" : "12345",
"address1" : "test area",
"date_inspected" : "2019-01-10",
"address2" : "test city",
"country" : "Ireland",
"latitude" : null,
"name" : "test street",
"property_id" : "22"
}
]
}
as you see in response i have three array and i am fetching inspections_overdue_data array i fetched successfully but not able to display on tableview
Here is my code
Code
func OverdueList(){
let preferences = UserDefaults.standard
let uid = "u_id"
let acTkn = "acc_tkn"
let u_ID = preferences.object(forKey: uid)
let A_Token = preferences.object(forKey: acTkn)
let params = ["user_id": u_ID!, "access_token": A_Token!]
print(params)
Alamofire.request(inspectionsList, method: .post, parameters: params).responseJSON(completionHandler: {(response) in
switch response.result{
case.success(let value):
let json = JSON(value)
print(json)
let data = json["inspections_overdue_data"]
print(data)
if data == []{
self.viewNodata.isHidden = false
}else{
data.array?.forEach({ (iunOverDue) in
let iOveList = OvedueModel(surveyor_id: iunOverDue["surveyor_id"].stringValue, country: iunOverDue["country"].stringValue, time: iunOverDue["time"].stringValue, address2: iunOverDue["address2"].stringValue, notes: iunOverDue["notes"].stringValue, house_num: iunOverDue["house_num"].stringValue, name: iunOverDue["name"].stringValue, address1: iunOverDue["address1"].stringValue, eircode: iunOverDue["eircode"].stringValue, date_inspected: iunOverDue["date_inspected"].stringValue, property_id: iunOverDue["property_id"].stringValue, county: iunOverDue["county"].stringValue, client_id: iunOverDue["client_id"].stringValue)
print(iOveList)
self.overDueData.append(iOveList)
})
self.tblOvedue.reloadData()
}
case.failure(let error):
print(error.localizedDescription)
}
})
}
i also added delegate and datasource through storyboard but still i am not able to show data here is my tableview methods
func numberOfSections(in tableView: UITableView) -> Int {
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return overDueData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! OverDueTableViewCell
let name = overDueData[indexPath.row].name
let address1 = overDueData[indexPath.row].address1
cell.lblTitle.text = "\(name) \(address1)"
return cell
}
numberOfSections is returning 0, you have to return at least 1 if you want to see anything.
Protip: If you only have 1 section, you can omit it since 1 is the default return value.
You should either remove below tableview's delegate method or you should return at-least 1.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
in my app i am first time using AlamofireObjectMapper.
So i am mapping api response data in one class and then i want to use that data.
So here is my code that how i map object
extension OrderListViewController
{
func get_order_list()
{
let url = "\(OrderURL)get_New_order_byPharmacy"
let param : [String : AnyObject] = [
"pharmacyId" : "131"
]
Alamofire.request(.GET, url, parameters: param, encoding: .URL).responseObject { (response:Response<OrderList, NSError>) in
let OrderList = response.result.value
print(OrderList!.Message)
}
}
}
and here is the class where i saving my data
class OrderList: Mappable {
var Message : String!
var Status : Int!
var result:[OrderResult]?
required init?(_ map: Map){
}
func mapping(map: Map) {
Message <- map["Message"]
Status <- map["Status"]
result <- map["Result"]
}
}
now in my OrderListViewController i want to use this data so how can i use this??
class OrderListViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var table_OrderList: UITableView!
override func viewDidLoad() {
slideMenuController()?.addLeftBarButtonWithImage(UIImage(named: "ic_menu_black_24dp")!)
slideMenuController()?.addRightBarButtonWithImage(UIImage(named: "ic_notifications_black_24dp")!)
get_order_list()
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : OrderList_Cell = tableView.dequeueReusableCellWithIdentifier("OrderList_Cell") as! OrderList_Cell
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
}
for example i want to print message value in my tableview cell label. so how can i get that value form OrderList?
Thanks slava its give me some solution. but my json response give me array. So how can i manage it? and i want to return in numberofrowinSetcion is count of array so how can i do this. please see my updated question.
here is my api response.
{
"Status": 1,
"Message": "records are available",
"Result": [
{
"id": 30162,
"status_id": 2,
"status_type": "New Order",
"created_date": "2016-05-11T10:45:00.6779848",
"created": "11 May 2016"
},
{
"id": 30170,
"status_id": 2,
"status_type": "New Order",
"created_date": "2016-05-12T07:01:00.6968385",
"created": "12 May 2016"
},
{
"id": 30171,
"status_id": 2,
"status_type": "New Order",
"created_date": "2016-05-12T09:12:53.5538349",
"created": "12 May 2016"
},
{
"id": 30172,
"status_id": 2,
"status_type": "New Order",
"created_date": "2016-05-12T09:46:09.4329398",
"created": "12 May 2016"
},
{
"id": 30173,
"status_id": 2,
"status_type": "New Order",
"created_date": "2016-05-12T11:26:58.3211678",
"created": "12 May 2016"
},
{
"id": 30178,
"status_id": 2,
"status_type": "New Order",
"created_date": "2016-05-16T07:34:19.9128517",
"created": "16 May 2016"
}
]
}
You need a local variable in your controller to store all the received information that will be used to fill the table. Something like that should do:
class OrderListViewController: ... {
private var orderList: OrderList? // <- the local variable needed
...
}
extension OrderListViewController {
func get_order_list() {
...
Alamofire
.request(...)
.responseObject { (response:Response<OrderList, NSError>) in
switch response.result {
case .Success(let value):
self.orderList = value // <- fill the local variable with the loaded data
self.tableView.reloadData()
case .Failure(let error):
// handle error
}
}
}
...
}
extension OrderListViewController: UITableViewDataSource {
...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : OrderList_Cell = tableView.dequeueReusableCellWithIdentifier("OrderList_Cell") as! OrderList_Cell
// I assume 'OrderList_Cell' class has outlet for status type named 'statusTypeLabel' and OrderResult.statusType is of type String
if let orderList = orderList, orderResults = orderList.result {
cell.statusTypeLabel.text = orderResults[indexPath.row].statusType // <- use of the locally stored data
}
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let orderList = orderList, orderResults = orderList.result {
return orderResults.count
} else {
return 0
}
}
}
Note: the code should be correct in case you receive the single object in JSON from backend.
If backend sends the array of objects - you'll need to use array to store local data (private var listOfOrderLists: [OrderList]) and use Alamofire.request(...).responseArray(...) instead. But the idea about local variable is still the same.
typealias FailureHandler = (error: AnyObject) -> Void
typealias SuccessHandler = (result: AnyObject) -> Void
class WebServiceManager: NSObject {
class func getDataFromService(mehodName:String,success:(result:AnyObject)->(), apiError:(FailureHandler))
{
let url = "\(OrderURL)get_New_order_byPharmacy"
let param : [String : AnyObject] = [
"pharmacyId" : "131"
]
alamoFireManager!.request(.GET, url)
.responseJSON { response in
print(response.response!)
print(response.result)
CommonFunctions.sharedInstance.deactivateLoader()
switch response.result {
case .Success(let JSON):
print("Success with JSON: \(JSON)")
guard let _ = JSON as? NSMutableArray else {
apiError(error: "")
return
}
let listOfItem:NSMutableArray = NSMutableArray()
for (_, element) in adsArray.enumerate() {
let adsItem = Mapper<OrderList>().map(element)
listOfItem.addObject(adsItem!)
}
success(result:listOfItem)
case .Failure(let data):
print(data)
}
}
}
class OrderListViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var table_OrderList: UITableView!
var listOFOrder:NSMutableArray =[]
override func viewDidLoad() {
slideMenuController()?.addLeftBarButtonWithImage(UIImage(named: "ic_menu_black_24dp")!)
slideMenuController()?.addRightBarButtonWithImage(UIImage(named: "ic_notifications_black_24dp")!)
WebServiceManager.getDataFromService("", success: { (result) in
listOFOrder = result as NSMutableArray
self.recordTable?.reloadData()
}) { (error) in
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : OrderList_Cell = tableView.dequeueReusableCellWithIdentifier("OrderList_Cell") as! OrderList_Cell
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listOFOrder.count
}
}
situation:
I have a JSON response parsed via SwiftyJSON. And I'm populating table cells with the data. Everything is cool, but now I need to remove some dictionaries from my JSON, and it doesn't let me do that in an any way (loops or other conditions).
Example of received JSON:
{"post-comments" : [
{
"post" : "new",
"_id" : "1",
},
{
"post" : "new",
"_id" : "2",
},
{
"post" : "post with title",
"_id" : "23",
},
{
"post" : "new",
"_id" : "29",
},
{
"post" : "post with title",
"_id" : "90",
},
{
"post" : "post with title",
"_id" : "33",
}
]
}
I'm tryin to get rid of some dictionaries, lets say if "post" === "new" - I need to remove them from my JSON and continue populate my cells with left data.
Completely stuck.. Any ideas would be much appreciated.
here is the complete tableviewcontroller:
class TableViewContr: UITableViewController {
let Comments : String = "http://localhost:3000/api/comments/"
var json : JSON = JSON.null
override func viewDidLoad() {
super.viewDidLoad()
getPostComments(Comments)
}
func getPostComments(getcomments : String) {
Alamofire.request(.GET, getcomments).responseJSON {
response in
guard let data = response.result.value else {
return
}
self.json = JSON(data)
self.tableView.reloadData()
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch self.json["post-comments"].type {
case Type.Array:
return self.json["post-comments"].count
default:
return 1
}
}
func populateFields(cell: TableViewContrCell, index: Int) {
guard let comment = self.json["post-comments"][index]["post"].string else {
return
}
cell.commentContent!.text = comment
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewContrCell
populateFields(cell, index: indexPath.row)
return cell
}
}
This is an approach to parse and filter the JSON data before reloading the table view.
comments is the data source array containing the post-comments dictionaries
class TableViewContr: UITableViewController {
let commentURL : String = "http://localhost:3000/api/comments/"
var comments = [[String:String]]()
override func viewDidLoad() {
super.viewDidLoad()
getPostComments(commentURL)
}
func getPostComments(getcomments : String) {
Alamofire.request(.GET, getcomments).responseJSON {
response in
guard let data = response.result.value else { return }
let jsonData = JSON(data)
let postComments = jsonData["post-comments"].arrayObject as! [[String:String]]
self.comments = postComments.filter{$0["post"] != "new"}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewContrCell
let comment = comments[indexPath.row]
cell.commentContent!.text = comment["post"]!
return cell
}
}
I Have three section in a UITableview ie - Categories, MyAccount and Support were MyAccount and Support Section are populated with static data but the categories section is to be populated with web api response by the help of Alamofire & SwiftyJSON I am getting the result which i want but can't figure out how to populate the particular section
Here is my code...
import UIKit
import Alamofire
import SwiftyJSON
class MenuView: UIViewController, KYDrawerControllerDelegate,UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var menuTableview: UITableView!
struct Objects {
var sectionName : String!
var sectionObjects : [String]!
}
var objectsArray = [Objects]()
var categoriesArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let bar:UINavigationBar! = self.navigationController?.navigationBar
//self.title = "Home Screen"
bar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
bar.shadowImage = UIImage()
bar.alpha = 0.0
objectsArray = [
Objects(sectionName: "", sectionObjects: ["Home"]),
Objects(sectionName: "Categories", sectionObjects: categoriesArr),
Objects(sectionName: "My Account", sectionObjects: ["My WishList", "My Profile", "My Addresses", "My Order", "Log out"]),
Objects(sectionName: "Support", sectionObjects: ["About Us", "Delivery Information", "Privacy Policy", "Terms & Conditions", "Contact Us", "Return Policy"])]
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
callAPI()
}
//MARK: UITabView DataSources
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell!
cell.textLabel?.text = objectsArray[indexPath.section].sectionObjects[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectsArray[section].sectionObjects.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return objectsArray.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return objectsArray[section].sectionName
}
func callAPI () {
//SwiftSpinner.show("Sending request..")
Alamofire.request(.POST, "http://www.picknget.com/webservice/index.php/Home/get_all_category")
.responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
if let _statusCode = json["status"].string {
print("the ststus code is ", _statusCode)
if (_statusCode == "1"){
self.parseJSON(json)
}
else {
self.callAlert("Alert", _msg: "Something Went Wrong Kindly Check Your Connection & Try Agian")
}
}
//print ("json result ", json)
}
}.responseString { response in
//print("response ",response.result.value)
}
}
func parseJSON(json: JSON) {
for result in json["category"].arrayValue {
print("The available categories are",result["MainCatName"].stringValue)
self.categoriesArr.append(result["MainCatName"].stringValue)
}
print("########")
objectsArray[2].sectionObjects = categoriesArr
print(categoriesArr.count)
print(categoriesArr[0],categoriesArr[1])
dispatch_async(dispatch_get_main_queue(),{
self.menuTableview.reloadData()
});
}
Any Suggestion ,
Thank you in advance
Here is the updated code which works.. Thank to #pbasdf Sir Support and guidance :)
import UIKit
import Alamofire
import SwiftyJSON
class MenuView: UIViewController, KYDrawerControllerDelegate,UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var menuTableview: UITableView!
struct Objects {
var sectionName : String!
var sectionObjects : [String]!
}
var objectsArray = [Objects]()
var categoriesArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let bar:UINavigationBar! = self.navigationController?.navigationBar
//self.title = "Home Screen"
bar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
bar.shadowImage = UIImage()
bar.alpha = 0.0
objectsArray = [
Objects(sectionName: "", sectionObjects: ["Home"]),
Objects(sectionName: "Categories", sectionObjects: categoriesArr),
Objects(sectionName: "My Account", sectionObjects: ["My WishList", "My Profile", "My Addresses", "My Order", "Log out"]),
Objects(sectionName: "Support", sectionObjects: ["About Us", "Delivery Information", "Privacy Policy", "Terms & Conditions", "Contact Us", "Return Policy"])]
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
callAPI()
}
//MARK: UITabView DataSources
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell!
cell.textLabel?.text = objectsArray[indexPath.section].sectionObjects[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectsArray[section].sectionObjects.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return objectsArray.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return objectsArray[section].sectionName
}
func callAPI () {
Alamofire.request(.POST, "http://www.picknget.com/webservice/index.php/Home/get_all_category")
.responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
if let _statusCode = json["status"].string {
print("the ststus code is ", _statusCode)
if (_statusCode == "1"){
self.parseJSON(json)
}
else {
self.callAlert("Alert", _msg: "Something Went Wrong Kindly Check Your Connection & Try Agian")
}
}
//print ("json result ", json)
}
}.responseString { response in
//print("response ",response.result.value)
}
}
func parseJSON(json: JSON) {
for result in json["category"].arrayValue {
print("The available categories are",result["MainCatName"].stringValue)
self.categoriesArr.append(result["MainCatName"].stringValue)
}
print("########")
objectsArray[2].sectionObjects = categoriesArr
print(categoriesArr.count)
print(categoriesArr[0],categoriesArr[1])
dispatch_async(dispatch_get_main_queue(),{
self.menuTableview.reloadData()
});
}
I have a Dictionary data structure like below and I am trying to group them in my TableViewController such that Group A displays MyData that starts with title = A and at the same time display sectionIndexTitlesForTableView with available letters gotten from Title.
[This is my what I want to achieve]
I have tried to scrap off all the first letters from the title Element in my Dictionary and save them in a set using the code below but when I run my app, I get results duplicated in my table.
I am quite new to swift and would be glad to be guided on how to achieve this.
Here's my Dictionary Data:
var data: [[String:AnyObject]] =
[
[
"id": "1",
"title": "A Title",
"alphabet": "A",
"Detail": "This is a String"
],
[
"id": "2",
"title": "A Title Again",
"alphabet": "A",
"Detail": "This is a String"
],
[
"id": "3",
"title": "B Title",
"alphabet": "B",
"Detail": "This is a String"
],
[
"id": "4",
"title": "B Title Again",
"alphabet": "B",
"Detail": "This is a String"
]
]
And Here's my attempt:
class Index: UITableViewController {
var MyData = data
var letters = Set<String>()
override func viewDidLoad() {
super.viewDidLoad()
for element in MyData {
var title = element["title"] as? String
let letter = title?.substringToIndex(advance(title!.startIndex, 1))
letters.insert(letter!)
}
MyData = MyData.sort { element1, element2 in
let title1 = element1["title"] as? String
let title2 = element2["title"] as? String
return title1 < title2
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return letters.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.MyData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell?
cell!.textLabel?.text = (MyData[indexPath.row]["title"] as! String)
return cell!
}
The problem is numberOfRowsInSection has to return the number of rows per section, in your example 2 for section 0 and 2 for section 1
You can collect your letter set with the key value coding method valueForKey which is often mistaken for objectForKey.
Unlike objectForKey which returns one value for the given key valueForKey returns the value of the key alphabetof all members in the array.
This code creates a Set of the letters to purge the duplicates, turns it back to an Array and sorts it.
let letters = (data as NSArray).valueForKey("alphabet") as! [String]
let filteredLetters = Set<String>(letters)
let sortedLetters = Array(filteredLetters).sorted {$0 < $1}
If all values for alphabet – as well as the other keys - are guaranteed to be String there is no need to cast them to optionals.
Then in numberOfRowsInSection you have to filter the number of items of each section
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.filter { ($0["alphabet"] as! String) == sortedLetters[section] }.count
}
Notice that there is no casting needed for the expression sortedLetters[section] because the compiler knows that's an array of String.
Of course you have also to retrieve the appropriate items for the sections in cellForRowAtIndexPath which is quite expensive because the main array is going to be filtered multiple times.
I'd recommend to transform data in viewDidLoad() into a new dictionary with the letters as keys and an array containing the items starting with this particular letter as values. This is the best solution regarding speed and performance.
Here a complete solution (without displaying the letters for quick search)
class TableViewController: UITableViewController {
let data: [[String:String]] =
[
[
"id": "1",
"title": "A Title",
"alphabet": "A",
"Detail": "This is a String"
],
[
"id": "2",
"title": "A Title Again",
"alphabet": "A",
"Detail": "This is a String"
],
[
"id": "3",
"title": "B Title",
"alphabet": "B",
"Detail": "This is a String"
],
[
"id": "4",
"title": "B Title Again",
"alphabet": "B",
"Detail": "This is a String"
]
]
var letters = [String]()
var dataSource = [String:AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
for value in data {
let letter = value["alphabet"]!
if dataSource[letter] == nil {
letters.append(letter)
dataSource[letter] = [[String:AnyObject]]()
}
var array = dataSource[letter] as! [[String:AnyObject]]
array.append(value)
dataSource.updateValue(array, forKey: letter)
}
letters.sorted {$0 < $1}
tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return letters.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let letter = letters[section]
return dataSource[letter]!.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
let letter = letters[indexPath.section]
let letterArray = dataSource[letter]! as! [[String:AnyObject]]
let item = letterArray [indexPath.row]
if let title = item["title"] as? String {
cell.textLabel?.text = title
}
return cell
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return letters[section]
}
}