Swift: UICollectionView & UIImage fatal error: Array index out of range - swift

Hi Im new to swift and need some help thank you. The program worked once, tried to run it again then it crashed. The items are in the array when I debugged but when I try to display the images I get the fatal error.
// Get Brands
func getBrand () {
// Create PFQuery
var query:PFQuery = PFQuery(className: "BrandInfo")
// Call findobjects
query.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]?, error: NSError?) -> Void in
// refresh array
self.brands = [String]()
self.pics = [UIImage]()
// loop through array
for brandObject in objects! {
// get PFObjects
let brandName:String? = (brandObject as! PFObject)["Name"] as? String
if brandName != nil {
self.brands.append(brandName!)
}
let brandPicture = brandObject["Image"] as! PFFile
brandPicture.getDataInBackgroundWithBlock({ (imageData:NSData?, error:NSError?) -> Void in
if(error == nil){
let brandImage = UIImage(data: imageData!)
self.pics.append(brandImage)
println(self.pics.count)
}
})
}
// Refresh CollectionView
self.collectionView.reloadData()
}
}
// Setting up collection view
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.brands.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:collectionViewCell = self.collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! collectionViewCell
// Get elements
let picToDisplay:UIImage? = pics[indexPath.row] // this is the error line
let dataToDisplay:String = brands[indexPath.row]
let imageView:UIImageView? = cell.viewWithTag(2) as? UIImageView
// Set labels
cell.brandImage.image = picToDisplay
cell.brandLabel.text = dataToDisplay
return cell
}
///////////////////////////////
struct brandCollection {
var brandText:[String] = [String]()
var brandImage:[UIImage?] = [UIImage]()
}

You have brands.count items, and you need brand.count images. But you are using a different array pics which will fail if it has less elements than brands. Looking at your code, there are several possibilities why this could happen.
This is bad design. You should have a data array that encapsulates all the information you need. Each image should be connected to its object (the brand), not held in some other array.

Related

Swift: Bool if true, doesn't display image

I'm a beginner so please be patient explaining, Thanks.
So, basically I have a bool column in parse and I want to display an image if it's false and don't display anything if its true.
here are my codes:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("todayCell", forIndexPath: indexPath) as! reqTodaysCell
let cellDataParse: PFObject = self.dataparse.objectAtIndex(indexPath.row) as! PFObject
var newReadQuery = PFQuery(className: "request")
newReadQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
if object["reqRead"] as! Bool == true {
myCell.isRead.image = nil //here is where I say pic to be nil but what happens is that if the first one is true then it will remove the pic for all of them.
// and if its not true it should display the pic
} else {
myCell.isRead.image = UIImage(named: "newReq")
print("user not read")
}
}
}
})
If I don't explain properly please let me know and I will try my best to explain again.
This sounds like an ideal use case for a ternary operator. As per my example below, you use the ? : syntax following a Bool, if the bool is true it will return the first case and if it's false it will return the second case.
newReadQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
let reqRead = object["reqRead"] as! Bool
cell.image.image = reqRead ? nil : UIImage(named: "newReq")
}
}
})
UPDATE
The above may not have worked as the Parse call might not be completed before the cells are loaded.
Create a global variable (outside of any function):
var reqRead = [Bool]()
In ViewDidLoad you can create an array of the bools.
var newReadQuery = PFQuery(className: "request")
newReadQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
reqRead.append(object["reqRead"] as! Bool)
}
tableView.reloadData()
}
})
Then in your CellForRow:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("todayCell", forIndexPath: indexPath) as! reqTodaysCell
let cellDataParse: PFObject = self.dataparse.objectAtIndex(indexPath.row) as! PFObject
cell.image.image = reqRead[indexPath.row] ? nil : UIImage(named: "newReq")
return cell
}
There's a possibility that it will try to populate the cells before the array is loaded but let me know if this works for you.
if object["reqRead"] as! Bool == false {
myCell.isRead.image = nil
myCell.isRead.hidden = false
} else {
myCell.isRead.hidden = true
}

UITableView in swift

can anyone tell me what's wrong with my code i always get the same data repeated in all cells
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.textColor = UIColor.whiteColor()
let query = PFQuery(className:"book")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
var contentString:String = String()
if error == nil {
print("Success retrieved \(objects!.count) scores.")
if let objects = objects {
for object in objects {
let a:String = object.objectForKey("title") as! String
contentString = a
}
}
cell.textLabel!.text = contentString
}}
return cell
}
It will be better to query all your data into viewDidLoad() or any life cycle you would like.
var contentString = [String]() //<-- use an array of string instead of a string variable
override func viewDidLoad()
{
super.viewDidLoad()
let query = PFQuery(className:"book"){ query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
print("Success retrieved \(objects!.count) scores.")
if let objects = objects {
for object in objects
{
let value = object["title"] as! String
self.contentString.append(value) //<-- save the query value to your array
}
self.tableView.reloadData() //<--- then you reload your UI
}
}}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.textColor = UIColor.whiteColor()
cell.textLabel!.text = self.contentString[indexPath.row] //<-- then assign the value into your cell
return cell
}
content inside your loop should be like this.
let a:String = object.objectForKey("title") as! String
contentString += a
and i noticed your block calls for same query always. can you tell me whats the different there
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
var contentString:String = String()
if error == nil {
print("Success retrieved \(objects!.count) scores.")
if let objects = objects {
for object in objects {
let a:String = object.objectForKey("title") as! String
contentString = a
}
}
cell.textLabel!.text = contentString
}}
The query in cellForRowAtIndex is trouble. Imagine, every time a cell comes into view, the code asks for every book from the server.
Put the query outside the datasource method (viewWillAppear, for example). When the query is complete, initialize an array property with the results (e.g. self.bookObjects = objects).
Then in cellForRowAtIndex...
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.textColor = UIColor.whiteColor()
// this is the important idea...
let object = self.bookObjects[indexPath.row]
let a:String = object.objectForKey("title") as! String
cell.textLabel!.text = a
return cell

Populate table with username of other followed users

class FollowingTableTableViewController: UITableViewController {
let usernames = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
getUsernames()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
func getUsernames() {
var user1 = PFUser.currentUser()
var findUsernames:PFQuery = PFQuery(className: "FollowRequests")
findUsernames.whereKey("from", equalTo: user1!)
findUsernames.includeKey("to")
findUsernames.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
var myObjects = objects as! [PFObject]
//Solve this part. Need to get all of the users from the "to" field in FollowRequests.
//If No error
if (error == nil) {
for myObjects in objects! {
var user2 = PFUser()
user2.objectForKey("to")
self.usernames.addObject(user2.username!)
println(user2.username)
}
self.tableView.reloadData()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return self.usernames.count
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell {
let item: AnyObject = self.usernames[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.text = (usernames[indexPath.row] as! String)
return cell
}
I keep getting the fatal error:
unexpectedly found nil while unwrapping an Optional value.
What do I need to do?
Hoping to find where I messed up.
There is a lot of ? in your code:
First: are you trying to query using the currentUser(username ) or (objectID)
Second: in the findObjectsInBackgroundWithBlock method
you need to check if there is an error first then convert the array of any object to PFObject
if error == nil{
if let objects = objects as! [PFObject]{
for oneSpecificObject in objects{
var data = oneSpecificObject["to"] as! String
self.usernames.addObject(data)
}
self.tableView.reloadData()
}
}
Third: In the cellForRowAtIndexPath method
why you have that extra line
let item: AnyObject = self.usernames[indexPath.row]
//review carefully your code then if you still have problem let me know.

Unable to load and display data from parse

var findPublisher:PFQuery = PFUser.query()!
findPublisher.whereKey("objectId", equalTo: quote.objectForKey("publisher")!)
findPublisher.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFUser] {
for object in objects {
//println(object.objectId)
let user:PFuser = (objects as NSArray).lastObject as PFUser
cell.publisherLabel.text = user.username
}
}
}
// Configure the cell...
}
The code here is supposed to query for the objectId and display the username instead of the label. I am getting an error that says use of undeclared type of 'PFUser'... I used "println" right after the error == nil statement and it logs correctly so the error definitely occurs after there.
if you are using a TableViewController, you don't need to create an Outlet of the tableview. So you need to query all the objects from the _User class into an array. So it is up to you to know where you want to place this code either in the ViewDidLoad or ViewWillAppear.
var arrayOFUser :NSMutableArray = []
var findPublisher:PFQuery = PFQuery(className:"_User")
findPublisher.findObjectsInBackgroundWithBlock{ (objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil{
if let objects = objects as? [PFObject] {
for object in objects {
var getusername = object["username"] as! String
self.arrayOFUser.addObject(getusername)
self.tableview.reloadData()
}
}
}
}
// return the count of user into the each cell in the tableview
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.arrayOFUser.count
}
then you go into the ** cellForRowAtIndexPath** method
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.tableview.dequeueReusableCellWithIdentifier("CellSubtitle", forIndexPath: indexPath) as! UITableViewCell
let name = self.arrayOFUser[indexPath.row] as! String
cell.textlabel?.text = name
return cell
}
QuickNote "CellSubtitle" is the name of the cell in the tableview so go into the storyboard in change it.

UICollectionView and Parse Images ( Swift ) No Error but Crash

The program launched and displayed the images from parse with no error, when i ran it again it crashed. My question is if it is because the images aren't scaled to the correct size of the custom cell.
here is the error: the item width must be less than the width of the UICollectionView minus the section insets left and right values.
fatal error: Array index out of range
here is my function:
// Get Brands
func getBrand () {
// Create PFQuery
var query:PFQuery = PFQuery(className: "BrandInfo")
// Call findobjects
query.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]?, error: NSError?) -> Void in
// refresh array
self.brands = [String]()
self.pics = [UIImage]()
// loop through array
for brandObject in objects! {
// get PFObjects
let brandName:String? = (brandObject as! PFObject)["Name"] as? String
if brandName != nil {
self.brands.append(brandName!)
}
let brandPicture = brandObject["Image"] as! PFFile
brandPicture.getDataInBackgroundWithBlock({ (imageData:NSData?, error:NSError?) -> Void in
if(error == nil){
let brandImage = UIImage(data: imageData!)
self.pics.append(brandImage)
println(self.pics.count)
}
})
}
// Refresh CollectionView
self.collectionView.reloadData()
}
}
// Setting up collection view
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.brands.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:collectionViewCell = self.collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! collectionViewCell
// Get elements
let picToDisplay:UIImage? = pics[indexPath.row] // here is where the warning occurs
let dataToDisplay:String = brands[indexPath.row]
let imageView:UIImageView? = cell.viewWithTag(2) as? UIImageView
// Set labels
cell.brandImage.image = picToDisplay
cell.brandLabel.text = dataToDisplay
return cell
Check if you are getting same count for brands and pics array.
In your case you should append array only if you both the brandImage and BrandName.So that your pics and brands array count will be same.