I am using the following code to check if the array is empty and then return 0 number of rows for tableview to avoid the crash. But even when the array is empty it is not returning 0 row count and enters the else part which it is not supposed to do. Hence my application crashes with an index out of range exception.
I have tried putting two checks using count and isEmpty but it still enters the else part even when the array is empty.
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if HomeVC.categoryDetail.count == 0{
return 0
}else{
if(HomeVC.categoryDetail.isEmpty)
{
return 0
}
else
{
return HomeVC.categoryDetail.count + 3 as Int
}
}
}
I want to ensure that it won't enter the else part when the array has no value.
You wont. Actually you never reach HomeVC.categoryDetail.isEmpty because you checked HomeVC.categoryDetail.count == 0 earlier.
Note 1: Generally .isEmpty is much more performant than getting .count. Because there is no need to count items when you can check only the last index.
Count Complexity: O(1) if the collection conforms to RandomAccessCollection; otherwise, O(n), where n is the length of the collection. - Source
Note 2: There is no need to cast count to Int. Because it can be done automatically from UInt to Int. And count is returning as Int Already.
Note 3: Use ? : for one line two conditions situations. It will prevent you from extra codes and undefined states.
So the code would be:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return HomeVC.categoryDetail.isEmpty ? 0 : HomeVC.categoryDetail.count + 3
}
Note 4: Try print(HomeVC.categoryDetail) before return to see what is inside when you expect to be empty and it's not.
Related
I'm having an array of data that is being retrieved from a JSON. Storing that data into modal of struct. Also, I have a UITableView to display that data.The problem is that I'm getting multiple entity of response but want to show only one cell at a time. By returning 1 in numberOfRowsInSection is not working.
If I return 1 in numberOfRowsInSection then it gives an error
index out of bound exception
suppose you are storing your response in array named arrResponseData then you need to write your code like this to achieve your desired result.
You were getting index of bound issue because your tableview gets loaded before you store data in array and compiler do not get any index, so it fails with index out of bound
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(arrResponseData && arrResponseData.count>0) {
return 1
} else {
return 0
}
}
I have an array of Dates which I'd like to filter, depending on a date selected by a user. However, since each object has also time, I am having a hard time filtering correctly.
In my table view, I would like to filter this array of items by their date parameter without it comparing the time. Aka:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.filter({$0.date == selectedDate}).count
//count is always 0 because time is not exactly the same in any instance
}
Any ideas on how I can do this?
Try using this function of the system calendar
Calendar.current.isDate($0.date, inSameDayAs: selectedDate)
Here is the apple documentation for this function in case you have an additional clarifying questions
https://developer.apple.com/documentation/foundation/calendar/2292885-isdate
In my CoreData, I have a Person entity, and each Person can have multiple (to Many) Statement entities. The statement entity has an attribute called amountOwed which is a decimal amount. Right now my idea is to loop over all the amounts and add them up, if they of a positive amount add them to the positive array and if they are negative amount add them to that array. Then use that array to figure out how many cells each sections needs to display.
I created a fetchedResultsController and I am trying to use that for the for loop
for i in 0..<fetchedResultsController.fetchedObjects!.count {
let person = fetchedResultsController.fetchedObjects?[i]
let amountTotal = person?.value(forKeyPath: "statement.#sum.amountOwed") as? Decimal
if(amountTotal! <= Decimal(0) )
{
postiveCellNumber += 1
print("\(postiveCellNumber) postive number count")
}
else{
negativeCellNumber += 1
print("\(negativeCellNumber) negative number count")
}
}
Then, I'm trying to use those arrays in the numberOfRowsInSection function like so:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch(section) {
case 0:
return postiveCellNumber
case 1:
return negativeCellNumber
default :return 0
}
}
I don't think my for loop is looping properly as I get an error that says
no object at index 2 in section at index 0.
How about using two different queries, one for the positive values and one for the negative ones, with two different fetched results controllers? Then you can let the FRC do the iterating and counting for you.
You won't be able to use the FRCs to manage sections. You'll have to do that yourself. Specify nil for the sectionNameKeyPath. Then you want something like
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return positiveResultsController.fetchedObjects?.count ?? 0
}
else {
return negativeResultsController.fetchedObjects?.count ?? 0
}
}
or maybe
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
let sectionInfo = positiveResultsController.sections![section]
return sectionInfo.numberOfObjects
}
else {
let sectionInfo = negativeResultsController.sections![section]
return sectionInfo.numberOfObjects
}
}
with similar logic for tableView(_:cellForRowAt:).
It's very difficult to identify the specific cause of the error, without seeing more of how your FRC and tableView are constructed. But this bit looks suspicious:
if(amountTotal! <= Decimal(0) )
{
postiveCellNumber += 1
print("\(postiveCellNumber) postive number count")
}
Surely the if condition is back to front: you should increment the positiveCellNumber if amountTotal! >= Decimal(0)?
But that aside, even if you successfully calculate the counts, you will then face the issue of working out which of the FRC's fetchedObjects should appear in each row of each section:
You could do this "on the fly" in cellForRowAt, but that will involve iterating through the fetchedObjects again, to determine which appear in which section, which is clumsy and inefficient.
You could separate the fetchedObjects out into two separate arrays as a one-off step, once the FRC has done its performFetch. But you then need pretty ugly FRC delegate methods to update the two arrays whenever the FRC's fetchedObjects array is updated.
You could configure your FRC to automatically assign its fetched objects to the correct section, by specifying a sectionNameKeyPath and associated sort descriptors for the underlying fetch. The problem here is that it is not possible to sort the fetch using a calculated figure. If you want to pursue this route, you will need to add a totalAmountOwed attribute to your Person entity, and to ensure it is updated whenever the related Statements change.
Alternatively, you could follow #HalMueller's suggestion, and use two separate FRCs. You would use complementary predicates for the underlying fetches, one to get only those Persons with positive amount owed, the other to get those with negative amount owed. You can then use the fetchedObjects array for one FRC to populate section 0, and the other FRC to populate section 1. Overall, I think this is the solution I would recommend.
Here is my viewDidLoad method -
var query = PFUser.query()
query.findObjectsInBackgroundWithBlock({ ( objects : [AnyObject]! , error: NSError! ) -> Void in
//self.users.removeAll(keepCapacity: true)
for object in objects {
var user:PFUser = object as PFUser
println(user.username)
self.users.append(user.username)
}
self.tableView.reloadData()
})
println( " Count \(users.count) ")
The count of users gets printed before the usernames in the users which makes me believe that it is taking time to fetch the users from the database. And for that reason my tableView never gets updated, the code for which looks like this -
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = users[indexPath.row]
return cell
Gives me an 'Array index out of range error' because the number of rows in my table is three while my dictionary is empty.
Couldn't find any particular solution on swift. Any suggestions?
EDIT : Forgot to mention that the users do get printed but after a long time (even after the count of the users which are being printed after the usernames are)
Just for the information, count is always printed as 1.
The output is something like this -
Count 1
genaks
genaks1427
genaks14271
adu
afd
I shifted my the code in my viewDidLoad function to the viewDidAppear function and it works just about the way I wanted it to work :)
If you can, do the findObjectsInBackgroundWithBlock query inside the cellForRowAtIndexPath method. Check this tutorial: https://www.youtube.com/watch?v=Q6qcrO8uNzU he does the same in video.
When you said "the number of rows in my table is three" you inferred that in numberOfRowsInSection you returned 3.
If I'm right, I believe that's the problem.
You have to do it like this
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
This way the numberOfRowsInSection will never be different from the intended amount.
I have an enum declaration.
enum OP_CODE {
case addition
case substraction
case multiplication
case division
}
And use it in a method:
func performOperation(operation: OP_CODE) {
}
We all know that how we can call this normally
self.performOperation(OP_CODE.addition)
But if I have to call it in some delegate where the integer value is not predictable, how do I call it?
For example:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.delegate.performOperation(indexPath.row)
}
Here, the compiler throws an error Int is not convertible to 'OP_CODE'. I've tried many permutations, but haven't been able to figure it out.
You need to specify the raw type of the enumeration
enum OP_CODE: Int {
case addition, substraction, multiplication, division
}
addition will have a raw value of 0, substraction of 1, and so on.
and then you can do
if let code = OP_CODE(rawValue: indexPath.row) {
self.delegate.performOperation(code)
} else {
// invalid code
}
More info here: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-XID_222
for older swift releases
In case you're using an older version of swift, raw enumerations work a bit different. In Xcode < 6.1, you have to use fromRaw() instead of a failable initializer:
let code = OP_CODE.fromRaw(indexPath.row)
You can use raw values in your enum:
enum OP_CODE : Int{
case addition = 0
case substraction = 1
case multiplication = 2
case division = 3
}
and use the failable initializer taking a raw value as input:
let code = OP_CODE(rawValue: 2) // code == .multiplication
Note that code is an optional, because if the raw value doesn't map to a valid enum, the initializer returns nil.
In your case:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let code = OP_CODE(rawValue: indexPath.row)
if let code = code {
self.delegate.performOperation(code)
}
}
Moreover, given an instance of the enum, you can obtain the associated raw value using the rawValue property.
Note: enums have changed a little bit in Xcode 6.1 - if you're using a previous version, read #GabrielePetronella's answer and related comments.