How to reload image in UIButton in applicationDidBecomeActive - iphone

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.

Related

Application crashing in the imagePickerController:didFinishPickingMediaWithInfo method inside UIImagePickerController

I am a beginner in iOS development, I tried to make an application to select an image from the UIImagePickerController which is displayed inside a UIImageView object. The problem I am facing is inside this method
-(void)imagePickerController:(UIImagePickerController*)Picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
NSLog(#"Inside dismiss modal view delegate 2");
UIImage * image = [info objectForKey:UIImagePickerControllerOriginalImage];
selectedImage.image = [image retain];
NSLog(#"Inside the did pickerController method");
}
When I select an image from the UIImagePickerController, the image gets selected but the application crashes. I tried debugging, and the first NSLog statement gets displayed on the console, but the 2nd one does not get displayed.
I am trying to load an image from the Photos library inside the simulator. I am using XCode 3.2.5 and the simulator is 4.2. This might men the error is there in between the two NSLog statements. But, I have tried my best to find out the error, but without any success. Can you please help me out.
NSData *dataImage = UIImageJPEGRepresentation([info objectForKey:#"UIImagePickerControllerOriginalImage"],1);
imgPicture.image = [[UIImage alloc] initWithData:dataImage];
[picker dismissModalViewControllerAnimated:YES];
Put this code in method

Adding "Breakpoint" or Pause in iPhone App

I have an app that is continuously taking in images from the video buffer (using the process described here: http://www.benjaminloulier.com/articles/ios4-and-direct-access-to-the-camera) and performing various processing on the most recent image in the buffer. For my particular application, when something noteworthy is found in the image, I want to display this information to the user and have the user decide whether the information is correct or not.
I want to display 2 UIButtons on the screen when this information is returned and it is at this point that I wish the code to "pause" (like a runtime breakpoint) and wait to see which button the user clicks before resuming. After clicking a button, the buttons will disappear.
The reason for this is I can't have the camera continue to acquire images and process them while I am waiting for the user input.
Thanks!
EDIT:
Here is what my code basically looks like:
if (continueRunningScript == YES) {
NSString *results = [self processImage];
[self displayResults: results];
// Show pause button
dispatch_async(dispatch_get_main_queue(), ^{
[pauseButton setHidden: NO];
});
}
and the pause button code:
- (UIAction) pauseButtonPress:(id) sender {
[pauseButton setHidden: YES];
[playButton setHidden: NO];
continueRunningScript = NO;
}
and the play button code:
- (UIAction) playButtonPress:(id) sender {
[playButton setHidden:YES];
continueRunningScript = YES;
}
Where could I add more boolean to handle the delay?

UIImagePickerController Camera Source Problem

Ok here is the code that I wrote to display the UIImagePickerController in the camera source. I just declared the myPhotopicker in the header for the property and retain it. Synthesized it in the main code file. Then calling the picker I wrote the code below:
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
if (myPhotopicker==nil) {
myPhotopicker = [[UIImagePickerController alloc] init];
myPhotopicker.delegate = self;
}// create once!
myPhotopicker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentModalViewController:myPhotopicker animated:NO];
}
Upon calling it, there are a few things that is weird happening to the app.
Sometimes, when there are many apps running in the background (iPhone4), the app would fail to load the camera and crash the app. Though it will load CameraRoll/PhotoAlbums without problem.
If the camera view is able to load (when there are less apps running in the background), tapping the Cancel button on the camera view results in the app rebooting itself (where the Default.png image is shown quickly, and back to the main page - like when we started the app).
I have been trying to figure out this problem, but not sure what to do... pls help.. Thanks.
here is the complete code of image pickercontrol try too find solu. here.
http://www.icodeblog.com/2009/07/28/getting-images-from-the-iphone-photo-library-or-camera-using-uiimagepickercontroller/
Regards,
Shyam Parmar
Rather than your 'create once' logic try creating and releasing each time.
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
myPhotopicker = [[UIImagePickerController alloc] init];
myPhotopicker.delegate = self;
myPhotopicker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentModalViewController:myPhotopicker animated:NO];
[myPhotopicker release];
}
You should also implement the delegate to remove the modal view controller from view when it is dismissed (if you haven't already).
You should also check that the current class conforms to the UINavigationConrollerDelegate protocol.

iPhone - Splash Screen with progress bar

I tried to create a SplashView which display the Default.png in the background and a UIProgressBar in front. But the splash screen is not being updated...
Inside my view controller I load first the splash view with a parameter how many steps my initialisation has and then I start a second thread via NSTimer and after each initialisation step I tell the SplashView to display the new progress value.
All looks good in theory, but when running this app the progress bar is not being updated (the method of the splash screen receives the values, I can see it in the logs). I also tried to add usleep(10000); in between to give the view updates a bit time and also instead of using the progress bar I drew directly on the view and called [self setNeedsDisplay]; but all didn't work :/
What am I doing wrong?
Thanks for your help!
Tom
Here is some code:
SPLASHSCREEN:
- (id)initWithFrame:(CGRect)frame withStepCount:(int)stepCount {
if (self = [super initWithFrame:frame]) {
// Initialization code
background = [[UIImageView alloc] initWithFrame: [self bounds]];
[background setImage: [UIImage imageWithContentsOfFile: [NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"Default.png"]]];
[self addSubview: background];
progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
[progressView setFrame:CGRectMake(60.0f, 222.0f, 200.0f, 20.0f)];
[progressView setProgress: 0.0f];
stepValue = 1.0f / (float)stepCount;
[self addSubview:progressView];
}
return self;
}
- (void)tick {
value += stepValue;
[progressView setProgress: value];
}
VIEWCONTROLLER:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
splashView = [[SplashView alloc] initWithFrame: CGRectMake(0.0f, 0.0f, 320.0f, 480.0f) withStepCount:9];
[self setView: splashView];
NSTimer* delayTimer;
delayTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(finishInitialization) userInfo:nil repeats:NO];
}
return self;
}
- (void)finishInitialization {
// do some stuff, like allocation, opening a db, creating views, heavy stuff...
[splashView tick]; // this should update the progress bar...
// do some stuff, like allocation, opening a db, creating views, heavy stuff...
[splashView tick]; // this should update the progress bar...
// init done... set the right view and release the SplashView
}
As mentioned in another answer, for some finite amount of time, as your app is being launched, Default.png is displayed and you have no control over it. However, if in your AppDelegate, you create a new view that displays the same Default.png, you can create a seamless transition from the original Default.png to a view that you can add a progress bar to.
Now, presumably, you have created a view or similar and you are updating a progress bar every so often in order to give the user some feedback. The challenge here is that your view is only drawn when it gets called to do a drawRect. If, however, you go from AppDelegate to some initialization code to a viewcontroller's viewDidLoad, without the run loop getting a chance to figure out which views need to have drawRect called on, then your view will never display its status bar.
Therefore in order to accomplish what you want, you have to either make sure that drawRect gets called, such as by pushing off a lot of your initialization code into other threads or timer tasks, or you can force the drawing by calling drawRect yourself, after setting up contexts and such.
If you go with the background tasks, then make sure your initialization code is thread-safe.
Default.png is just a graphic, a static image shown while the application is launching. If you want to show further progress, you'll have to show everything at the applicationDidLaunch phase. Show your modal "Splash Screen" there first (Create a view controller, add its view as a subview of your main window) and dismiss it when you are done whatever additional loading you needed to do.
Also, you need to do update your progress bar in a seperate thread. Updating your GUI in the same thread where a lot of business is going on is (in my opinion, but I could be wrong) a bad idea.
The main thread is, as far as I know, the only one that can safely do GUI things, and its event loop (that is, the main application thread's) is the one that does the actual displaying after you've called -setNeedsDisplay. Spawn a new thread to do your loading, and update the progress on the main thread.

Why does popViewController only work every other time

I am totally stumped, here's the situation:
My app uses the Core Location framework to get the current location of the user and then pings my server at TrailBehind for interesting places nearby and displays them as a list. No problems.
To conserve batteries, I turn off the GPS service after I get my data from the server. If the user moves around while using the app and wants a new list he clicks "Refresh" on the navigation controller and the CLLocation service is again activated, a new batch of data is retrieved from the server and the table is redrawn.
While the app is grabbing data from my server I load a loading screen with a spinning globe that says "Loading, please wait" and I hide the navigation bar so they don't hit "back".
So, the initial data grab from the server goes flawlessly.
The FIRST time I hit refresh all the code executes to get a new location, ping the server again for a new list of data and updates the cells. However, instead of loading the table view as it should it restores the navigation controller bar for the table view but still shows my loading view in the main window. This is only true on the device, everything works totally fine in the simulator.
The SECOND time I hit refresh the function works normally.
The THIRD time I hit refresh it fails as above.
The FOURTH time I hit refresh it works normally.
The FIFTH time I hit refresh it fails as above.
etc etc, even refreshes succeed and odd refreshes fail. I stepped over all my code line by line and everything seems to be executing normally. I actually continued stepping over the core instructions and after a huge amount of clicking "step over" I found that the table view DOES actually display on the screen at some point in CFRunLoopRunSpecific, but I then clicked "continue" and my loading view took over the screen.
I am absolutely baffled. Please help!! Many thanks in advance for your insight.
Video of the strange behavior:
Relevant Code:
RootViewControllerMethods (This is the base view for this TableView project)
- (void)viewDidLoad {
//Start the Current Location controller as soon as the program starts. The Controller calls delegate methods
//that will update the list and refresh
[MyCLController sharedInstance].delegate = self;
[[MyCLController sharedInstance].locationManager startUpdatingLocation];
lv = [[LoadingViewController alloc] initWithNibName:#"Loading" bundle:nil];
[self.navigationController pushViewController:lv animated:YES];
[super viewDidLoad];
}
- (void)updateClicked {
//When the location is successfully updated the UpdateCells method will stop the CL manager from updating, so when we want to update the location
//all we have to do is start it up again. I hope.
[[MyCLController sharedInstance].locationManager startUpdatingLocation];
[self.navigationController pushViewController:lv animated:YES];
//LV is a class object which is of type UIViewController and contains my spinning globe/loading view.
}
-(void)updateCells {
//When the Core Location controller has updated its location it calls this metod. The method sends a request for a JSON dictionary
//to trailbehind and stores the response in the class variable jsonArray. reloadData is then called which causes the table to
//re-initialize the table with the new data in jsonArray and display it on the screen.
[[MyCLController sharedInstance].locationManager stopUpdatingLocation];
if(self.navigationController.visibleViewController != self) {
self.urlString = [NSString stringWithFormat:#"http://www.trailbehind.com/iphone/nodes/%#/%#/2/10",self.lat,self.lon];
NSURL *jsonURL = [NSURL URLWithString:self.urlString];
NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];
NSLog(#"JsonData = %# \n", jsonURL);
self.jsonArray = [jsonData JSONValue];
[self.tableView reloadData];
[self.navigationController popToRootViewControllerAnimated:YES];
[jsonData release];
}
}
CLController Methods: Basically just sends all the data straight back to the RootViewController
// Called when the location is updated
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"New Location: %# \n", newLocation);
NSLog(#"Old Location: %# \n", oldLocation);
#synchronized(self) {
NSNumber *lat = [[[NSNumber alloc] init] autorelease];
NSNumber *lon = [[[NSNumber alloc] init] autorelease];
lat = [NSNumber numberWithFloat:newLocation.coordinate.latitude];
lon = [NSNumber numberWithFloat:newLocation.coordinate.longitude];
[self.delegate noteLat:lat];
[self.delegate noteLon:lon];
[self.delegate noteNewLocation:newLocation];
[self.delegate updateCells];
}
}
The first thought is that you may not want to send startUpdatingLocation to the CLLocationManager until after you've pushed your loading view. Often the first -locationManager:didUpdateToLocation:fromLocation: message will appear instantly with cached GPS data. This only matters if you're acting on every message and not filtering the GPS data as shown in your sample code here. However, this would not cause the situation you've described - it would cause the loading screen to get stuck.
I've experienced similarly weird behavior like this in a different situation where I was trying to pop to the root view controller when switching to a different tab and the call wasn't being made in the correct place. I believe the popToRootViewController was being called twice for me. My suspicion is that your loading view is either being pushed twice or popped twice.
I recommend implementing -viewWillAppear:, -viewDidAppear:, -viewWillDisappear: and -viewDidDisappear: with minimal logging in your LoadingViewController.
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"[%# viewWillAppear:%d]", [self class], animated);
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
NSLog(#"[%# viewDidAppear:%d]", [self class], animated);
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
NSLog(#"[%# viewWillDisappear:%d]", [self class], animated);
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
NSLog(#"[%# viewDidDisappear:%d]", [self class], animated);
[super viewDidDisappear:animated];
}
Then, run a test on your device to see if they are always being sent to your view controller and how often. You might add some logging to -updateClicked to reveal double-taps.
Another thought, while your #synchronized block is a good idea, it will only hold off other threads from executing those statements until the first thread exits the block. I suggest moving the -stopUpdatingLocation message to be the first statement inside that #synchronized block. That way, once you decide to act on some new GPS data you immediately tell CLLocationManager to stop sending new data.
Can you try and debug your application to see where the control goes when calling updateCells? Doesn't seem to be anything apparently wrong with the app.
Make sure that there are no memory warnings while you are in the LoadingViewController class. If there is a memory warning and your RootViewController's view is being released, then the viewDidLoad will be called again when you do a pop to RootViewController.
Keep breakpoints in viewDidLoad and updateCells. Are you sure you are not calling LoadingViewController anywhere else?
So, I never did get this to work. I observe this behavior on the device only every time I call popViewController programatically instead of allowing the default back button on the navigation controller to do the popping.
My workaround was to build a custom loading view, and flip the screen to that view every time there would be a delay due to accessing the internet. My method takes a boolean variable of yes or no - yes switches to the loading screen and no switches back to the normal view. Here's the code:
- (void)switchViewsToLoading:(BOOL)loading {
// Start the Animation Block
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.tableView cache:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:.75];
// Animations
if(loading) {
if (lv == nil) { lv = [[LoadingViewController alloc] initWithNibName:#"Loading" bundle:nil]; }
[self.view addSubview:lv.view];
[self.view sendSubviewToBack:self.tableView];
self.title = #"TrailBehind";
}
else {
[lv.view removeFromSuperview];
}
// Commit Animation Block
[UIView commitAnimations];
//It looks kind of dumb to animate the nav bar buttons, so set those here
if(loading) {
self.navigationItem.rightBarButtonItem = nil;
self.navigationItem.leftBarButtonItem = nil;
self.title = #"TrailBehind";
}
else {
UIBarButtonItem *feedback = [[UIBarButtonItem alloc] initWithTitle:#"Feedback" style:UIBarButtonItemStylePlain target:self action:#selector(feedbackClicked)];
self.navigationItem.rightBarButtonItem = feedback;
UIBarButtonItem *update = [[UIBarButtonItem alloc] initWithTitle:#"Move Me" style:UIBarButtonItemStylePlain target:self action:#selector(updateClicked)];
self.navigationItem.leftBarButtonItem = update;
[feedback release];
[update release];
}
}
Looking at your original code, I suspect this block very much:
- (void)viewDidLoad {
...
lv = [[LoadingViewController alloc] initWithNibName:#"Loading" bundle:nil];
[self.navigationController pushViewController:lv animated:YES];
[super viewDidLoad];
}
viewDidLoad is called every time the NIB is loaded, which can happen multiple times, especially if you run low on memory (something that seems likely given your remark that it only happens on device). I recommend that you implement -didReciveMemoryWarning, and after calling super, at the very least print a log so you can see whether it's happening to you.
The thing that bothers me about the code above is that you're almost certainly leaking lv, meaning that there may be an increasing number of LoadingViewControllers running around. You say it's a class variable. Do you really mean it's an instance variable? ivars should always use accessors (self.lv or [self lv] rather than lv). Do not directly assign to them; you will almost always do it wrong (as you are likely dong here).
I came across this while searching for the exact same issue, so while I'm sure you've already solved your problem by now, I figured I'd post my solution in case someone else runs across it...
This error seems to be caused when you assign two IBActions to the same UIButton in interface builder. It turned out that the button I used to push the view controller onto the stack was assigned to two IBActions, and each one was pushing a different controller onto the navigationController's stack (although you'll only end up seeing one of them - perhaps the last one to be called). So anyway, pressing the back button on the topmost view doesn't really dismiss it (or maybe it's dismissing the 2nd, unseen controller), and you have to press twice to get back.
Anyway, check your buttons and be sure they're only assigned to a single IBAction. That fixed it for me.