I initialize a tableviewcell with a small image while the large image is downloading. when the large image is downloaded I notify the tableView cell imageView to update the image but nothing happens.
This is my table view cell creation method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"EventTableViewCellID";
EventTableViewCell *cell = [self.eventsTableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
Event *e = [_events objectAtIndex:indexPath.row];
[cell.eventTitle setText:e.eventTitle];
if (e.largeEventImage) {
[cell.eventImageView setImage:[e largeEventImage]];
} else {
[cell.eventImageView setImage:[e thumbImage]];
}
cell.event = e;
cell.delegate = self;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSString *dateString = [_formatter stringFromDate:e.eventDate];
[cell.eventDate setText:dateString];
return cell;
}
This method (in the tableView cell) is called when a image is fetched and it is supposed to update the cell image view
- (void)imagesDownloaded {
if (self.event.largeEventImage) {
[self.eventImageView setImage:_event.largeEventImage];
} else {
[self.eventImageView setImage:_event.thumbImage];
}
}
you can use AFNetworking library for that.
there is a class AFNetworking + UIImageView
this is the method for load and display the image...
[imageView setImageWithURLRequest:request
placeholderImage:#"Your thumbImage"
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
yourImageView.image = image;
}
failure:nil];
hope this helps you to done your work.
Related
I have to solve this problem, as you can see from the photo.
If I click on one of the two lines, the icon pops up and the label and imageView gets smaller.
I must be able to select and deselect without the content being reduced.
I'm trying with sizeToFit but without success...
The other thing is that I use the custom .xib in this way
-(MultiIbanTableCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
NSString *idCell = #"MultiIbanTableCell";
MultiIbanTableCell *cell;
DtoInstrument *account = (DtoInstrument *)[self.bankSelected.dtoBank.instruments objectAtIndex:indexPath.row];
cell = [tableView dequeueReusableCellWithIdentifier:idCell];
if (cell) {
[cell setBankCellWithBankData:account];
}
else {
cell = [MultiIbanTableCell getBankCellWithBankData:account];
}
[cell.logoImage sd_setImageWithURL:[NSURL URLWithString:self.bankSelected.logo_search]
placeholderImage:nil
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
}];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.adjustsFontSizeToFitWidth = YES;
cell.contentView.sizeToFit;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//link store vuoto
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
self.selectedInstrument = (DtoInstrument *)[self.bankSelected.dtoBank.instruments objectAtIndex:indexPath.row];
self.confirmButton.enabled = YES;
tableView.sizeToFit;
}
Sorry for posting this question again but I've looked into many answers and neither of them was helpfull to solve my issue.
So this my code :
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = #"radioCell";
RadioTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[RadioTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
[self configureCommentCell:cell atIndexPath:indexPath];
return cell;
}
when I scroll down my cell get mixed up and some of data are repeated, so I've tried this :
static NSString *CellIdentifier = #"memberCell";
RadioCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[RadioTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
and this :
RadioTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil) {
cell = [[RadioTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
}
But it didn't fixed my issue and I get white empty cells ? please how to fix this issue ?
Update
- (void)configureCommentCell:(RadioTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
NSDictionary *object;
if ([_dataArray[indexPath.section] isKindOfClass:[NSArray class]])
object = [_dataArray[indexPath.section] objectAtIndex:indexPath.row];
else
object = [[_dataArray[indexPath.section] valueForKey:#"radioList"] objectAtIndex:indexPath.row];
if (object[#"jsonUrl"]) {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:object[#"jsonUrl"] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//NSDictionary *tempObject = (NSDictionary *) responseObject;
if (![[responseObject objectForKey:#"type"] isEqualToString:#"error"]) {
NSDictionary *tempObject = [responseObject[#"data"] objectAtIndex:0];
cell.playingNow.text = tempObject[#"song"];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
cell.name.text = [NSString stringWithFormat:#" %#", object[#"title"]];
if (object[#"logoUrl"])
[cell.logo setImageWithURL:[NSURL URLWithString:object[#"logoUrl"]]];
}
I see that your problem is that you are fetching the data of you cells inside configureCommentCell that's called inside cellForRowAtIndexPath. which is wrong, because it too late to fetch data inside cellForRowAtIndexPath, in this delegate method you should return the cell.
this line may be called before retrieving the data from server :
cell.name.text = [NSString stringWithFormat:#" %#", object[#"title"]];
Instead you should:
Fetch the data inside a separate method for example fetchData
when the data is downloaded inside the completion block of AFNetworking method, store the data inside an NSArray called for example myDataArray still inside the completion block call [self.tableView reloadData];
In viewDidLoad method just call your method fetchData
And your cellForRowAtIndexPath should looks like this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// hey please give me the cell to display ... harry up please
// please harry up ! oh my god you are fetching data from server
// while I am asking for the cell !
// ok I don't care do what you want
// I will return an empty cell anyway
// and guess what I will not take in consideration
// the retried data because it's inside a block
// which is called asynchronously
static NSString *cellIdentifier = #"radioCell";
RadioTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (cell == nil) {
cell = [[RadioTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier]; }
// now before return the cell you need to update the content of cell
// maybe you have an array of items and you should update the label
// for example here and then return the cell
cell.usernameLabel = self.myDataArray[indexPath.row]; // example
return cell;
}
Well the TableView is reusing the cells, and you add the image every time a cell is displaid. Thus when reusing the cell you add an other image, but there already is an image.
You will have to reuse the image view, and only add the image if you create the cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifer = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifer];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifer]autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(20,0,30,44)];
imageView.tag = 1001;
[cell addSubview:imageView];
[imageView release], imageView= nil;
}
TabBarTestAppDelegate *delegate = (TabBarTestAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *local = delegate.myData;
// ok, it's horrible, don't look at it :-)
cell.textLabel.text = [NSString stringWithFormat:#"%#%#", #" " ,[local objectAtIndex:indexPath.row]];
//
NSString* name = nil;;
if (indexPath.row == 0) {
name = #"topicon";
}
else if (indexPath.row + 1 == [local count]) {
name = #"bottomicon";
}
else {
name = #"innericon";
}
UIImageView *imageView = (UIImageView *)[cell viewWithTag:1001];
imageView.image = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:name ofType:#"png"]];
return cell;
}
I have a UICollectionView in which I am loading multiple images into. From what Ive been reading, in order to match the correct image to each cell I need to subclass UIImageView and get the image there. Because every time I collectionView reloadData, some images duplicate and they are all out of order. But I am unsure how to do this and haven't found any tutorials. I am using Parse for a database.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
albumImageCell *cell = (albumImageCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[albumImageCell alloc]init];
}
PFObject *temp = [_dataArray objectAtIndex:indexPath.row];
PFFile *file = [temp objectForKey:#"imageThumbnail"];
if (![cell.hasImage isEqualToString:#"YES"]) {
dispatch_async(imageQueue, ^{
NSData *data = [file getData];
if (data) {
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^(void){
cell.imageView.image = image;
cell.hasImage = #"YES";
});
}
});
}
return cell;
}
One way to solve this is to re-query the collection view for the cell again once you're back on the main queue. This code should work:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
albumImageCell *cell = (albumImageCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[albumImageCell alloc]init];
}
PFObject *temp = [_dataArray objectAtIndex:indexPath.row];
PFFile *file = [temp objectForKey:#"imageThumbnail"];
if (![cell.hasImage isEqualToString:#"YES"]) {
dispatch_async(imageQueue, ^{
NSData *data = [file getData];
if (data) {
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^(void){
// cellAgain will be the actual cell at that index path, if it is visible.
// If it is not visible, cellAgain will be nil.
albumImageCell *cellAgain = [collectionView cellForItemAtIndexPath:indexPath];
cellAgain.imageView.image = image;
cellAgain.hasImage = #"YES";
});
}
});
}
return cell;
}
I made a small 'tutorial' in answer to a this question. Although the question refers to Core Data, my answer applies to any data source so you should be able to fit it around your use case.
One thing you want to watch out for is the inner block, when you get back onto the main queue. Given that you have no idea how long it takes to get to that point, the cell may no longer be relevant to that image (could have been reused), so you need to do a couple of additional checks...
(a) is the image still required?
if ([[tableView indexPathsForVisibleRows] containsObject:indexPath])
(b) is that cell is the correct cell for the image?
UITableViewCell * correctCell = [self.tableView cellForRowAtIndexPath:indexPath];
Although this tutorial is still valid, I tend to abstract things further these days. As the viewController has to deal with thread-unsafe entities like UIKit and Core Data, it is a good idea to keep all viewController code on the main thread. Background queue abstractions should take place at a lower level, preferably in the model code.
What I ended up doing was subclassing UIImaveView and then passing the image file in cellForRow
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
albumImageCell *cell = (albumImageCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[albumImageCell alloc]init];
}
PFObject *temp = [_dataArray objectAtIndex:indexPath.row];
PFFile *file = [temp objectForKey:#"imageThumbnail"];
[cell.imageView setFile:file];
return cell;
}
And then in the customImageView -
- (void) setFile:(PFFile *)file {
NSString *requestURL = file.url; // Save copy of url locally (will not change in block)
[self setUrl:file.url]; // Save copy of url on the instance
self.image = nil;
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:data];
if ([requestURL isEqualToString:self.url]) {
[self setImage:image];
[self setNeedsDisplay];
}
} else {
NSLog(#"Error on fetching file");
}
}];
}
But this gets Data every time the user scrolls to a new cell. So Im still trying to figure out how to match a particular image to a cell, without getting data every time.
I am trying to download images in the UITableViewCell using multi-thread. However, when I using the simulator to see the results, the images can not be loaded until I scroll the table view.
I have watched many examples and tutorials in StackOverFlow, but it still doesn't work at all. Actually, I download the images from the Flickr server and stored them into a cached dictionary. But I could still load the images for the first time, unless I scroll the table view and the images start to appear.
And here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
NSString *cellIdentifier = [NSString stringWithFormat:#"cell%ld", (long)indexPath.row];
NSDictionary *photo = self.recentPhotos [indexPath.row];
NSString *title = [photo valueForKeyPath:FLICKR_PHOTO_TITLE];
cell.textLabel.text = title;
[cell.textLabel setFont:[UIFont fontWithName:#"OpenSans" size:18]];
if ([self.cashedImages objectForKey:cellIdentifier] != nil) {
cell.imageView.image = [self.cashedImages objectForKey:cellIdentifier];
}
else
{
dispatch_queue_t queue = dispatch_queue_create("fetch photos", 0);
dispatch_async(queue, ^{
NSURL *imageURL = [FlickrFetcher URLforPhoto:photo format:FlickrPhotoFormatSquare];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];
dispatch_async(dispatch_get_main_queue(), ^{
if ([tableView indexPathForCell:cell].row == indexPath.row) {
[self.cashedImages setValue:image forKey:cellIdentifier];
cell.imageView.image = [self.cashedImages valueForKey:cellIdentifier];
}
});
});
}
return cell;
}
For this task probably it is better to use a library. Usually I use the SDWebImage which is very easy to use, and handle image cashing also.
https://github.com/rs/SDWebImage
One example could be:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] ;
}
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
return cell;
}
EDIT:
If you want to download all the images first and cache them you shouldn't handle this in a tableviewCell. TableViewCells will start the download, when it is created -> when they become visible.
SDWebImageView cashes the images that has been downloaded before so try to add this code in the viewDidLoad method:
for (NSURL* url in self.recentPhotos) {
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:url options:SDWebImageContinueInBackground progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
//Your code if needed
}];
}
SDWebImage caches the images based on their absolute url path.
Note! GDImageLoader now (2016) has full, awesome, Swift version. It is maintained on an almost daily basis - it's really perhaps the single most critical and basically perfect library in iOS. Until Apple sensibly just include caching, it's basically a must-use library.
GDImageLoader is incredibly simple, solid - it's the best ..
https://github.com/AndreyLunevich/DLImageLoader-iOS/tree/master/DLImageLoader
No manual, no learning curve - one command. Totally fantastic.
-(UICollectionViewCell *)collectionView:(UICollectionView *)cv
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger thisRow = indexPath.row;
MeetingsCell *cell;
cell = [cv dequeueReusableCellWithReuseIdentifier:
#"CellPlayersB2" forIndexPath:indexPath];
cell.layer.shouldRasterize = YES; // TYPICALLY NEEDED ON iPhone6+
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
NSDictionary *mtg = CLOUD.players[thisRow];
NSString *hostText = mtg[#"host"]; etc...
cell.whenDescrip.text = etc ...;
NSString *imUrl = mtg[#"image"];
__weak UIBookView *loadBooky = cell.booky;
[DLImageLoader loadImageFromURL:imUrl
completed:^(NSError *error, NSData *imgData)
{
if (loadBooky == nil) return;
[loadBooky use:[UIImage imageWithData:imgData]];
}];
return cell;
}
Possibly the table doesn't redraw it's content. One fix could be to save the image in self.cashedImaged (you know that a typo, right ;) ) and then instruct the UITableView to reload the single cell using reloadRowsAtIndexPaths:
I think it will be better to add a Category on UIImageView and add a method something link this:
#interface UIImageView (Caching)
- (void)setImageWithFlickrURL:(NSURL *)url;
#end
And put your caching logic there.
And then in your cellForRow:indexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
NSURL *imageURL = [FlickrFetcher URLforPhoto:photo format:FlickrPhotoFormatSquare];
[cell.imageView setImageWithFlickrURL:imageURL];
return cell;
}
Or, you can just use caching mechanism that provides AFNetworking's UIImageView+AFNetworking
- (void)setImageWithURL:(NSURL *)url;
You need to add one line:
cell.imageView.image = [self.cashedImages valueForKey:cellIdentifier];
//add this line to make tableview redraw
[cell setNeedsLayout];
Say I have 60-70 UIImageViews and I want to dynamically load the same image into all of them at the same time (so to speak).
For example, if I were working on a web page with 60-70 divs and I wanted to give them all a background image I would give them all a class of myDivs and call `$('.myDivs').css('background-image', 'url(' + imageUrl + ')');
I know how to set a UIImageView's image but is there an easy way to set a bunch at once in Objective C?
I did try searching but I was flooded with a ton of stuff that is really unrelated.
It depends on the way you wish to display the imageView(s).
If you are using a UITableView or a UICollectionView, in the cellForRowAtIndexPath: method you can dynamically update an imageView placed in a cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// Configure the cell...
[self setCell:cell forIndexPAth:indexPath];
return cell;
}
- (void)setCell:(UITableViewCell *)cell forIndexPAth:(NSIndexPath *)indexPath
{
__weak UITableViewCell *weakCell = cell;
// 5. add picture with AFNetworking
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"www.facebook.com/profileImageLocation"]];
[cell.profileImage setImageWithURLRequest:request
placeholderImage:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakCell.profileImage
.image = image;
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"bad url? %#", [[request URL] absoluteString]);
}];
}
Another option will be using a for loop like this:
- (void)addImagesToAllMyImageViews:(NSArray *)images
{
for (id obj in images) {
if ([obj isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)obj;
imageView.image = [UIImage imageNamed:#"someImage.png"];
}
}
}
I think you can do with tag property, select all ImageView and give them a teg like 777 and
for(id* subview in self.view.subview) {
if([subview isKindaOfclass:[UIImageView class]] && (subview.tag == 777)) {
UIImageView* imageView = (UIImageView*)subview;
imageVIew.image = youImage;
}
}
hope this helps you