How would I get my image to occupy the whole image view? - ios

I have an image view with constraints set up, and the image that I load in (from a URL) doesn't fill the whole image view for some reason. I included a picture of the one in question followed by the detail view of the same 'post'. The same code is used for both. In the detail view (one that appears correctly), I set the post image in during viewdidload, and it's a standalone view. The one that loads in wonky is a table view cell with a custom class, and the image is set in the cellForRowAtIndexPath. I do resize the image before I send it to Back4App with parse to 500x500, and the image views are both set to clip to bounds and aspect fit. the uiimageview constraints are exactly the same, with horizontal alignment of zero, a fixed width and height, and constraints for all four sides (8 away from nearest neighbors)
here are code snippets for how both of them are done:
pink background:
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
PostCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"PostCell"];
Post *post = self.posts[indexPath.row];
PFUser *user = post.author;
[user fetchInBackgroundWithBlock:^(PFObject * _Nullable object, NSError * _Nullable error) {
cell.usernameLabel.text = post.author.username;
}];
cell.titleLabel.text = post.caption;
cell.isLiked = NO;
NSURL *imageURL = [NSURL URLWithString:post.image.url];
[cell.imageView setImageWithURL:imageURL];
return cell;
}
one that loads correctly:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
PFUser *user = self.post.author;
[user fetchInBackgroundWithBlock:^(PFObject * _Nullable object, NSError * _Nullable error) {
self.usernameTextLabel.text = self.post.author.username;
}];
self.titleTextLabel.text = self.post.caption;
self.descriptionTextLabel.text = self.post.bodyText;
NSURL *postImageURL = [NSURL URLWithString:self.post.image.url];
[self.postImageView setImageWithURL:postImageURL];'''
}
Any tips? There may be some basics that I don't have a handle on - i took a crash course basically and I've only been at this for about 3 weeks so I don't understand a lot of this as deeply as I wish I did.
I've set the background color to pink so you could see the image size vs image view
one that loads correctly
side note: i literally can't figure out how to do blocks of code correctly??? i'm following what it says lol

Check following points
Debug the frame size of the ImageView that it is correct which you supplied.
Set following to the ImageView
[cell.imageView setImageWithURL:imageURL];
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
[cell.imageView setClipsToBounds:YES];

I was accidentally using the default imageView that the cell comes with instead of my own outlet to my new image view.

Related

Multiple Times load of image from API in ImageView in IOS

I have made a custom Tableview which has an image view and some labels that takes values from API.
It's a custom table in which images and labels recieve data from API related to real estate .
When my custom table loads, values start coming from API.
The actual issue ocurr in image view . I have set a local image in an image view , when the data coming from API has no image than it should show local image otherwise data should show its original image submitted by customer in the forms.
Now when data start coming from API first in image view it shows local image than when images start coming from API it start showing that but when we scroll down in image view it shows same images in image view and repeats many times and we cannot identify that which is original image.
I can provide the code if anyone sent me his email , he can get all the senario of work.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier=#"Cell";
CustomTableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
StringData* cust = [_Title objectAtIndex:indexPath.row];
cell.Title.text = cust.title1;
cell.area.text=cust.Area;
cell.city.text=cust.City;
cell.phone.text=cust.phoneno;
cell.price.text=cust.Price;
cell.location1.text=cust.location;
cell.name1.text=cust.dealer_name;
cell.email1.text=cust.dealer_email;
cell.type1.text=cust.property_type;
cell.status1.text=cust.status;
cell.room1.text=cust.rooms;
cell.washrooms.text=cust.bath;
cell.floor1.text=cust.floors;
cell.imagetext.text = cust.images;
tii=cell.Title.text;
NSLog(#"Tittttttle is %#",tii);
NSURL *url = [NSURL URLWithString:cust.images];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.images1.image = image;
});
}
}
}];
[task resume];
return cell;
}
It seems you don't store an image into local application memory. You tried to load image on every time when cell is appear. So you download image every time.
Best Solution:
You need to store image local memory using SDWebImage.
Objective-C:
#import <SDWebImage/UIImageView+WebCache.h>
...
[imageView sd_setImageWithURL:[NSURL URLWithString:#"IMAGE_URL"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Swift:
import SDWebImage
imageView.sd_setImage(with: URL(string: "IMAGE_URL"), placeholderImage: UIImage(named: "placeholder.png"))
If you are loading an image for the first time, then it will download and store an image into local memory. So from second times, it will load from local itself.
Well, its because your cell is getting re-used and the previous image is being displayed. You can override prepareForReuse() method of your tableviewcell subclass or consider setting placeholder for the url image using SDWebImage
Edit: Explaination
You are downloading your image asyc and setting it to your imageview of your tableviewcell, what is happening here is while your next image is being downloaded, before that your cell is re-used and displays previous image.

UITableViewCell Image gets Larger when selected

I'm working on an app that includes some functionality that shows a list of photo albums. I'm using a vanilla UITableView and vanilla UITableViewCells. I'm using a Subtitle Style UITableViewCell and setting a thumbnail image on cell.imageView.image. Note: This is not a custom cell, it's the built-in cell imageView.
The problem is that when I tap a cell, the imageView gets LARGER, which then looks weird, because a) that's not intuitive behavior and b) it no longer matches up with every other row.
Before the selection, the cell's imageView frame is:
cell.imageView.frame: {{16, 1}, {84, 84}}
After the selection, the cells' imageView frame is:
cell.imageView.frame: {{16, 0}, {88, 87.5}}
I don't have any code involved, this is a vanilla UITableViewCell.
The only code I have that interacts with the imageView is the PhotoKit code which I use to fetch the photo asset and update the cell:
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.resizeMode = PHImageRequestOptionsResizeModeExact;
[self.imageManager requestImageForAsset:asset
targetSize:AssetGridThumbnailSize
contentMode:PHImageContentModeAspectFill
options:options
resultHandler:^(UIImage *result, NSDictionary *info) {
// Only update the thumbnail if the cell tag hasn't changed.
// Otherwise, the cell has been re-used.
if (cell.tag == currentTag) {
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
cell.imageView.image = result;
}
}];
Why is this happening? How can I stop it?
your custom UITableViewCell have a outlet with name "imageView" which cause the problem. remove that outlet and the problem is fixed
i find when you set the UIImageView.ContentMode = UIViewContentModeScaleAspectFill
if you request a web image ,and download has finish and callback,the imageview will be change it's frame
you can try this method
[img setClipsToBounds:YES];

Loading images asynchronously in UITableView with Autolayout

I have a UITableView which loads images asynchronously in a UITableView. As all the images are different heights, I set a height constraint according to the height of the image.
This works fine when I use DataWithContentsOfUrl, however freezes the UI. When I load asynchronously, the images pile up on top of each other like so:
http://i.stack.imgur.com/TEUwK.png
The code i'm using is as follows:
NSURL * imageURL = [NSURL URLWithString:img];
NSURLRequest* request = [NSURLRequest requestWithURL:imageURL];
cell.imageViewHeightConstraint.constant=0;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * response, NSData * data, NSError * error) {
if (!error){
UIImage *imgz = [UIImage imageWithData:data];
[cell.CellImg setBackgroundImage:imgz forState:UIControlStateNormal];
[cell.CellImg sizeToFit];
cell.imageViewHeightConstraint.constant=result.width*(imgz.size.height/imgz.size.width);
[cell setNeedsUpdateConstraints];
}
}];
[cell updateConstraints];
When I scroll up and down a few times the images correctly position themselves.
The question doesn't specify the desired behavior for varying image sizes. Should the cell get taller to fit, or just the image view (what looks like a button from the code) within the cell?
But we should put aside that problem for a moment and work on the more serious problem with the code: it issues an unguarded network request from cellForRowAtIndexPath. As a result, (a) a user scrolling back and forth will generate many many redundant requests, and (b) a user scrolling a long way quickly will generate a request that's fulfilled when the cell that started it is gone - reused as the cell for another row.
To address (a), the datasource should cache fetched images, and only request those that haven't been received. To address (b), the completion block shouldn't refer directly to the cell.
A simple cache would look like this:
#property(strong,nonatomic) NSMutableDictionary *images;
// initialize this when you initialize your model
self.images = [#{} mutableCopy];
// move the network code into its own method for clarity
- (void)imageWithPath:(NSString *)path completion:(void (^)(UIImage *, NSError *))completion {
if (self.images[indexPath]) {
return completion(self.images[indexPath], nil);
}
NSURL *imageURL = [NSURL URLWithString:path];
NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error){
UIImage *image = [UIImage imageWithData:data];
self.images[indexPath] = image;
completion(image, nil);
} else {
completion(nil, error);
}
}];
}
Now, we fix the multiple request problem by checking first for the image in the cache in cellForRowAtIndexPath.
UIImage *image = self.images[indexPath];
if (image) {
[cell.CellImg setBackgroundImage:image forState:UIControlStateNormal];
} else {
// this is a good place for a placeholder image if you want one
[cell.CellImg setBackgroundImage:nil forState:UIControlStateNormal];
// presuming that 'img' is a string from your mode
[self imageWithPath:img completion:^(UIImage *image, NSError *error) {
// the image is ready, but don't assign it to the cell's subview
// just reload here, so we get the right cell for the indexPath
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
}
Also notice what isn't done in the completion block... we're fixing the cell reuse by not referring to the cell. Instead, knowing that the image is now cached, we reload the indexPath.
Back to image sizing: most apps like to see the table view cell get taller or shorter along with the variable height subview. If that's the case, then you should not place a height constraint on that subview at all. Instead, constrain it's top and bottom edges to the cell's content view (or include it in a chain of subviews who constrain to each other top and bottom and contain the topmost and bottommost subviews top and bottom edge to the cell). Then (in iOS 5+, I think), this will allow your cell to change hight with that subview constraint chain...
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = // your best guess at the average height here
Try the following along with constraint constant:
cell.imageViewHeightConstraint.constant=result.width*(imgz.size.height/imgz.size.width);
[table reloadRowsAtIndexPaths:#[indexPathForCurrentCell] withRowAnimation:UITableViewRowAnimationNone];
You can also set the animation.
Although reloadRowsAtIndexPaths works, it ends up causing the interface to be slow and laggy, especially when the user is scrolling. My solution was once the data was loaded to iterate through https://graph.facebook.com/v2.1/%#/?access_token=%#&fields=attachments for the object id, and then store the image height dimensions in an array. Then, in cellForRowAtIndexPath simply setting cell.imageViewHeightConstraint.constant to the value in the array. That way all the cells heights are set as soon as the cell loads, and the image fits into place perfectly as soon as it loads.

UITableView scrolling is not smooth

I have the smooth scrolling issue at my UITableView with UITableViewCell which contains UIImageView. Similar issues could be found all over the StrackOverflow but none of the proposed solutions helped me to completely get rid of the lag.
My case is quite common:
images are stored at application storage (in my sample at app bundle)
images could have different size (500x500, 1000x1000, 1500x1500)
I need to display those images in UITableView where UIImageView size is 120x120 (retina)
I have followed multiple optimization tips and managed to optimize scrolling a lot.
Unfortunately it is still not perfect. This is my scenario:
first I moved all the image loading/processing/resizing logic to the background thread
UITableViewCell reuse is enabled
once UITableViewCell is in view I clear old values (settings to null) and start background thread to load the image
at this point we are in background thread and I'm adding 500 ms delay to avoid settings new image to often (in case we are scrolling fast) (see below explanation)
if UIImage exists at static image cache (regular dictionary with UIImage instances) - fetch that one and go to the step 9.
if not - load new image from bundle (imageWithName) using url to app bundle (in real world scenario images will be stored to application storage, not bundle)
once image is loaded resize it to 120x120 using graphics context
save resized image to the static image cache
at this point we have instance to UIImage and process is in the background thread. From here we move back to UI Thread with the given image
if data context was cleared (for example UITableViewCell disappeared or was reused to display another image) we skip processing of the currently available image.
if data context is the same - assign UIImage to UIImageView with an alpha animation (UIView.Animate)
once UITableViewCell is out of view - clear the data context
Originally before starting new background thread to fetch the image here (step 1) was UIImage cache check without background thread. In this case if we have the image in the cache we assign it instantly and this introduces a great lag during fast scrolling (we assign images to often as long as we fetch them instantly). Those lines are commented at my example attached below.
There are still two issues:
at some point during scrolling I still have a small lag (at the
moment when I'm assign new UIImage to UIImageView.
(this one is more noticeable) when you tap on item and go back from details there is a lag right before back navigation animation is finished.
Any suggest how to deal with those two issues or how to optimize my scenario are appreciated
Please take into account that sample written in Xamarin but I don't believe that Xamarin is the cause of the problem as long as I have the same issue for the app written in ObjectiveC as well.
Smooth Scrolling Test App
Did you every tried to populate your TableView with only one 120x120 Image which is saved in your Bundle? This way you can check, if the problem occurs of your Image rendering
Instead of resizing all your images to 120x120 and save them in cache, I would recommend creating and using a thumbnail of all your images. You are somehow already doing this, but you are doing this couple of times (everytime you are scrolling or if your cache is full).
In our last project we had a UICollectionView with book covers. Most of the covers were between 400-800kb big and the feeling while scrolling was really bad. So we created a thumbnail for each image (thumbails about 40-50kb) and used the thumbnails instead of real covers. Works like a charm! I attached the thumbnail creation function
- (BOOL) createThumbnailForImageAtFilePath:(NSString *)sourcePath withName:(NSString *)name {
UIImage* sourceImage = [UIImage imageWithContentsOfFile:sourcePath];
if (!sourceImage) {
//...
return NO;
}
CGSize thumbnailSize = CGSizeMake(128,198);
float imgAspectRatio = sourceImage.size.height / sourceImage.size.width;
float thumbnailAspectRatio = thumbnailSize.height/thumbnailSize.width;
CGSize scaledSize = thumbnailSize;
if(imgAspectRatio >= thumbnailAspectRatio){
//image is higher than thumbnail
scaledSize.width = scaledSize.height * thumbnailSize.width / thumbnailSize.height;
}
else{
//image is broader than thumbnail
scaledSize.height = scaledSize.width * imgAspectRatio;
}
UIGraphicsBeginImageContextWithOptions( scaledSize, NO, 0.0 );
CGRect scaledImageRect = CGRectMake( 0.0, 0.0, scaledSize.width, scaledSize.height );
[sourceImage drawInRect:scaledImageRect];
UIImage* destImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSString* thumbnailFilePath = [[self SOMEDIRECTORY] stringByAppendingPathComponent:name];
BOOL success = [UIImageJPEGRepresentation(destImage, 0.9) writeToFile:thumbnailFilePath atomically:NO];
return success;
}
Try facebook's Async Display library.
https://github.com/facebook/AsyncDisplayKit
Really easy to use.. from their guide: http://asyncdisplaykit.org/guide/
_imageNode = [[ASImageNode alloc] init];
_imageNode.backgroundColor = [UIColor lightGrayColor];
_imageNode.image = [UIImage imageNamed:#"hello"];
_imageNode.frame = CGRectMake(10.0f, 10.0f, 40.0f, 40.0f);
[self.view addSubview:_imageNode.view];
This decodes the image on a background thread.
I'm not sure if it's easy to use iOS libraries on Xamarin but if it's easy, give this a shot.
I sub-class Paul Hegarty's CoreDataTableViewController and employ thumbnails of my photos in the CoreDataTableView.
Look for the examples in Lecture 14 titled FlickrFetcher and Photomania. You will also need to download the CoreDataTableViewController at that same link.
Make a CoreData Entity with an appropriate title and define whatever attributes (data variables) you want. You will need to define two "Transformable" attributes, one for the photo and one for the thumbnail.
Then load your thumbnail in the CoreDataTableView:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *exceptions = [NSArray arrayWithObjects:#"SCR", #"DNS", #"NT", #"ND", #"NH", nil];
static NSString *CellIdentifier = #"resultsDisplayCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
MarksFromMeets *athleteMarks = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString* date = [ITrackHelperMethods dateToAbbreviatedString:athleteMarks.meetDate];
NSMutableString *title = [NSMutableString stringWithFormat:#"%#", athleteMarks.markInEvent];
NSMutableString *subTitle = [NSMutableString stringWithFormat:#"%# - %#",date, athleteMarks.meetName];
[title replaceOccurrencesOfString:#"(null)"
withString:#""
options:0
range:NSMakeRange(0, [title length])];
// cell.imageView.image = athleteMarks.photoThumbNail; // Don't like image in front of record.
[cell.textLabel setFont:[UIFont
fontWithName:#"Helvetica Neue" size:18]];
[cell.detailTextLabel setFont:[UIFont fontWithName:#"Helvetica Neue" size:16]];
[cell.detailTextLabel setTextColor:[UIColor grayColor]];
// make selected items orange
if ([athleteMarks.eventPR integerValue] != 0
&& (![exceptions containsObject:athleteMarks.markInEvent])) {
title = [NSMutableString stringWithFormat:#"%# (PR)",title];
[cell.textLabel setTextColor:[UIColor redColor]];
}
else if ([athleteMarks.eventSB integerValue] != 0
&& (![exceptions containsObject:athleteMarks.markInEvent])) {
title = [NSMutableString stringWithFormat:#"%# (SB)",title];
[cell.textLabel setTextColor:[UIColor orangeColor]];
} else {
[cell.textLabel setTextColor:[UIColor grayColor]];
}
cell.textLabel.text = title;
cell.detailTextLabel.text = subTitle;
cell.indentationLevel = indentationLevelOne;
cell.indentationWidth = indentationForCell;
return cell;
}
If you want, I can send you an example of a Category for an Entity's NSManagedObject Sub-Class. This Category loads the photo and the thumbnail into CoreData Entity. The first time will be slow. However, after that the user should be able to scroll through TableView smoothly and then all the updated results will load automatically. Let me know.
One nice thing is that CoreData handles all the memory management.
Good luck!
I don't have enough rep to comment, So here's an answer which helped my tableview scrolling performance:
Make the tableview height larger than the viewable window. Cells will load "off screen" and helps improve scroll smoothness.
Do your image processing in the following method:
-(void)tableView:(UITableView *)tableView
willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
Those two tricks got my table flowing really nice. I'm getting my image data from an API service and AFNETWORKING has an awesome image loader, but not necessary for you since images are in the bundle.
Maybe you could try SDWebImage instead. It is also a xamarin component
which fashions an asynchronous image downloader and asynchronous memory and disk image caching with automatic cache expiration handling. Using it would probably mean throwing away a lot of hard written code, but it might be worth it -plus your code will become a lot simpler. In iOS you can also setup a SDWebImageManager inside the viewDidLoad of a controller:
- (void)viewDidLoad{
...
SDWebImageManager *manager = [SDWebImageManager sharedManager];
manager.delegate = self;
...
}
and set the view controller as the delegate. Then, when the following delegate method is called:
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL
you could scale your images to thumbs of the appropriate size before caching them.
Hope that helps.
Weel I had a similar problem, my scroll was not smooth. I am inserting in the table a variable UIImageView with inside labelViews.
What I did was to change the method HeightforRowAtIndexPath for estimatedHeightforRowAtIndexPath and now scroll is smooth.

UITableView with images crashes app when I scroll down a lot

I have this simple UITableView and each cell has an image corresponding to it. All I'm doing is displaying a title for the image and the image itself in each cell. Here is my cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// this is where the data for each cell is
NSDictionary *dataForThisCell = cachedData.posts[indexPath.row][#"data"];
// this creates a new cell or grabs a recycled one, I put NSLogs in the if statement to make sure they are being recycled, they are.
post *cell = (post *) [self.tableView dequeueReusableCellWithIdentifier:#"postWithImage"];
if (cell == nil) {
cell = [[[NSBundle mainBundle]loadNibNamed:#"postWithImage" owner:self options:nil]objectAtIndex:0];
[cell styleCell];
}
// if this cell has an image we need to stick it in the cell
NSString *lowerCaseURL = [dataForThisCell[#"url"] lowercaseString];
if([lowerCaseURL hasSuffix: #"gif"] || [lowerCaseURL hasSuffix: #"bmp"] || [lowerCaseURL hasSuffix: #"jpg"] || [lowerCaseURL hasSuffix: #"png"] || [lowerCaseURL hasSuffix: #"jpeg"]) {
// if this cell doesnt have an UIImageView, add one to it. Cells are recycled so this only runs several times
if(cell.preview == nil) {
cell.preview = [[UIImageView alloc] init];
[cell.contentView addSubview: cell.preview];
}
// self.images is an NSMutableDictionary that stores the width and height of images corresponding to cells.
// if we dont know the width and height for this cell's image yet then we need to know now to store it
// once the image downloads, and then cause our table to reload so that heightForRowAtIndexPath
// resizes this cell correctly
Boolean shouldReloadData = self.images[dataForThisCell[#"name"]] == nil ? YES : NO;
// download image
[cell.preview cancelImageRequestOperation];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: dataForThisCell[#"url"]]];
[request addValue:#"image/*" forHTTPHeaderField:#"Accept"];
[cell.preview setImageWithURLRequest: request
placeholderImage: [UIImage imageNamed:#"thumbnailLoading.png"]
success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
// if we indicated earlier that we didnt know the dimensions of this image until
// just now after its been downloaded, then store the image dimensions in self.images
// and tell the table to reload so that heightForRowAtIndexPath
// resizes this cell correctly
if(shouldReloadData) {
NSInteger imageWidth = image.size.width;
NSInteger imageHeight = image.size.height;
if(imageWidth > [ColumnController columnWidth]) {
float ratio = [ColumnController columnWidth] / imageWidth;
imageWidth = ratio * imageWidth;
imageHeight = ratio* imageHeight;
}
if(imageHeight > 1024) {
float ratio = 1024 / imageHeight;
imageHeight = ratio * imageHeight;
imageWidth = ratio* imageWidth;
}
self.images[dataForThisCell[#"name"]] = #{ #"width": #(imageWidth), #"height": #(imageHeight), #"titleHeight": #([post heightOfGivenText: dataForThisCell[#"title"]]) };
[self.tableView reloadData];
// otherwise we alreaady knew the dimensions of this image so we can assume
// that heightForRowAtIndexPath has already calculated the correct height
// for this cell
}else{
// assign the image we downloaded to the UIImageView within the cell
cell.preview.image = image;
// position the image
NSInteger width = [self.images[dataForThisCell[#"name"]][#"width"] integerValue];
NSInteger height = [self.images[dataForThisCell[#"name"]][#"height"] integerValue];
cell.preview.frame = CGRectMake( ([ColumnController columnWidth] - width)/2 , [self.images[dataForThisCell[#"name"]][#"titleHeight"] integerValue] + 10, width, height);
}
}
failure: ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {}];
}
// set title of the cell
cell.title.text = [NSString stringWithFormat: #"%#\n\n\n\n\n", dataForThisCell[#"title"]];
`enter code here`// ask for a restyle
[cell setNeedsLayout];
// returns my customized cell
return cell;
}
What happens is that everything works exactly how I want it to, however once I scroll down past around 100 cells or so the background of my app goes black for a few seconds and then I see my homescreen (I've seen some people call this the HSOD - home screen of death). Sometimes in the console in xcode I see memory warnings before a crash and sometimes I do not.
I know for a fact that whatever the problem is, it has to do with putting images into the cells. If I comment out just this line:
cell.preview.image = image;
Then everything works fine and it doesn't crash any more (but then of course the images are not being displayed in the cells).
The cells are being reused and I know that's working, for good measure I set the UIImageView's image property to nil:
- (void) prepareForReuse {
[super prepareForReuse];
if(self.preview != nil)
self.preview.image = nil;
}
and in my appDelegate I also define this:
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[UIImageView clearAFImageCache];
}
Which deletes the image cache but that doesn't fix the problem either (and anyway iOS should clear the image caches upon memory warnings automatically anyway).
I ran analyze on my project and it reports no memory leaks, and here is the profiler showing that, as well as showing the allocations at the time of the crash:
Other than the occasional memory warning in the console which appears about 2/3rds of the time the app crashes, there are no other errors that appear in the console, and I do not hit any breakpoints or exceptions.
All of those allocations are you creating new table view cells each time they're requested, rather than reusing existing ones. Without setting a reuseIdentifier for cells created from UINib, dequeueReusableCellWithIdentifier: will always return `nil.
To fix this, add the following code (as referenced in this question):
[self.tableView registerNib:[UINib nibWithNibName:#"nibname"
bundle:nil]
forCellReuseIdentifier:#"cellIdentifier"];
I've found a solution, albeit not a perfect one:
The AFNetworking library is brilliant and I assume the cause of my problem lies within my own code or my lack of understanding as to how NSCache works.
AFNetworking caches images using Apple's NSCache. NSCache is similar to NSMutableDictionary, but releases objects when memory is spread thin (see more here).
Within UIImageView+AFNetworking.m I located the definition of
+ (AFImageCache *)af_sharedImageCache
And altered it to resemble this:
+ (AFImageCache *)af_sharedImageCache {
static AFImageCache *_af_imageCache = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_af_imageCache = [[AFImageCache alloc] init];
_af_imageCache.countLimit = 35;
});
return _af_imageCache;
}
The important line here is
_af_imageCache.countLimit = 35;
This tells the NSCache object being used in AFNetworking to cache images that it must only cache up to a maximum of 35 things.
For reasons unknown to me, iOS was not automatically purging objects from the cache as it should, and calling removeAllObjects on the cache was not working either. This solution is hardly ideal because it may not utilize the cache to its full potential or may over use the cache, but for the meantime it atleast stops the cache from attempting to store an infinite number of objects.

Resources