Replacing a calloutaccessoryview in mkannotationview - iphone

In my viewForAnnotation delegate, I create a MKAnnotationView with leftCalloutaccessory as a info button. When the info button is tapped, I want to replace it with a UIActivityIndicator. So in the calloutAccessoryTapped delegate, I wrote
view.leftCalloutaccessoryView = [UIActivityIndicator indicatorWithStyle:UIActivityIndicatorWhite];
The callout accessory seems to change, but it seems like the view doesn't get updated immediately.
That's when the callout accessory gets hidden (by tapping another pin) and is re-opened, I see a spinner. But otherwise, I don't.
I tried calling [view setNeedsdisplay] and [view setNeedsLayout] and [view layoutsubviews] as well but in vain.
Any suggestions/pointers?

I would consider removing and re-adding your annotation. It seems a little heavy handed, but might work.

I was having a similar problem -- I needed to display an image for the leftCalloutAccessoryView if there was one associated with the annotation. Try setting it in mapView:didSelectAnnotationView instead of in mapView:viewForAnnotation
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotView
{
...
if(imgURL.length>1){
// set a thread that will load the image
// but don't set the leftCalloutAccessoryView until the image is loaded
dispatch_queue_t downloader = dispatch_queue_create("annotation image downloader",NULL);
dispatch_async(downloader, ^{
NSURL* photoURL = [NSURL URLWithString:imgURL];
NSData* photoData = [NSData dataWithContentsOfURL:photoURL];
UIImage* photoImage = [UIImage imageWithData:photoData];
dispatch_async(dispatch_get_main_queue(), ^{
annotView.leftCalloutAccessoryView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
[(UIImageView *) annotView.leftCalloutAccessoryView setImage:photoImage];
});
});
}
}

Related

Reload own created UITableViewCell

I got a custom UITableViewcell which should load an Image when use that code:
- (void)setImageWithURL:(NSURL *)URL
{
dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(taskQ, ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
imageView.image = image;
NSLog(#"test");
});
}
It loads the Image very fast, so I get the response "test" very fast, but it needs to load IN the tableView. Also if I select the row it loads the Image.
How can I fix this issue?
Thanks
As long as setImageWithURL: is called as an effect of cellForRowAtIndexPath, then it should should show up as you expect... however, you need to call imageView.image = image; on the main thread. This goes for ALL UI related tasks. So you'll want:
- (void)setImageWithURL:(NSURL *)URL
{
dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(taskQ, ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
NSLog(#"test");
});
});
}
EDIT:
Using NSOperationQueue.
Create an operation queue as a member of your view controller.
m_operationQueue = [NSOperationQueue new];
m_operationQueue.maxConcurrentOperationCount = 1 // if you want to load images in order;
Make sure you cancel operations when the view unloads and when the table will refresh using
[m_operationQueue cancelAllOperations];
Do your thing in setImageWithURL
- (void)setImageWithURL:(NSURL *)URL
{
[m_operationQueue addOperationWithBlock:^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
NSLog(#"test");
});
}];
}
EDIT again:
Sorry, forgot that setImage was in your custom table cell. You can either:
1) make it a static object that you initialize in the +(void)initialize; method of your cell
2) make it a static object available via class method on your view controller
2) make a singleton instance that is available to your table cell
The design preference is up to you and depends what other practices you've been using if you want to be consistent.
You should call [tableView reloadData] when you finish downloading the image.
There is a similar question with an accepted answer, but also it has a suggestion to use SDWebImage. Take a look: How to get UITableViewCell images to update to downloaded images without having to scroll UITableView

Getting an image from a remote server

In my iPhone app I have to show the preview of the thumbnail image. That preview image actually we will get from remote server. Before loading that big image on screen I have to show preloading view but actually this preloading view is not appearing on the screen.
The code I used is:
zoomview=[[UIView alloc]initWithFrame:CGRectMake(0,0,320,460)];
imageloadview.backgroundColor=[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5];
[self.view addSubview:imageloadview];
[activity startAnimating];
[self loadimageview];
Here instead of loading the zoom view on screen this loading view method is executing but I want to display preloading view before getting the big image from the server.
-(void)loadimageview
{
imageloader.image=[UIImage imageNamed:#""];
[self loadimage];
}
-(void)loadimage
{
NSData *data=[NSData dataWithContentsOfURL:[NSURL URLWithString:[picfullarray objectAtIndex:0]]];
if([data length]==0)
{
NSLog(#"data");
}
else
{
UIImage *image1=[UIImage imageWithData:data];
imageloader.image=image1;
[activity stopAnimating];
[loadlabel1 setText:#""];
}
}
How do I show preloaded view on iPhone screen before getting the big image from the server?
You have to load the image asynchronously with NSURLRequest.
Make the class implement NSURLRequestDelegate protocol. In the function - (void)connectionDidFinishLoading:(NSURLConnection *)connection of NSURLRequestDelegate, add the code to update the view when the loading is completed.
// You must declare NSMutableData somewhere to write received data in delegate method
// - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
// I assume the instance of NSMutableData is named data
// If you want to load multiple images, it is a bit tricky, but doable.
// I'll post the code if that is what you need.
- (void) connectionDidFinishLoading: (NSURLConnection *) connection {
// You may want to declare a instance member to store the image (UIImage*), so that you can restore the view if the view is unloaded due to memory warning.
UIImage* image = [UIImage imageWithData: data];
data = nil; // Important. You should clean the data, or it will take up space.
// You may want to check whether image != nil, since the incoming data may not be image
[self displayImage: image];
}
- (void) displayImage: (UIImage*) aImage {
imageloader.image = aImage;
// Depending on how UIImageView is initialized, you may need to call sizeToFit.
// Or set the frame accordingly
[activity stopAnimating];
[loadlabel1 setText: #""];
}
I'd suggest using SDWebImage framework. It already has async image loading capability, and it's super easy to use.
[imageView.image setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
You don't have to worry about messing with NSURLConnection, maintaining NSData, etc..
There is also AFNetworking which has a way to do this easily.
https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking
Check out the "Download and Display Images" section.

uiimageview.image memory leak

In my applicayion i am using a uiimageview ,and it will load diffrent images on a button click. But there is memory leak when i load images, is that needed to release uiimageview.image property before i load another image to it. Any help please...........
code for loading images to uiimageview
-(void)setOverlayImage:(UIImage *)img
{
overlayView.image=nil;
overlayView.image=img;
}
Before i do overlayView.image=img; i hope the memory allocated for the previous image will be replaced with the new image.
Or is that needed to do [overlayView.image release] and then overlayView.image=img;???????
But when i tried to release, the app crashed.
-(void)setOverlayImage:(UIImage *)img
{
overlayView.image=img;
}
This should be sufficient but you can also go for this.
-(void)setOverlayImage:(UIImage *)img
{
if(overlayView)
{
[overlayView release];
overlayView = nil;
}
overlayView = [[UIImageView alloc] initWithImage:img];
overlayView.frame = yourFrame;
// Add this to your parent view
[self.view addSubview:overlayView];
}
Hope this helps

How to know when UIimageView finished loading?

In my view controller, how can I know when a certain UIImageView has finished loading (large jpeg from documents directory)? I need to know so that I can then swap a placeholder low-res imageview with this hi-res imageview. Do I need to create a custom callback to know this? Any way is fine.
By the way, here is a snippet of code where I load the image:
NSString *fileName = [NSString stringWithFormat:#"hires_%i.jpg", currentPage];
NSString *filePath = [NSString stringWithFormat:#"%#/BookImage/%#", [self documentsDirectory], fileName];
hiResImageView.image = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
UIImageView isn't doing any loading at all. All the loading is being done by [[UIImage alloc] initWithContentsOfFile:filePath], and your thread is blocked while the file is loaded (so the load is already complete by the time that call finally returns).
What you want to do is something like this:
- (void)loadImage:(NSString *)filePath {
[self performSelectorInBackground:#selector(loadImageInBackground:) withObject:filePath];
}
- (void)loadImageInBackground:(NSString *)filePath {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
[self performSelectorOnMainThread:#selector(didLoadImageInBackground:) withObject:image waitUntilDone:YES];
[image release];
[pool release];
}
- (void)didLoadImageInBackground:(UIImage *)image {
self.imageView.image = image;
}
You would set up self.imageView to display the low-res image and then call loadImage: to load the high-res version.
Note that if you call this repeatedly before didLoadImageInBackground: gets called from earlier calls, you may cause the device to run out of memory. Or you might have the image from the first call take so much longer to load than image from the second call that didLoadImageInBackground: gets called for the second image before it gets called for the first. Fixing those issues is left as an exercise for the reader (or for another question).

How to reload image in UIButton in applicationDidBecomeActive

I have a MenuViewController that loads when the app loads up; it is the root view of a UINavigationController.
Within the view I have a UIButton with an image that loads from a URL, as well as a label (its a picture indicating the weather and the current temp.).
Everything works fine, but if I am using the app on an iPhone 4, with multi-tasking, when the home button is pressed and then the app is reopened, I need the UIButton image and temp. label to reload.
I have tried calling this in my AppDelegate under applicationDidBecomeActive:
[self parseWeatherXML]; //re-parse weather data
[menuViewController reloadWeatherData]; //loads the image/label from the XML
with no luck.
I have also tried releasing menuViewController in applicationWillResign, then reallocating menuViewController in applicationDidBecomeActive so the viewDidLoad method gets called again, both ways just end up crashing my app.
Any help appreciated!
EDIT
Heres my method that gets called with the notification:
- (void)reloadWeather
{
[self parseWeatherXML];
UIImage *img = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString:iconURL]]];
if (img != nil)
{
NSLog(#"setting image");
[weatherButton setImage:img forState:normal];
img = nil;
}
currentTemp = [currentTemp stringByAppendingString:#"°F"];
[tempLabel setText:currentTemp];
tempLabel.adjustsFontSizeToFitWidth = YES;
if([self isIPad])
{
tempLabel.textAlignment = UITextAlignmentRight;
[tempLabel setFont: [UIFont systemFontOfSize: 45.0]];
}
currentTemp = nil;
}
I would have your MenuViewController become an observer to the UIApplicationDidBecomeActiveNotification and perform the parseWeatherXML data. I would think you would be refreshing the data from the URL, so you would need to wait for that data to come back again. So, I would follow the same logic that you are doing when you receive that notification.
UIApplicationDidBecomeActiveNotification
Posted when the application becomes
active. An application is active when
it is receiving events. An active
application can be said to have focus.
It gains focus after being launched,
loses focus when an overlay window
pops up or when the device is locked,
and gains focus when the device is
unlocked.
Availability
Available in iOS 2.0 and later.
Declared In
UIApplication.h
Have you tried saving the image and temp using a plist or something, during the applicationWillResign, and then during applicationDidBecomeActive, you can reload the image and temp from the saved file/label. Just another option to explore.