I have a table that I am populating with information stored in Runner Objects. Each Runner object has a property called fileName that corresponds to an image saved in the document directory.
If I start with an empty table, I can add runners and their photos just fine until I reach about 8 runners. When I try to add a photo for the 8th or so runner, the app crashes. It seems to be a memory issue, but I don't see how this can be when all I'm storing locally are references to images in the directory.
I pass a runner object to my set up cell method in my RunnerCell class and set up the cell like so:
if (currentRunner.fileName != nil) {
if ([manager fileExistsAtPath:fullPath]){
UIImage *originalImage = [[UIImage alloc] initWithContentsOfFile:fullPath];
if ([currentRunner.photoOrientation isEqual: #"portrait"]) {
CGImageRef imageRef = [originalImage CGImage];
UIImage *rotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationRight];
self.runnerImageView.image = rotatedImage;
} else {
self.runnerImageView.image = originalImage;
}
self.addPhotoLabel.hidden = true;
}
}
As per documentation states that you mustn't load image to a UITableViewCell on main thread. That is why your app is crashing.
Because when you load image on main thread it became too much laggy when you scroll because dequereusable cell reassign cell and again load image for upcoming indexpath's cell and until the image loads it block the main thread which cause laggy behaviour of tableview.
Please load your cached image in a background thread. use this code.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
if ([currentRunner.photoOrientation isEqual: #"portrait"]) {
CGImageRef imageRef = [originalImage CGImage];
UIImage *rotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationRight];
dispatch_sync(dispatch_get_main_queue(), ^(void) {
self.runnerImageView.image = rotatedImage;
});
} else {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
self.runnerImageView.image = originalImage;
});
}
});
Related
I have a scroll view with many UIViews, and these views have UIImageViews-that i load images into, while scrolling.
So every page has 4 of these views,that are preloaded to scroller at start, and i load the next page images while scrolling.
I do that in another thread , and still, scroller not entirely scrolls fast, if you look good it has some tiny flicks .
here is the function that loads them :
-(void)loadMainImageToIndex:(int)index
{
NSDictionary *dic=[mainData objectAtIndex:index];
NSString *userImageUrl=[dic objectForKey:#"url"];
NSURL *userUrl=[NSURL URLWithString:userImageUrl];
[self downloadImageWithURL:userUrl completionBlock:^(BOOL succeeded, NSData *tdata)
{
if (succeeded)
{
//load back
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
{
UIImage *image=[UIImage imageWithData:tdata scale:0.25];
if (image)
{
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
[image drawAtPoint:CGPointZero];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
dispatch_async(dispatch_get_main_queue(), ^
{
UIImageView *view=[mainImagesArray objectAtIndex:index];
});
}
}];
The url is loaded Asynch.
Turned out that the views shadow cause this thing :
//self.layer.shadowOffset=CGSizeMake(6.0, 6.0);
//self.layer.shadowColor=[UIColor blackColor].CGColor;
//self.layer.shadowRadius = 3;
//self.layer.shadowOpacity = 0.7;
Removing this solves the problem.
self.layer.shouldRasterize = YES; //solve the problem,
I am writing a multithreaded application which needs to upload photos from the ALAssetsLibrary en masse in the background. So I have an NSOperation subclass which finds the appropriate ALAsset via the asset's URL and adds the image to an upload queue.
In the upload queue for the current ALAsset, I need to get the metadata from the image, but I've encountered a problem: both the -metadata and the -fullResolutionImage methods never return when they are called on the ALAssetRepresentation of the ALAsset. They simply hang there indefinitely. I tried printing the value of each of these methods in LLDB, but it hung the debugger up, and I ended up killing Xcode, signal 9 style. These methods are being called on a background queue.
I am testing these on an iPad 2. This is the method in which the ALAsset is added to the upload queue when it is found in the success block of -assetForURL:resultBlock:failureBlock:
- (void)addMediaToUploadQueue:(ALAsset *)media {
#autoreleasepool {
ALAssetRepresentation *defaultRepresentation = [media defaultRepresentation];
CGImageRef fullResolutionImage = [defaultRepresentation fullResolutionImage];
// Return if the user is trying to upload an image which has already been uploaded
CGFloat scale = [defaultRepresentation scale];
UIImageOrientation orientation = [defaultRepresentation orientation];
UIImage *i = [UIImage imageWithCGImage:fullResolutionImage scale:scale orientation:orientation];
if (![self isImageUnique:i]) return;
NSDictionary *imageDictionary = [self dictionaryForAsset:media withImage:i];
dispatch_async(self.background_queue, ^{
NSManagedObjectContext *ctx = [APPDELEGATE createManagedObjectContextForThread];
[ctx setUndoManager:nil];
[ctx performBlock:^{
ImageEntity *newImage = [NSEntityDescription insertNewObjectForEntityForName:#"ImageEntity"
inManagedObjectContext:ctx];
[newImage updateWithDictionary:imageDictionary
inManagedObjectContext:ctx];
[ctx save:nil];
[APPDELEGATE saveContext];
dispatch_async(dispatch_get_main_queue(), ^{
[self.fetchedResultsController performFetch:nil];
});
if (!currentlyUploading) {
currentlyUploading = YES;
[self uploadImage:newImage];
}
}];
});
}
}
I had a similar problem and I was tearing my hair out trying to figure it out.
Turns out while I had thought I setup a singleton for ALAssetsLibrary, my code was not calling it properly and some ALAssets were returning an empty 'fullResolutionImage'
In all of my NSLogs I must have missed the most important message from Xcode:
"invalid attempt to access ALAssetPrivate past the lifetime of its owning ALAssetsLibrary"
Follow this link
http://www.daveoncode.com/2011/10/15/solve-xcode-error-invalid-attempt-to-access-alassetprivate-past-the-lifetime-of-its-owning-alassetslibrary/
I hope that helps
I have a tableview which runs some code for fetching a UIImage from NSData in a background thread, then in the foreground it calls [button setImage:image forState:UIControlStateNormal];.
The problem is that it freezes the UI for a split second on that line, until it's complete. This means that the UI freezes while it sets it as the image. Because the user is scrolling, this is very noticeable and jittery. Is there any way to avoid my UI from freezing like this?
dispatch_queue_t cellSetupQueue = dispatch_queue_create("Setup", NULL);
dispatch_async(cellSetupQueue, ^{
UIImage *image = [self.mediaDelegate largeThumbnailForMediaAtIndex:indexPath.row];
dispatch_async(dispatch_get_main_queue(), ^{
[self.thumbnailButton setImage:image forState:UIControlStateNormal];
});
});
dispatch_release(cellSetupQueue);
I'm guessing your images are JPGs. If so then UIImage will defer decoding the actual image until the first time it actually needs those pixels. Check out the ImageIO framework for more precise control on this. Here's a snippet I use:
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); // data is your image file NSData
if (source)
{
NSDictionary *dict = #{ (__bridge NSString *)kCGImageSourceShouldCache : #(YES) };
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)dict);
if (cgImage)
{
CFRelease(source);
return cgImage; // you can keep this reference so you dont have to decode again later
}
}
The important part is the kCGImageSourceShouldCache option. You can run this code on the background thread and pass the decoded image to the UI thread.
In my app, I have a root view which is a gridview. I am loading an images in the gridview cells.I am having problem with the loading images. As soon as I launch the app, there will be blank cells with out images after a second the images are loading into the cells. Is there any way to solve this issue. The below is my code.
UIImage *defaultImage = [UIImage imageNamed:#"addEmployee.png"];
if (!employee.imageName) {
return defaultImage;
}
if ([_imageCache objectForKey:employee.imageName]) {
return [_imageCache objectForKey:employee.imageName];
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
UIImage *image = [UIImage imageWithContentsOfFile:[self imagePath:employee.imageName]];
dispatch_async(dispatch_get_main_queue(), ^ {
if (image) {
[_imageCache setObject:image forKey:employee.imageName];
cell.imageView.image = image;
} else {
[_imageCache setObject:defaultImage forKey:employee.imageName];
cell.imageView.image = defaultImage;
return;
}
});
});
return cell.imageView.image;
}
Loading images takes time, there is no way to make it instant. Your code seems sensible, you are loading the images off the main thread so it will not block the UI, and caching them once loaded. The only way to make it faster is to load less images or make the images smaller.
I have WebView where I load content of webarchive. In the same view I have IKImageView outlet. Image drag n drop from web view onto image view doesn't work for me.
What is weird, it works when I drag photo e.g. from iPhoto onto the same image view.
Also, I can drag image from my web view onto NSScrollView (which creates a link to the image) and I can drag the same photo onto a new Mail message (created an image as expected).
IKImageView has "Supports Drag and Drop" enabled in the IB.
What am I missing here?
IKImageView is probably expecting a pasteboard of NSFilenamesPboardType, how does the webview handle dragging images?
It turned out, that the best way to handle d'n'd in my case is via WebArchivePboardType.
Then:
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
NSPasteboard *pboard;
NSDragOperation sourceDragMask;
sourceDragMask = [sender draggingSourceOperationMask];
pboard = [sender draggingPasteboard];
// Create image data from webarchive stored in a pasteboard.
NSData *image = [pboard dataForType:WebArchivePboardType];
WebArchive *webArchive = [[WebArchive alloc] initWithData:image];
// Let's see what are we dragging.
for (WebResource *subresource in [webArchive subresources])
{
NSString *mimeType = [subresource MIMEType];
if ([mimeType hasPrefix:expectedMimeTypeStartsWith])
{
NSData *data = [subresource data];
CFDataRef imgData = (CFDataRef)data;
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData (imgData);
CGImageRef image;
if ([mimeType hasSuffix:#"png"])
{
image = CGImageCreateWithPNGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);
}
else if ([mimeType hasSuffix:#"jpeg"])
{
image = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);
}
[self setImage:image imageProperties:nil];
}
}
return YES;
}