I'm attempting to display a dictionary in the following format in a tableView (storyboard shown below):
var guestList = ["Event 1": ["Guest 1", "Guest 2", "Guest 3"], "Event 2": ["Guest 1", "Guest 2"], "Event 3": ["Guest 1", "Guest 2"], "Event 4": ["Guest 1", "Guest 2"]]
The data gets added to the objectArray variable successfully, but for some reason, none of the guests are showing in the table (see screenshot below):
import UIKit
import Parse
class RSVPTableViewController: UITableViewController {
var guestList = [String : [String]]()
let eventList = ["Hindu", "Reception", "Sangeet", "Tibetan"]
struct Objects {
var sectionName : String!
var sectionObjects : [String]!
}
var objectArray = [Objects]()
override func viewDidLoad() {
super.viewDidLoad()
for event in eventList { guestList[event] = [String]() }
let query = PFQuery(className:"GuestList")
query.whereKey("Family", equalTo: "Family1")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let family = objects {
print(family)
for guest in family {
for event in self.eventList {
if let invited = guest.valueForKey("\(event)Invite") as? Bool {
if invited {
self.guestList[event]!.append(guest.valueForKey("GuestName") as! String)
}
}
}
}
print(self.guestList)
for (key, value) in self.guestList {
print("\(key) -> \(value)")
self.objectArray.append(Objects(sectionName: key, sectionObjects: value))
self.tableView.reloadData()
}
print(self.objectArray)
}
} else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return objectArray.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return objectArray[section].sectionObjects.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return objectArray[section].sectionName
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = objectArray[indexPath.section].sectionObjects[indexPath.row]
return cell
}
}
SOLUTION:
I added self.tableView.reloadData() in my viewDidLoad and it worked.
Related
SOLVED: I just added 'for-in'
I hope that somebody will help me.
How can I display cells with a different elements inside satisfying "sequence" order. The cells should be arranged in any order and any number of each custom cell. I mean that first cell should to show first item type, second cell - second item type, third cell - first item type, fourth cell - first item.
It depends on the sequence from json data.
I have a json file with next structure (check the sequence array, that what I need):
{
"data": [{
"name": "first item",
"data": {
"text": "first item"
}
}, {
"name": "second item",
"data": {
"url": "picture.png"
}
}
],
"sequence": ["first item", "second item", "first item", "first item"]
}
I created My class for json parsing and other methods:
class MyClass {
private let jsonStr = "url adress"
var items = [ModelItem]()
var jsonData: Object!
func jsonParsing(completionHandler: #escaping (([ModelItem], Error?) -> Void)) {
guard let url = URL(string: jsonStr) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { DispatchQueue.main.async {completionHandler([], error)}
return
}
do {
self.jsonData = try JSONDecoder().decode(Object.self, from: data)
DispatchQueue.main.async {
if let name = self.jsonData?.data[0].data.text {
let firstItem = FirstItemModel(text: name)
self.items.append(firstItem)
}
if let pictureUrl = self.jsonData?.data[1].data.url {
let secondItem = SecondItemModel(pictureUrl: pictureUrl)
self.items.append(secondItem)
}
}
DispatchQueue.main.async {completionHandler(self.items, nil)}
} catch {
print("Error serializing json:", error)
}
} .resume()
}
}
Struct for JSON:
struct Object: Decodable {
let data: [ArrayModelData]
let sequence: [String]
}
Enum for multiple cell types:
enum ModelItemType: String {
case firstItem
case secondItem
}
protocol ModelItem {
var type: ModelItemType { get }
var rowCount: Int { get }
var sectionTitle: String { get }
}
extension ModelItem {
var rowCount: Int {
return 1
}
}
UITableView methods:
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items[section].rowCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.section]
switch item.type {
case .firstItem:
...
case .secondItem:
...
return UITableViewCell()
}
It would be better to keep a separate variables holding the each type of items.
var one: [ItemA]?
var two: [ItemB]?
var seq: [Int] = [1,2,1,1]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return one.count + two.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = indexPath.row
var data: [Item]
if seq[row] == 1 {
data = one[row]
} else {
data = two[row]
}
// ..
}
All items ItemA, ItemB types can conform to a protocol say Item.
protocol Item {
}
class ItemA: Item {}
class ItemB: Item {}
I am trying to implement a TableView like Instagram with one row per section.
I would like to populate two arrays :
first sectionArray to get the row data in function of the section
and object to get the Name of the section.
But when I try to populate sectionArray, I get an error :
"fatal error: Array index out of range"
Do you have an idea of how to fix it??
Thanks!
import UIKit
import ParseUI
import Parse
class TableView: UIViewController, UITableViewDelegate, UITableViewDataSource, CLLocationManagerDelegate {
#IBOutlet weak var tableView : UITableView?
var sectionArray : [[PFFile]] = []
override func viewDidLoad() {
super.viewDidLoad()
self.loadCollectionViewData()
}
var object = [PFObject]()
func loadCollectionViewData() {
let query = PFQuery(className: "Myclass")
// Fetch data from the parse platform
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
// The find succeeded now rocess the found objects into the countries array
if error == nil {
// Clear existing country data
self.object.removeAll(keepCapacity: true)
// Add country objects to our array
if let objects = objects as [PFObject]? {
self.object = Array(objects.generate())
let index = self.object.count as Int
print (index)
for i in 1...index {
//error here!
if let finalImage = self.object[i]["image"] as? [PFFile]
{
self.sectionArray[i] = finalImage
print(self.sectionArray[i])
}
}
}
// reload our data into the collection view
self.tableView?.reloadData()
} else {
// Log details of the failure
print("Error: \(error!) ")
}
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sectionArray.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sectionArray[section].count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < self.object.count {
if let namelabel = object[section]["Name"] as? String {
return namelabel
}
}
return nil
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! ListControllerViewCell!
if cell == nil
{
cell = ListControllerViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
if let finalImage = sectionArray[indexPath.section][indexPath.row] as? PFFile //object[indexPath.row]["image"] as? PFFile
{
finalImage.getDataInBackgroundWithBlock{(imageData: NSData?, error: NSError?) -> Void in
if error == nil
{
if let imageData = imageData
{
cell.ImagePromo!.image = UIImage(data:imageData)
}
}
}
if let CommentLabel = sectionArray[indexPath.section][indexPath.row]
//object[indexPath.row]["Comment"] as? String
{
cell.CommentLabel!.text = CommentLabel
cell.CommentLabel!.adjustsFontSizeToFitWidth = true
}
return cell;
}
}
You have a problem in your for in loop :
You should start at 0, not 1 so your call to the loop looks like :
for i in 0..<index
This is the "danger" with for-in loops compared to C-style loops. You are looping the correct number of times, but you exceed your array size by 1 because you are starting at the wrong index.
Try adding Exception Breakpoint to catch the error location exactly,
Also edit your datasource as,
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if(sectionArray.count != 0) {
return sectionArray.count
} else {
return 0;
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(sectionArray.count < section) {
return sectionArray[section].count
} else {
return 0;
}
}
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'm using an array to read data from a database, Currently I have 8 items in the array. I am trying to make a table where I have a section header. Currently I have 4 sections and I have set that properly and it works. It also works running the first time but when I try to scroll back I get an index out of range. I am using myarray[myindex] to set the cell data for each item and that is not working.
It seems that I need to break up my data into 4 sections that contains only the data for each section to let the table view control it properly. The data can contain any number of sections.
Is there a better way to do this?
I have attached a pic to describe the problem.
Thanks
Adding code on request.
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
print("Returning Sections - > \(sections)")
return sections //seems to work
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print("Return number of rows in section -> \(noRowsInSection[section])")
return noRowsInSection[section] // seems to work
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionHeader[section] // seems to work
}
override func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
// Format for section Headers
let header:UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
header.textLabel!.textColor = UIColor.blueColor()
UIColor.blueColor()
header.textLabel!.font = UIFont.boldSystemFontOfSize(12)
header.textLabel!.frame = header.frame
header.textLabel!.textAlignment = NSTextAlignment.Right
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("OurCell", forIndexPath: indexPath) as! OurTableViewCell
print("myindex - > \(myindex) row -> \(indexPath.row)")
cell.OurCellLabel.text = MyHouses[myindex].getAddressDetails() // End configure houses.cell
//cell.OurCellLabel.text = MyHouses[indexPath.row].getAddressDetails() // End configure houses.cell
myindex++ // PROBLEM HERE - GOES OUT OF RANGE
return cell
}
Here I am getting data from the sqlite DB
func GetListOfHousesFromDB() {
let docsDir = dirPaths[0]
let databasePath = docsDir.stringByAppendingString("/newdb.db")
if fileMgr.fileExistsAtPath(databasePath as String) {
let houseDB = FMDatabase(path: databasePath as String)
if houseDB.open() {
var noRows: Int = 0
var sql = "select count(Address) as cnt from Houses" // Define Query
houseDB.executeStatements(sql) // Execute Query
let results:FMResultSet? = houseDB.executeQuery(sql,withArgumentsInArray: nil) //Get results from Query
if results?.next() == true {
let cnt = (results?.stringForColumn("cnt"))! // Retrieve number of rows from DB
noRows = Int(cnt)!
}
var i = 0
sql = "SELECT Address, Street, City, State, Zip from Houses ORDER BY State, City, Street, Address" // Define Query
houseDB.executeStatements(sql) // Execute Query
let results2:FMResultSet? = houseDB.executeQuery(sql,withArgumentsInArray: nil) // Get results from Query
while results2?.next() == true {
MyHouses.append(newhouse())
MyHouses[i].address = (results2?.stringForColumn("Address"))!
MyHouses[i].street = (results2?.stringForColumn("Street"))!
MyHouses[i].city = (results2?.stringForColumn("City"))!
MyHouses[i].state = (results2?.stringForColumn("State"))!
MyHouses[i].zip = (results2?.stringForColumn("Zip"))!
print("Address -> \(i) \(MyHouses[i].getAddressDetails())")
i++
}
}
houseDB.close()
}
}
Based on your other post, what you need is an updated House model and updated data structure for handling data for your table view.
House - Model class
struct House {
var address: String
var street: String
var city: String
var state: String
var zip: String
func getAddressDetails() -> String {
return "\(address) \(street) \(city) \(state) \(zip)"
}
func getCityState() -> String {
return "\(city) - \(state)"
}
}
Helper Class for loading data
class HouseDataHelper {
private static let _sharedInstance = HouseDataHelper()
var myHouses: Dictionary<String, [House]> = [:]
private init() {
loadHouseData()
}
static func sharedInstance() -> HouseDataHelper {
return _sharedInstance
}
private func loadHouseData() {
var houses = [House]()
//Populating your actual values here. GetListOfHousesFromDB()
//Loading dummy data for testing
var sectionHeader = ""
for i in 0...4 {
sectionHeader = "Header \(i)"
houses += [House(address: "Address1", street: "Street1", city: "City1", state: "State1", zip: "Zip1")]
houses += [House(address: "Address2", street: "Street2", city: "City2", state: "State2", zip: "Zip2")]
houses += [House(address: "Address3", street: "Street3", city: "City3", state: "State3", zip: "Zip3")]
houses += [House(address: "Address4", street: "Street4", city: "City4", state: "State4", zip: "Zip4")]
houses += [House(address: "Address5", street: "Street5", city: "City5", state: "State5", zip: "Zip5")]
myHouses.updateValue(houses, forKey: sectionHeader)
houses = []
}
}
}
Table View Controller
class TableViewController: UITableViewController {
var houses = HouseDataHelper.sharedInstance().myHouses
var sectionHeaders: [String] = []
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
sectionHeaders = Array(houses.keys.sort())
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return houses.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let rows = houses[sectionHeaders[section]] {
return rows.count
}
return 0
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionHeaders[section]
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Populate cells based on "houses"
}
}
I'd like to append the 'userVotes' column in the following parse table into an array using Swift -
Here is my code -
import UIKit
import Parse
class MusicPlaylistTableViewController: UITableViewController {
var usernames = [String]()
var songs = [String]()
var voters = [String]()
var numVotes = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorColor = UIColor.grayColor()
let query = PFQuery(className:"PlaylistData")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
self.usernames.removeAll()
self.songs.removeAll()
self.voters.removeAll()
for object in objects {
let username = object["username"] as? String
self.usernames.append(username!)
let track = object["song"] as? String
self.songs.append(track!)
let title = object["userVotes"]! as? String
self.voters.append(title!)
print("Array: \(self.voters)")
}
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return usernames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
//cell.username.text = usernames[indexPath.row]
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.votes.text = "\(numVotes)"
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
}
I would like the parse array column to append as follows -
[["user1,"user5,"user9"],["user1,"user2,"user3"],["user4,"user5,"user6"],...]
At this point, I'm getting the following runtime error - fatal error: unexpectedly found nil while unwrapping an Optional value
Since each object that is in your "userVotes" is an array and your you've declared
var voters = [String]()
which is not right because you're saying that there will be one element being appended which is not the case.
So, you should declare voters as...
var voters = Array<Array<String>>()
then as you are downloading it,
for object in objects {
let title = object["userVotes"]! as? [String]
self.voters.append(title!)
print("Array: \(self.voters)")
}