My code below will produce the correct amount of data in array but the display data will only take the last value and display repeated.
For Example:
When i selected the first image then the first image is successfully display in table view.
When i selected the second image then the array will has 2 data but problem is in table view i will get 2 same image (the second selected image). My expected result will be when selected the second image the first image will still be there and the second display at the subsequence row.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Collector in photoList %#",self.collector);
for (int i = 0; i < collector.count; i++) {
// define the block to call when we get the asset based on the url (below)
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
CGImageRef iref = [imageRep fullResolutionImage];
if (iref) {
galleryImage = [UIImage imageWithCGImage:iref];
[self.tableView reloadData];
}
NSLog(#"[imageRep filename] : %#", [imageRep filename]);
};
NSLog(#"Collector %#",self.collector);
// get the asset library and fetch the asset based on the ref url (pass in block above)
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc]init];
[assetslibrary assetForURL:[collector objectAtIndex:i] resultBlock:resultblock failureBlock:nil];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.imageView.image = galleryImage;
NSLog(#"Gallery image is %#",self.galleryImage);
return cell;
}
EDITED!
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Collector in photoList %#",self.collector);
for (int i = 0; i < collector.count; i++) {
// define the block to call when we get the asset based on the url (below)
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
CGImageRef iref = [imageRep fullResolutionImage];
if (iref) {
galleryImage = [UIImage imageWithCGImage:iref];
//Added mutable array for galleryImage
[photoCollector addObject:galleryImage];
[self.tableView reloadData];
}
NSLog(#"[imageRep filename] : %#", [imageRep filename]);
};
NSLog(#"Collector %#",self.collector);
// get the asset library and fetch the asset based on the ref url (pass in block above)
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc]init];
[assetslibrary assetForURL:[collector objectAtIndex:i] resultBlock:resultblock failureBlock:nil];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//Display image
if(photoCollector.count != 0)
{
cell.imageView.image = [self.photoCollector objectAtIndex:indexPath.row];
}
NSLog(#"This is in cellForRowAtIndexPath");
NSLog(#"Gallery image is %#",self.galleryImage);
// Configure the cell...
return cell;
}
EDITED code at picker didFinishPickingMediaWithInfo!!
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Initialize View Controller
PhotosListViewController *photoListViewController = [[PhotosListViewController alloc]initWithNibName:#"PhotosListViewController" bundle:nil];
ImageModel *imgModel = [[ImageModel alloc]init];
// get the ref url
imageURL = [info valueForKey:UIImagePickerControllerReferenceURL];
//set the imageUrl to the imageModel url in property ?
imgModel.url = imageURL;
[self.collector addObject:imageURL];
photoListViewController.urlCollector = self.collector;
NSLog(#"Collector in root %#",self.collector);
[picker dismissViewControllerAnimated:YES completion:nil];
[self.navigationController pushViewController:photoListViewController animated:YES];
}
EDITED FULL CODE!!
RootViewController.m
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Initialize View Controller
PhotosListViewController *photoListViewController = [[PhotosListViewController alloc]initWithNibName:#"PhotosListViewController" bundle:nil];
// get the ref url
imageURL = [info valueForKey:UIImagePickerControllerReferenceURL];
[self.collector addObject:imageURL];
photoListViewController.urlCollector = self.collector;
NSLog(#"Collector in root %#",self.collector);
[picker dismissViewControllerAnimated:YES completion:nil];
[self.navigationController pushViewController:photoListViewController animated:YES];
}
ImageModel.h
#import <Foundation/Foundation.h>
typedef void(^handler)(UIImage *image);
#interface ImageModel : NSObject
#property (nonatomic, strong) NSURL *imageUrl;
- (void)getImageWithCompletionHandler:(handler)completionBlock;
#end
ImageModel.m
#import "ImageModel.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import <AssetsLibrary/AssetsLibrary.h>
#implementation ImageModel
#synthesize imageUrl;
- (void)getImageWithCompletionHandler:(handler)completionBlock
{
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
CGImageRef iref = [imageRep fullResolutionImage];
if (iref) {
UIImage *image = [UIImage imageWithCGImage:iref];
completionBlock(image);
}
};
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc]init];
[assetslibrary assetForURL:self.imageUrl resultBlock:resultblock failureBlock:nil];
}
#end
PhotoListViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
test1 = [[UIImage alloc]init];
self.imageModelObjects = [NSMutableArray array];
for(NSURL *url in self.urlCollector)
{
ImageModel *imageModel = [[ImageModel alloc] init];
imageModel.imageUrl = url;
[self.imageModelObjects addObject:imageModel];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
ImageModel *model = [self.imageModelObjects objectAtIndex:indexPath.row];
[model getImageWithCompletionHandler:^(UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
});
}];
return cell;
}
#interface ViewController () <UITableViewDataSource>
#property (nonatomic, strong) NSMutableArray *images;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.images = [[NSMutableArray alloc] init];
NSLog(#"Collector in photoList %#",self.collector);
for (int i = 0; i < collector.count; i++) {
// define the block to call when we get the asset based on the url (below)
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
CGImageRef iref = [imageRep fullResolutionImage];
if (iref) {
[self.images addObject:[UIImage imageWithCGImage:iref]];
[self.tableView reloadData];
}
NSLog(#"[imageRep filename] : %#", [imageRep filename]);
};
NSLog(#"Collector %#",self.collector);
// get the asset library and fetch the asset based on the ref url (pass in block above)
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc]init];
[assetslibrary assetForURL:[collector objectAtIndex:i] resultBlock:resultblock failureBlock:nil];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.images.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.imageView.image = self.images[indexPath.row];
return cell;
}
#end
Edited:
ImageModel.h
#import <Foundation/Foundation.h>
typedef void(^handler)(UIImage *image);
#interface ImageModel : NSObject
#property (nonatomic, strong) NSURL *imageURL;
- (void)getImageWithCompletionHandler:(handler)completionBlock;
#end
ImageModel.m
#import "ImageModel.h"
#implementation ImageModel
- (void)getImageWithCompletionHandler:(handler)completionBlock
{
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
CGImageRef iref = [imageRep fullResolutionImage];
if (iref) {
UIImage *image = [UIImage imageWithCGImage:iref];
completionBlock(image);
}
};
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc]init];
[assetslibrary assetForURL:self.imageURL resultBlock:resultblock failureBlock:nil];
}
Controller.m
#import "ViewController.h"
#import "ImageModel.h"
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *imageModelObjects;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageModelObjects = [NSMutableArray array];
for(NSURL *url in self.collector)
{
ImageModel *imageModel = [[ImageModel alloc] init];
imageModel.url = url;
[self.imageModelObjects addObject:imageModel]
}
//You can discard the collecter. IF u want the url, u can get from the self.imageModelObjects.
self.collector = nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
ImageModel *model = [self.imageModelObjects objectAtIndex:indexPath.row];
[model getImageWithCompletionHandler:^(UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
});
}];
// Configure the cell...
return cell;
}
if (iref)
{
galleryImage = [UIImage imageWithCGImage:iref];
//Added mutable array for galleryImage
[photoCollector addObject:galleryImage];
[photoCollector retain];
//[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"cell %d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = #"Hello";
cell.imageView.image = [self.photoCollector objectAtIndex:indexPath.row];
}
// Configure the cell.
return cell;
}
Related
I created in storyboard UITableView with prototype cell, TableViewController and UITableViewCell. Now I would like to choose all the pictures from phone gallery and save one or more to the database. But at this moment I need to implement adding pictures to tableview. I'm beginner in iOS. Can anyone tell me how can do that? give me a link or code? Thank you for advance
You can use ALAssetsLibrary in AssetsLibrary framework to fetch all images from phone gallery. Save each images in to an Array.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
void (^assetEnumerator)( ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result != nil) {
if([[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto]) {
NSURL *url= (NSURL*) [[result defaultRepresentation] url];
[library assetForURL:url
resultBlock:^(ALAsset *asset) {
UIImage *img = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]];
[imgArray addbject:img];
}
failureBlock:^(NSError *error){ NSLog(#"operation failed"); } ];
}
}
};
Then you can use this array for saving to DB or to show in tableview.
For Using in TableView, you need to add UIImageView in your prototype cell, then set the images from the array to the cell accordingly. Your table view delegate methods will be like below.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [imgArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CellId = #"ImageCell";
CustomTableViewCell *cell = (CustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellId];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellId];
}
UIImage *img = [imgArray objectAtIndex:indexPath.row];
cell.imgView.image = img;
return cell;
}
If you want to pick the image from gallery and show it to tableview, you can use Custom ImageView in tableView or CustomCell imageView
First you need to pick the image from gallery and save it to array
Before that allocate and initialize the array in viewDidLoad method
ViewController.m
#import "ViewController.h"
#interface ViewController ()
{
NSMutableArray *arrayImage;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
arrayImage = [[NSMutableArray alloc]init];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image=[info objectForKey:#"UIImagePickerControllerOriginalImage"];
imageView.image=image;
[arrayImage addObject:image];
picker.delegate =self;
[picker dismissViewControllerAnimated:YES completion:nil];
}
Then in tableView
If you CustomCell
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = (CustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell==nil)
{
NSArray *nib = [[NSBundle mainBundle]loadNibNamed:#"CustomTableViewCell" owner:self options:nil];
cell = nib[0];
}
cell.galleryImageView.image = [arrayImage objectAtIndex:indexPath.row];
return cell;
}
If you use default table view cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *strCell = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:strCell];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strCell];
}
cell.imageView.image = [arrayImage objectAtIndex:indexPath.row];
return cell;
}
Good afternoon,
I have created a Segue in my TableViewController where the user can touch the user profile and it sends that username to a ProfileViewController, where the user information is loaded (posts, image, info...) (something like a Facebook wall).
The problem is when I touch one user, it displays everything correctly, but when I touch another profile image, it displays the same ProfileViewController again, like the username is not updated and it's send wrong.
When I touch anywhere on the screen and then I touch again the user profile, in that case it works, but I need to make this work in every case.
I'm going to post my TableViewController if that helps you to find the problem because it's almost working, the only thing that I need to fix is that username that is not updated (Tap Gesture Recognizer over the image).
The segues are at the end of the code.
TableViewController.m
//
// CarTableViewController.m
//
#import "CarTableViewController.h"
#import "CarTableViewCell.h"
#import "CarTableViewController.h"
#import "CarDetailViewController.h"
#import "OtherProfileUserViewController.h"
#import <SDWebImage/UIImageView+WebCache.h>
#implementation CarTableViewController
#synthesize carMakes = _carMakes;
#synthesize carModels = _carModels;
#synthesize carImages = _carImages;
#synthesize likes = _likes;
#synthesize comments = _comments;
#synthesize username = _username;
#synthesize refuser = _refuser;
#synthesize profileImage = _profileImage;
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchJson];
[self.tableView reloadData];
// Initialize the refresh control.
self.refreshControl = [[UIRefreshControl alloc] init];
//self.refreshControl.backgroundColor = [UIColor blackColor];
//self.refreshControl.tintColor = [UIColor whiteColor];
[self.refreshControl addTarget:self
action:#selector(fetchJson)
forControlEvents:UIControlEventValueChanged];
}
- (void)viewWillAppear:(BOOL)animated
{
self.navigationController.navigationBar.hidden = YES;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [_jsonArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"carTableCell";
CarTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CarTableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.makeLabel.text = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"id"];
cell.likes.text = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"likes"];
cell.comments.text = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"comments"];
cell.username.text = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"username"];
cell.refuser.text = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"user_ref"];
cell.modelLabel.text = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"user"];
NSURL * imageURL = [NSURL URLWithString:[[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"imagen"]];
[cell.carImage setImageWithURL:imageURL placeholderImage:[UIImage imageNamed:#"imagen"] options:SDWebImageRefreshCached];
NSURL * imageURL2 = [NSURL URLWithString:[[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"image"]];
[cell.profileImage setImageWithURL:imageURL2
placeholderImage:[UIImage imageNamed:#"image"]
options:SDWebImageRefreshCached];
return cell;
}
-(void)fetchJson {
self.carModels = [[NSMutableArray alloc] init];
self.carMakes = [[NSMutableArray alloc] init];
self.carImages = [[NSMutableArray alloc] init];
self.likes = [[NSMutableArray alloc] init];
self.comments = [[NSMutableArray alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSString * urlString = [NSString stringWithFormat:#"http://website.com/service.php"];
NSURL * url = [NSURL URLWithString:urlString];
NSData * data = [NSData dataWithContentsOfURL:url];
NSError *error;
[_jsonArray removeAllObjects];
_jsonArray = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
for(int i=0;i<_jsonArray.count;i++)
{
NSDictionary * jsonObject = [_jsonArray objectAtIndex:i];
NSString* imagen = [jsonObject objectForKey:#"imagen"];
[_carImages addObject:imagen];
NSDictionary * jsonObject2 = [_jsonArray objectAtIndex:i];
NSString* user = [jsonObject2 objectForKey:#"user"];
[_carMakes addObject:user];
NSDictionary * jsonObject3 = [_jsonArray objectAtIndex:i];
NSString* date = [jsonObject3 objectForKey:#"date"];
[_carModels addObject:date];
}
NSLog(#"carModels ==> %#", _jsonArray);
dispatch_async(dispatch_get_main_queue(), ^{
{
[self.tableView reloadData];
[self.refreshControl endRefreshing];
}});
}
);
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueprofile"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
OtherProfileUserViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"username"];
}
if ([segue.identifier isEqualToString:#"segueprofile2"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
OtherProfileUserViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"username"];
}
}
#end
Thanks in advance.
OK, here is one of the options.
1. Delete all existing segues (segueprofile and segueprofile2)
2. Connect your tableviewcontroller (important: not tableview but controller itself) with your details controller by push-segue and name it segueprofile3
3. Refactor your code in the following manner
#property (nonatomic, strong) NSIndexPath * selectedIndexPath;
...
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)anIndexPath {
self.selectedIndexPath = anIndexPath;
[self performSegueWithIdentifier:#"segueprofile3" sender:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueprofile3"]) {
OtherProfileUserViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = [[_jsonArray objectAtIndex:selectedIndexPath.row] valueForKey:#"username"];
}
}
Another option. Place UIButton control right over avatar UIImageView. Create associated IBOutlet inside your CarTableViewCell class and make outlet connection.
#property (nonatomic, weak) UIButton * btnAvatar;
Next, modify CarTableViewController code
//1. declare new propery
#property (nonatomic, strong) id selectedProfile;
//2. implement future button tap handler
-(IBAction) btnAvatarDidPress:(id) sender {
int tag = ((UIButton *) sender).tag;
self.selectedProfile = [_jsonArray objectAtIndex:tag];
[self performSegueWithIdentifier:#"segueprofile3" sender:nil];
}
//Extend your cellForRowAtIndexPath method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"carTableCell";
CarTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CarTableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.makeLabel.text = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"id"];
cell.btnAvatar.tag = indexPath.row;
... rest of method
}
then remove didSelectRowAtIndexPath method, and connect btnAvatar Touch Up Inside action to btnAvatarDidPress method inside your controller code.
That's all I hope I've forgot nothing.
here is your example, in prepareForSegue, get the cell which button is in:
id v = sender;
while (![(v= v.superView) isKindOfClass:[UITableViewCell class]]
&& v!=nil) ;
if (v==nil)
// no ancestor view is cell.
else{
// now v is the cell.
NSIndexPath * indexPath = [self.tableView indexPathForCell:v];
// do the left
}
Here's my method inside of the UITableViewDataSource view controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"studentCell";
StudentTableCell *cell = (StudentTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
// Never gets called
}
Student *student = self.studentList[indexPath.row];
cell.nameFirst.text = student.nameFirst;
cell.nameLast.text = student.portrait.assetURL;
// Portrait
CGImageRef portraitRef = [cell.portrait.image CGImage];
CIImage *portraitImage = [cell.portrait.image CIImage];
if (portraitRef == NULL && portraitImage == nil) {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *representation = [asset defaultRepresentation];
CGImageRef assetRef = [representation fullResolutionImage];
if (assetRef) {
[cell.portrait setImage:[UIImage imageWithCGImage:assetRef]];
}
} failureBlock:^(NSError *error) {}];
}
return cell;
}
This works as expected for the first few rows that fit inside of the initial scroll position of the table.
But as I scroll down, cell.nameFirst.text change as expected, while cell.portrait.image gets recycled and starts repeating the images loaded inside of the first scroll position.
Questions
How do I make sure every cell has the appropriate image
Can cell every be nil?
You need to update image whether it is set or not. Your code only sets the image if there isn't one already. Cells get reused as you scroll. So each cell needs to be updated with the image appropriate for the indexPath.
Also note that assetForURL:resultBlock:failureBlock:. It's asynchronous. This means you need to update the cell on the main thread once you get the image in the resultBlock.
cell.nameFirst.text = student.nameFirst;
cell.nameLast.text = student.portrait.assetURL;
// Portrait
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *representation = [asset defaultRepresentation];
CGImageRef assetRef = [representation fullResolutionImage];
if (assetRef) {
dispatch_async(dispatch_get_main_queue(), ^{
[cell.portrait setImage:[UIImage imageWithCGImage:assetRef]];
});
}
} failureBlock:^(NSError *error) {}];
return cell;
The best way to make sure every cell has the appropriate image is create dictionary and in cellForRowAtIndexPath: check value(image) in the dictionary object at key (I like use indexPath.row as a key). If it is set it up for the cell if it isn't call:
[library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {...
and once image is downloaded add it to the dictionary with key (indexPath.row).
You should reload the cell when you download your image, just remember to do it on the main thread.
I would suggest to utilize an image cache. Suppose the image cache has the following API:
typedef void (^completion_t)(id result, NSError* error);
#interface SimpleImageCache : NSObject
/**
Returns the image for the specified URL if it exists, otherwise nil.
*/
- (UIImage*) imageWithURL:(NSURL*)url;
/**
Asychronounsly loads the image from the asset library. The compeltion handler will
be called when the image is available or when an error occured.
The execution context where the compeltion handler will be executed is
implementation defined.
*/
- (void) loadImageWithURL:(NSURL*)url completion:(completion_t)completionHandler;
#end
In your code you would use it as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"studentCell";
StudentTableCell *cell = (StudentTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
// Never gets called
}
Student *student = self.studentList[indexPath.row];
cell.nameFirst.text = student.nameFirst;
cell.nameLast.text = student.portrait.assetURL;
// Portrait
NSURL* url = [NSURL URLWithString:student.portrait.assetURL];
UIImage* portrait = [self.imageCache imageWithURL:url];
if (portrait == nil) {
portrait = self.placeholderImage;
[self.imageCache loadImageWithURL:url completion:^(id image, NSError*error){
dispatch_async(dispatch_get_main_queue(), ^{
StudentTableCell* cell = (StudentTableCell *)[tableView cellForRowAtIndexPath:indexPath];
[cell.portrait setImage:image];
});
}];
}
[cell.portrait setImage:portrait];
return cell;
}
Implementation of SimpleImageCache
Warning: It's not tested, but it might give you a jump start, or an idea.
#interface SimpleImageCache ()
#property (nonatomic, strong) NSMutableDictionary* images;
#property (nonatomic, strong) ALAssetsLibrary* assetsLibrary;
#property (nonatomic, strong) UIImage* missingImage;
#end
#implementation SimpleImageCache {
dispatch_queue_t _sync_queue;
}
- (id)init {
self = [super init];
if (self) {
_sync_queue = dispatch_queue_create("sync_queue", NULL);
}
return self;
}
- (NSMutableDictionary*) images {
if (_images == nil) {
_images = [[NSMutableDictionary alloc] init];
}
return _images;
}
- (ALAssetsLibrary*) assetsLibrary {
if (_assetsLibrary == nil) {
_assetsLibrary = [[ALAssetsLibrary alloc] init];
}
return _assetsLibrary;
}
- (UIImage*) imageWithURL:(NSURL*)url {
__block UIImage* image;
dispatch_sync(_sync_queue, ^{
id obj = self.images[url];
if ([obj isKindOfClass:[UIImage class]]) {
image = obj;
}
});
return image;
}
- (void) loadImageWithURL:(NSURL*)url completion:(completion_t)completionHandler {
dispatch_async(_sync_queue, ^{
if (self.images[url] != nil) {
return;
}
self.images[url] = #"pending";
[self.assetsLibrary assetForURL:url resultBlock:^(ALAsset *asset) {
ALAssetRepresentation* representation = [asset defaultRepresentation];
__block UIImage* image = CFBridgingRelease([representation fullResolutionImage]);
dispatch_async(_sync_queue, ^{
if (image == NULL) {
image = self.missingImage;
NSAssert(image, #"image is nil");
}
self.images[url] = image;
if (completionHandler) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
completionHandler(image, nil);
});
}
});
} failureBlock:^(NSError *error) {
if (completionHandler) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
completionHandler(nil, error);
});
}
}];
});
}
#end
Currently i have 2 view Controllers, i select a photo from view controller,A and pass the image url to second view controller,B and use the url to retrieve the image selected from A.But problem is the image can be retrieved at viewDidLoad but cannot retrieve at table viewcellForRowAtIndexPath. I not sure what is the root cause can somebody help me?
//A.m
(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Initialize View Controller B
B *b = [[B alloc]initWithNibName:#"PhotosListViewController" bundle:nil];
// get the ref url
NSURL *imageURL = [info valueForKey:UIImagePickerControllerReferenceURL];
photoListViewController.test = imageURL;
NSLog(#"%#",imageURL);
[picker dismissViewControllerAnimated:YES completion:nil];
[self.navigationController pushViewController:photoListViewController animated:YES];
}
- (IBAction)organiseAttachement:(id)sender {
// Initialize View Controller
B *b = [[PhotosListViewController alloc]initWithNibName:#"B" bundle:nil];
[self.navigationController pushViewController:b animated:YES];
}
//B.h
#import <UIKit/UIKit.h>
#interface PhotosListViewController : UITableViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
#property (nonatomic, strong) NSURL *test;
#end
//B.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Image is THIS --- %#",test);
// define the block to call when we get the asset based on the url (below)
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
CGImageRef iref = [imageRep fullResolutionImage];
if (iref) {
self.galleryImage = [UIImage imageWithCGImage:iref];
}
[self.tableView reloadData];
NSLog(#"[imageRep filename] : %#", [imageRep filename]);
NSLog(#"[imageRep image] : %#", galleryImage);
};
// get the asset library and fetch the asset based on the ref url (pass in block above)
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:test resultBlock:resultblock failureBlock:nil]
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.imageView.image = self.galleryImage;
return cell;
}
Replace
// define the block to call when we get the asset based on the url (below)
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
CGImageRef iref = [imageRep fullResolutionImage];
if (iref) {
self.galleryImage = [UIImage imageWithCGImage:iref];
}
NSLog(#"[imageRep filename] : %#", [imageRep filename]);
NSLog(#"[imageRep image] : %#", galleryImage);
};
with
// define the block to call when we get the asset based on the url (below)
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
CGImageRef iref = [imageRep fullResolutionImage];
if (iref) {
self.galleryImage = [UIImage imageWithCGImage:iref];
}
[self.tableView reloadData]; //Reload the tableView.
NSLog(#"[imageRep filename] : %#", [imageRep filename]);
NSLog(#"[imageRep image] : %#", galleryImage);
};
EDITED:
A.m
#interface A ()
#property(nonatomic,strong) B *b;
#end
#implementation b
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
if(self.b)
{
// Initialize View Controller B
self.b = [[B alloc]initWithNibName:#"PhotosListViewController" bundle:nil];
}
// get the ref url
NSURL *imageURL = [info valueForKey:UIImagePickerControllerReferenceURL];
self.b.test = imageURL;
NSLog(#"%#",imageURL);
[picker dismissViewControllerAnimated:YES completion:nil];
[self.navigationController pushViewController:self.b animated:YES];
}
- (IBAction)organiseAttachement:(id)sender {
if(self.b == nil)
{
// Initialize View Controller
self.b = [[PhotosListViewController alloc]initWithNibName:#"B" bundle:nil];
}
[self.navigationController pushViewController:self.b animated:YES];
}
#end
I am using asyncimageview classes nicely provided by: http://www.markj.net/iphone-asynchronous-table-image/
I am getting image urls from a json file and loading each image to a cell.
Problem:
When i scroll up, and scroll back down to the same cell, the image reloads (disappears and appears again). I can't figure out why the image keeps reloading? Does anyone have suggestions or a solution to how I could make it stop? Thanks in advance!
// Asks the data source to return a cell to insert in a particular table view location
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Gets the current news article
NewsArticle *theCurrentArticle = [self.listofNewsArticles objectAtIndex:[indexPath row]];
//Gets the title from the current article
NSString *theTitle = theCurrentArticle.title;
//Gets the image url
NSString *imageUrl = theCurrentArticle.imageURL;
//Gets the description of the current news article
NSString *theDescription = theCurrentArticle.description;
NewsCustomCell *cell = (NewsCustomCell *)[tableView dequeueReusableCellWithIdentifier:#"NewsContent"];
__block NewsCustomCell *aCell;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (cell == nil) {
aCell = (NewsCustomCell *)[tableView dequeueReusableCellWithIdentifier:#"NewsContent"];
} else {
AsyncImageView* oldImage = (AsyncImageView*)
[cell.contentView viewWithTag:999];
[oldImage removeFromSuperview];
}
AsyncImageView *imageView = [[AsyncImageView alloc] initWithFrame:CGRectMake(5, 5, 100, 100)];
imageView.tag = 999;
dispatch_async(dispatch_get_main_queue(), ^{
[imageView loadImageFromURL:[NSURL URLWithString:imageUrl]];
[cell.contentView addSubview:imageView];
cell.titleLabel.text = theTitle;
cell.descriptionLabel.text = theDescription;
cell.imageLabel.contentMode = UIViewContentModeScaleAspectFill;
});
});
return cell;
}
Heres what the current app looks like:
Solution:
Just worked with this class I finally found.
https://github.com/rs/SDWebImage
This handles caching, async downloads. Wish I found this sooner..
I hope you have Write AsyncImageView and ImageCache and ImageCacheObject classes.
Write this code inside cellForRowAtIndexPath:
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Quest_Forum *Quest_ForumObj=[queArr objectAtIndex:i+indexPath.row];
// NSLog(#"cell for row at... i val.. %d",i+indexPath.row);
for(UIView * view in cell.contentView.subviews){
if([view isKindOfClass:[AsyncImageView class]])
{
// NSLog(#"remove old images");
[view removeFromSuperview];
view = nil;
}
}
AsyncImageView *asyncImageView = nil;
UIImageView *cellImage = (UIImageView *)[cell viewWithTag:1]; // Inside tableview i have taken tableviewcell and given tag 1 to that imageview
asyncImageView = [[AsyncImageView alloc] initWithFrame:cellImage.frame] ;
[asyncImageView loadImageFromURL:Quest_ForumObj.Quest_Image];
asyncImageView.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:asyncImageView];
UILabel *lblQuest = (UILabel *)[cell viewWithTag:2]; // Given tag 2 to the label inside tableViewCell
lblQuest.text=Quest_ForumObj.Question;
In ImageCacheObject.h
#class ImageCacheObject;
#interface ImageCache : NSObject
{
NSUInteger totalSize; // total number of bytes
NSUInteger maxSize; // maximum capacity
NSMutableDictionary *myDictionary;
}
#property (nonatomic, readonly) NSUInteger totalSize;
-(id)initWithMaxSize:(NSUInteger) max;
-(void)insertImage:(UIImage*)image withSize:(NSUInteger)sz forKey:(NSString*)key;
-(UIImage*)imageForKey:(NSString*)key;
In ImageCacheObject.m
#import "ImageCacheObject.h"
#synthesize totalSize;
-(id)initWithMaxSize:(NSUInteger) max
{
if (self = [super init])
{
totalSize = 0;
maxSize = max;
myDictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
-(void)dealloc // Don't write this method if you are using ARC
{
[myDictionary release];
[super dealloc];
}
-(void)insertImage:(UIImage*)image withSize:(NSUInteger)sz forKey:(NSString*)key
{
// NSLog(#"count of insert image%d",sz);
ImageCacheObject *object = [[ImageCacheObject alloc] initWithSize:sz Image:image];
while (totalSize + sz > maxSize)
{
NSDate *oldestTime;
NSString *oldestKey;
for (NSString *key in [myDictionary allKeys])
{
ImageCacheObject *obj = [myDictionary objectForKey:key];
if (oldestTime == nil || [obj.timeStamp compare:oldestTime] == NSOrderedAscending)
{
oldestTime = obj.timeStamp;
oldestKey = key;
}
}
if (oldestKey == nil)
break; // shoudn't happen
ImageCacheObject *obj = [myDictionary objectForKey:oldestKey];
totalSize -= obj.size;
[myDictionary removeObjectForKey:oldestKey];
}
[myDictionary setObject:object forKey:key];
[object release];
}
-(UIImage*)imageForKey:(NSString*)key
{
ImageCacheObject *object = [myDictionary objectForKey:key];
if (object == nil)
return nil;
[object resetTimeStamp];
return object.image;
}
In ImageCacheObject.h
#interface ImageCacheObject : NSObject
{
NSUInteger size; // size in bytes of image data
NSDate *timeStamp; // time of last access
UIImage *image; // cached image
}
#property (nonatomic, readonly) NSUInteger size;
#property (nonatomic, retain, readonly) NSDate *timeStamp;
#property (nonatomic, retain, readonly) UIImage *image;
-(id)initWithSize:(NSUInteger)sz Image:(UIImage*)anImage;
-(void)resetTimeStamp;
In ImageCacheObject.m
#synthesize size;
#synthesize timeStamp;
#synthesize image;
-(id)initWithSize:(NSUInteger)sz Image:(UIImage*)anImage
{
if (self = [super init])
{
size = sz;
timeStamp = [[NSDate date] retain];
image = [anImage retain];
}
return self;
}
-(void)resetTimeStamp
{
[timeStamp release];
timeStamp = [[NSDate date] retain];
}
-(void) dealloc
{
[timeStamp release];
[image release];
[super dealloc];
}