I have an array that contains image references such as: <"UIImage: some number>". However, when I try to set the image in my table my app crashes with the following code:
UIImageView *pic = (UIImageView *)[cell viewWithTag:2];
pic.image = [pictures objectAtIndex:indexPath.row];
The method where the code above resides is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
But when I set the variable "pic" equal to "image" which comes from the following code, everything works. I would use this, but I need to save the images from the Parse database in an array to populate my table:
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
image= [UIImage imageWithData: pictureData];
//image is already declared in the implementation.
Any ideas on what I could do?
Related
im using PFQueryTableViewController from Parse and i noticed when scrolling that images are being repeated. how can i prevent this?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Item";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
PFObject *photoObject = [object objectForKey:#"toObject"];
PFQuery *query = [PFQuery queryWithClassName:#"Photo"];
[query getObjectInBackgroundWithId:photoObject.objectId block:^(PFObject *gameScore, NSError *error) {
// Do something with the returned PFObject in the gameScore variable.
PFImageView *photo = (PFImageView *)[cell viewWithTag:1];
photo.file = [gameScore objectForKey:#"image"];
[photo loadInBackground:^(UIImage * _Nullable image, NSError * _Nullable error) {
}];
}];
return cell;
}
Your query returns the image asynchronously and places it in the cell object captured in your completion block. There is no guaranty that, when the query returns, you cell object hasn't already been reused by the tableview for another row.
Also, updating UI components in a background thread (which I suspect the completion block will be) is also a source of UI problems.
One way around this could be to capture the index path (instead of the cell) and use tableView.cellForRowAtIndexPath() in the completion block to make sure you're updating the right cell with the image. You should also dispatch this to the main thread in order to avoid conflicting with other updates (such as having the cell you're trying to update be victim to yet another reuse - less probable but not impossible)
I am building an app which will query a set of images (thumbnails) from the parse server and then show them in a collection view, similar to how is done on instagram in the users profile page. I created a method which queries the data from the backend successfully :
-(void)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:#"VideoApp"];
NSString * author = [[PFUser currentUser] objectForKey:#"FBName"];
[query whereKey:#"author" equalTo:author];
[query orderByAscending:#"createdAt"];
[query setCachePolicy:kPFCachePolicyNetworkOnly];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %d objects", objects.count);
[self.collectionView reloadData];
userVideosArray = [[NSMutableArray alloc] initWithCapacity:objects.count];
for (PFObject *object in objects) {
PFFile *thumbnail = [object objectForKey:#"video_thumbnail"];
[thumbnail getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
NSLog(#"Fetching image");
[userVideosArray addObject:[UIImage imageWithData:data]];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
}
}];
}
This method successfully retrieves four objects from the back end, and is called in the ViewDidLoad method.
Then in the collection view cellForRowAtIndexPath method I try to set the queried objects images to the UIImageview on the collectionviewcell as follows:
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
//CollectionViewcellCollectionViewCell * cell = (CollectionViewcellCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 70, 70)];
imageView.backgroundColor = [UIColor grayColor];
[cell addSubview:imageView];
PFQuery *query = [PFQuery queryWithClassName:#"VideoApp"];
NSString * info = [[PFUser currentUser] objectForKey:#"FBName"];
[query whereKey:#"author" equalTo:info];
imageView.image = [UIImage imageWithData:[userVideosArray objectAtIndex:indexPath.row]];
return cell;
}
I keep getting an NSException on imageView.image = [UIImage imageWithData:[userVideosArray objectAtIndex:indexPath.row]]; . Not 100 percent sure why. Any ideas?
You are doing this:
1) reloadData: wrong because you did not update any array before doing this. I assume that it is side effect of findObjectsInBackgroundWithBlock which is definitely wrong, because only object which performs reloadData should be responsible for updating data for datasource.
2) initializing userVideosArray with no items (userVideosArray.count == 0). Looking at your error and knowing that cellForItemAtIndexPath is invoked I'm assuming that -collectionView:numberOfItemsInSection: uses other different array to tell the number of items which is wrong, because you are trying to get item from userVideosArray which may have different number of items
3) filling userVideosArray with items in background. Keeping in mind 1 and 2 gives us an answer to your crash. in cellForItemAtIndexPath you are trying to obtain item which is still not loaded
Btw: [cell addSubview:imageView]; will keep adding image views to your cell each time collection view will reuse it
You should update collectionView after you have handled received data, not before. Your call to [self.collectionView reloadData] trickers calls to cellForItemAtIndexPath and my guess is that userVideosArray does not yet contain as many items as you're expecting.
Anyways, the crash. Try this to prevent crashing:
if (indexPath.item < userVideosArray.count)
imageView.image = [UIImage imageWithData:userVideosArray[indexPath.item]];
Btw when using collectionView, I'd recommend using item instead of row, since one collectionView row might contain several items. You know now what you're doing, and it's ok to use row, but later row vs. item terms might get confusing.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
i am newbie in iOS development i Use JSON Data Array to show tableview cell data i make different Cell for Different Section it is working but now i want in my First Section Cell only Data Array first Value and In Second Section all remaining value are Show How it is possible?
I write a code for my First Cell But it was not working
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
static NSString *CellIdentifierOne=#"CellOne";
CustumCell *cellOne =[tableView dequeueReusableCellWithIdentifier:CellIdentifierOne];
if (cellOne == nil)
{
NSArray *nibOne=[[NSBundle mainBundle]loadNibNamed:#"CustumCell" owner:self options:nil];
cellOne=[nibOne objectAtIndex:0];
cellOne.userInteractionEnabled=NO;
}
{
[cellOne.spinner startAnimating];
NSDictionary *dict = [self.imageArray objectAtIndex:indexPath.section];
NSString *img2=[dict valueForKey:#"front_image"];
[cellOne.storeImage sd_setImageWithURL:[NSURL URLWithString:[img2 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] placeholderImage:[UIImage imageNamed:#"Setting.png"] options:SDWebImageHighPriority completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL)
{
[cellOne.spinner stopAnimating];
cellOne.spinner.hidesWhenStopped=YES;
}];
}
return cellOne;
}
}
it shows error in line
NSDictionary *dict = [self.imageArray objectAtIndex:indexPath.section];
here imagearray is a JSON parsing array.i know that it is ask many times but i not get Solution please give me Solution if it is possible.
Ashish Gabani Here Your First Section Contain Only One Row than Defie two Array Like as
NSArray *firstArray=[[NSArray alloc]init];
NSArray *secondArrayarray=[[NSArray alloc]init];
And then Write like as
self.firstArray=[self.imageArray objectAtIndex:0];
self.secondArray=[NSMutableArray arrayWithArray:self.imageArray];
[self.secondArray removeObjectAtIndex:0];
And For First Section Load First Array Data and For Second Section Load Second Array Data
Hope it Help You.
Sorry for the poorly worded title. I have a logic issue that I'm trying to get my head around. The view that I'm working in has a UICollectionView that displays a list of "tanks" associated with a user. This collection view displays a three items:
Tank Name
Tank Capacity
Last Image Stored
The last image stored part is where I'm having trouble. I'm making progress but its a matter of the logic behind it that I'm not sure on. Here is what the data looks like:
I have two classes that I'm interacting with; SavedTanks and SavedTankImages. The unique objectId from a saved tank is also stored as a value in SavedTankImages to allow a sort of pointer reference to the image. This logic works when the user loads a "tank" and can see all of the images they've stored associated with it.
However, for the purposes of this view, I only need to grab the first image from each tank and display that. This is where I need help. Here's what I have so far:
#pragma mark COLLECTION VIEW
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
photoHandler *cell = (photoHandler *)[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
_tankNameArray = [_array objectAtIndex:indexPath.section * 1 + indexPath.row];
cell.tankNameLabel.text = [_tankNameArray valueForKey:#"tankName"];
cell.tankCapLabel.text = [_tankNameArray valueForKey:#"tankCapacity"];
NSArray *objectId = [_array valueForKey:#"objectId"];
for (int i = 0; i < objectId.count; i++)
{
NSString *objectString = [[NSString alloc] init];
objectString = [objectId objectAtIndex:i];
PFQuery *imageQuery = [PFQuery queryWithClassName:#"SavedTankImages"];
[imageQuery whereKey:#"tankObjectId" equalTo:objectString];
[imageQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
for (PFObject *object in objects)
{
NSLog(#"OBJECT TEST: %#", object);
}
}
}];
}
return cell;
}
On OBJECT TEST: %#, this is the logged output:
2014-05-28 11:59:44.750 ReefTrack[305:60b] OBJECT TEST: <SavedTankImages:U6fRTuRo2c: (null)> {
tankImages = "<PFFile: 0x18a25890>";
tankObjectId = tsz4yvrIAN;
}
SavedTankImages: <x> is the objectId of the individual image, and tankObjectId is the tank the image is associated with. I'm getting close, but I need to know how I can effectively iterate and only grab the first item where tankObjectId matches the original objectId. Please forgive me if this sounds a little convoluted.
Thanks for the help in advance as usual.
UPDATE
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
photoHandler *cell = (photoHandler *)[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
_tankNameArray = [_array objectAtIndex:indexPath.section * 1 + indexPath.row];
cell.tankNameLabel.text = [_tankNameArray valueForKey:#"tankName"];
cell.tankCapLabel.text = [_tankNameArray valueForKey:#"tankCapacity"];
NSArray *objectId = [_array valueForKey:#"objectId"];
for (int i = 0; i < objectId.count; i++)
{
// NSString *objectString = [[NSString alloc] init];
// objectString = [objectId objectAtIndex:i];
PFQuery *imageQuery = [PFQuery queryWithClassName:#"SavedTankImages"];
[imageQuery whereKey:#"tankObjectId" equalTo:[objectId objectAtIndex:i]];
[imageQuery getFirstObjectInBackgroundWithBlock:^(PFObject *objects, NSError *error)
{
if (!error)
{
PFFile *imageFile = [objects valueForKey:#"tankImages"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error)
{
cell.parseImage.image = [UIImage imageWithData:data];
}
}];
NSLog(#"Heres your image: %#", objects);
}
}];
}
return cell;
}
The above code selects the first available image and makes it the background for every cell in collectionView. I want to get it so that it only returns the first image for the objectId. In other words
Tank 1 = tank 1 image 1
Tank 2 = tank 2 image 1
Tank 3 = tank 3 image 1
Right now this is what it's doing:
Tank 1 = tank 1 image 1
Tank 2 = tank 1 image 1
Tank 3 = tank 1 image 1
Simple solution, sort and take just the first result. Be aware this isn't as efficient as the next solution:
// sort by createdAt or use updatedAt
[imageQuery orderByDescending:#"createdAt"];
// change from find to getFirst
//[imageQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
[imageQuery getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error)
{
NSLog(#"OBJECT TEST: %#", object);
}
}];
A more advanced solution if you'll often be querying on this is to store a pointer to the most recent object on the tank. You can either do this in your code or create a Cloud Code method to update it automatically after each SavedTankImages object is saved (it would simply load up the related SavedTanks and set mostRecentImage to point to the saved image, then save the SavedTanks).
If you have done this, then you can just use the include: method to load the mostRecentImage with the SavedTanks.
I am trying to save an image taken with the app to core data and then display it in a UITableView. I have written some code I think should work, but it isn't. Thank you in advance!
Here is the code to save the image:
- (void)saveImage {
// NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(self.thumbImage)];
//
// [managedObjectContext setValue:imageData forKey:#"imageData"];
//
// NSLog(#"Saved to CoreData");
NSManagedObjectContext *context = [self managedObjectContext];
TimeTravelFeed *timeTravelFeed = [NSEntityDescription insertNewObjectForEntityForName:#"TimeTravelFeed" inManagedObjectContext:context];
NSData *imageData = UIImageJPEGRepresentation(thumbImage, 0.8f);
[timeTravelFeed setValue:imageData forKey:#"imageData"];
}
Here is were I am displaying it:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
PhotoCell *cell = (PhotoCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[PhotoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
// Configure the cell...
TimeTravelFeed *feed = [timeTravelFeeds objectAtIndex:indexPath.row];
NSData *data = feed.imageData;
self.feedImage = [UIImage imageWithData:data];
cell.thumbImage = self.feedImage;
return cell;
}
There are a number of problems with your code.
First, as has been pointed out, you are not calling [context save:nil].
Second, your cell does not seem to have an image view, just an image. This is not very transparent. Presumably, the cell is responsible for assigning the feedImage property to some image view. It is better to give the PhotoCell an image view property and fill it with
cell.imageView.image = imageRetrieved;
Third, in the cell you first assign the converted image to some class variable (self.feedImage). This is not logical and completely unnecessary. Instead use a variable with local scope or just assign the image to the image view directly.
Finally, you should not really store large blobs (photos) in Core Data. It is recommended that you only store small images, such as thumbnails. For larger files, you should store the path in the Core Data entity and the image files in the documents directory. This can also be accomplished automatically by checking "Allow External Storage" for this attribute in the model editor.
Additional recommendation: do the conversion in the NSManagedObject subclass so you do not have to concern your controllers with this technicality.
Does not look like you've successfully saved your image. Try adding this to saveImage
if (![context save:&error]) {
NSLog(#"Some error", [error localizedDescription]);
}