Code not executing as expected when loading URL into UIImageView - iphone

Im having trouble with my code, it seems that it never executes right.
I've tried many things from UIActivity, Sliders, UITextVieweer etc... but it never changes,
The code is running using Navigation based application from xCode. loadingTview is a Textview,
The problem is, see where loadingTview is, that never works, it always hangs, the user presses a button, and this code is executed. The loadingTview is a Textview saying "loading" with a alpha of 0.4 so basiclly whilst its downloading the image form the website, people know its loading.
I tried views as well but same issue.
How can i progress?
loadingTview.hidden = false;
today = [NSDate date];
dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
dateString = [dateFormat stringFromDate:today];
if (PageEntered == #"page1")
{
NSString *url = [NSString stringWithFormat:#"http://www.imagegoeshere.com/%#.jpg",dateString];
imageURL = [NSURL URLWithString:url];
imageData = [NSData dataWithContentsOfURL:url];
image = [UIImage imageWithData:imageData];
FullScreenImage.image = image;
loadingTview.hidden = true;
[navigationController pushViewController:vFullscreen animated:YES];
}

I'm not sure entirely what the problem is but I assume that when you go from view2 to view3 it 'hangs' on view2 until the image is loaded before actually opening view3 to show the loading screen, right?
If that's the case, then what you want to do is load your image in a different thread so that the loading doesn't block the view3 from showing the loading screen.
Have a look at NSThread (although there are cleaner/better ways to do this).
Basically do this in view3's controller:
- (void) viewDidLoad {
// <First, show your 'Loading...' screen here>
// Then create a thread to load the image:
[NSThread detachNewThreadSelector:#selector(loadImage) toTarget:self withObject:nil];
}
// Then somewhere in the same class define the loading method:
- (void)loadImage {
// Remember to create a new autorelease pool for this thread.
// <Load your image here>
// When image is done loading, call the main thread
[self performSelectorOnMainThread:#selector(imageDoneLoading) withObject:nil waitUntilDone:YES];
}
// Then define the method to call when the image is done
- (void) imageDoneLoading {
// Hide the 'Loading...' screen.
}
If this is not the problem you have, then please provide more detail as to what is actually happening and what the problem is.
Good luck.

I don't really understand your question, but I do see something that's almost certainly wrong. This line:
if (PageEntered == #"page1")
should be this:
if ([PageEntered isEqualToString:#"page1"])
Objective-C doesn't do operator overloading, so your code is doing a pointer comparison, not a value comparison.

Related

How to call a web service on page load

I am trying to call a web service on page load. Currently I call it on a button click and it works fine. But when I try to do the same on viewDidAppear, it doesn't happen. What i want to achieve is if username and password are saved then it should automatically load the next page. It is filling in the text boxes but not loading the next page.
Here is my code for submit button and ViewDidAppear:
-(IBAction)submitButton{
[apd showCoverView:YES WithActivityIndicator:YES];
PlaceWebService *handler = [[PlaceWebService alloc]init];
[handler setRequestType:Loginparser];
NSString *url = [NSString stringWithFormat:#"http://www.mywebsite.com/api.php?command=auth&cardno=%#&password=%#",username.text,password.text];
[handler sendingLoginRequest:url Respond:self At:#selector(showParsed:)];
}
and for viewDidAppear
-(void)viewDidAppear:(BOOL)animated
{
NSLog(#"Appeared");
[self loginArea];
apd=[[UIApplication sharedApplication]delegate];
NSString *filepath=[self pathOfFile];
if([[NSFileManager defaultManager]fileExistsAtPath:filepath])
{
NSArray *array=[[NSArray alloc]initWithContentsOfFile:filepath];
username.text=[array objectAtIndex:0];
password.text=[array objectAtIndex:1];
[self submitButton];
}
}
What should I do? Please help...
If you want to call the method after loading view and without any event, then you need to that as normal instance method instead of IBAction method.
-(Void)submitButton{
// implementation
}
and then call this method from viewDidAppear.
A couple things could be the problem here.
1)
IBActions usually take a parameter. Declare it as:
- (IBAction) submitButton: (id) sender;
And then call it from your viewDidAppear method as:
[self submitButton: self];
2)
Also make sure UI stuff is happening on the main thread (you didn't specify if the app is multi threaded or not), so maybe:
[self performSelectorOnMainThread: #selector(submitButton:) withObject: self];
And
3)
Set breakpoints to see if your submitButton method (and the lines before it) are actually even called when viewFromAppear: is called.
And Rishi's suggestion is good, too!

Pushing View Controller - viewDidAppear not called

I have this piece of code to push a view controller:
// Setup the animation
[self.navigationController pushViewController:self.productView animated:YES];
self.productView.imageURL = [product imageURL];
// Set the title of the view to the product's name
self.productView.title = [product name];
// Set the label text of all the labels in the view
[self.productView.caloriesL setText:[product calories]];
[self.productView.fatL setText:[product fat]];
[self.productView.saturatesL setText:[product saturates]];
[self.productView.sugarL setText:[product sugar]];
[self.productView.fibreL setText:[product fibre]];
[self.productView.saltL setText:[product salt]];
But the delegate method viewDidAppear does not get called when the productView appears. I looked up the problem on google and theres a lot of different solutions, none of which I could apply to my problem.. I had a similar problem in a previous solution but I got around it by manually calling viewDidApear in the viewDidLoad method. Unfortunately in this case I can't do that as viewDidLoad is called only once (on the first push). Does anyone know how to fix this?
Thanks,
Jack Nutkins
EDIT:
Here is the viewDidAppear method in the productView (and selector):
- (void)viewDidAppear:(BOOL)animated{
//Start animating the activity indicator
[indicator startAnimating];
//Perform this method in background
[self performSelectorInBackground:#selector(loadImage) withObject:nil];
}
- (void) loadImage {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Load the animals image into a NSData boject and then assign it to the UIImageView
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
self.imageView.image = image;
//Stop animating the activity indicator
[indicator stopAnimating];
[pool drain]; //see comment below
}
First: You definitely don't want to be calling any of the standard viewWillLoad, viewDidLoad, viewWillAppear, etc. methods manually. Let the OS do it for you.
Second: Can you show us how your viewDidAppear method is implemented in your self.productView instance? (Just a hunch, you're not expecting this method to be called on your navigation controller, right?) I just want to make sure your method signature is exactly correct. If it's not (due to a mispelling, improper args, etc.) then it definitely won't be called.
Third: I would move your pushViewController: call to after the rest of the code you provided. You don't want the view to be pushed on the screen (so the user can see it) and then have a bunch of on-screen values immediately change. Set your ivars and title property first, then push the view controller. This eliminates any weird flickering.
I solved it, though it doesn't seem conventional, can't believe I didn't try it earlier :
I put this line :
[self.productView viewDidAppear:YES];
Underneath :
// Setup the animation
[self.navigationController pushViewController:self.productView animated:YES];
I also moved the code to set the labels text to run before the above line. (As well as changing my code to send strings to the pushed controller rather that accessing its UI elements.)
Thanks for everyones help,
Jack

iOS Programming with GCD

I use GCD to fetch some images from the Internet in a new queue using dispatch_queueu_create. Then I go back to the main queue and update the UIImageView with the fetched image by using dispatch_async. I am using self.imageView to reference to the UIImageView in the main queue.
However, sometimes the image takes some time to load from the Internet. If the user clicks somewhere and the view controller changes to something else, I found some weird behavior or my app even crashes. I guess the reason is that I am referencing to self.imageView but the current view controller doesn't have that property. Or any other possibilities? Any suggestions to fix that?
Thanks in advance!
Sorry that I didn't include code here. Here is the code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
dispatch_queue_t photoFetchQueue = dispatch_queue_create("photo data downloader", NULL);
dispatch_async(photoFetchQueue, ^{
NSURL *photoURL = ...;
NSData *photoData = ...;
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithData:photoData];
self.imageView.frame = CGRectMake(0, 0, self.imageView.image.size.width, self.imageView.image.size.height);
self.scrollView.zoomScale = 1.0;
self.scrollView.contentSize = self.imageView.image.size;
[scrollView zoomToRect:imageView.frame animated:YES];
});
});
dispatch_release(photoFetchQueue);
}
You need a cancellation pattern. That is, you need to be able to cancel the background task. Without code, hard to say what the best approach would be. If you used the higher level NSOperation API, it has cancellation support.
For Blocks/GCD, you'd probably want to have an "isCanceled" on some object somewhere -- preferably on the object that represents the background fetch -- and then check that, when possible, to determine if the response is still relevant.

How can I see the intermediate updates on my UI while debugging iPad code?

I am developing an app in which I am using a plist file. There are 21 key-value pairs in the plist. Each pair is a dictionary (type) with 6 items. The dictionary contains a set of images. In my program, I am using the path to retrieve the images. My requirement is that the images should be displayed one-by-one on the imageView. i have done it successfully. The images are being dispalyed exactly from plist.
So my question is can I use the debugger to see the intermediate execution of the plist? When I placed breakpoints in my code, and run using the debugger, I am able to step into the code and the images are displayed on the view only after the whole execution of plist with 21 key-value pairs is done. How can I see the images on the view while debugging each pair?
(void)setSequenceInfo:(NSDictionary *)sequenceInfo
{
[self.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
self.sequenceQueue = [NSMutableArray array];
//load the sequenceinfo dictionary
[_sequenceInfo release];
if (!sequenceInfo)
return;
_sequenceInfo = [sequenceInfo retain];
//create one UIImageView by sequence
NSMutableDictionary* views = [NSMutableDictionary dictionaryWithCapacity:[sequenceInfo count]];
for (NSString* identifier in _sequenceInfo)
{
UIImageView* seqView = [[UIImageView alloc] initWithFrame:self.bounds];
seqView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
seqView.contentMode = self.contentMode;
//the image is hidden until its sequence is played
seqView.hidden=YES;
//add the newly created image view to our subviews
[self addSubview:seqView];
//also store it in our sequenceViews dictionary
[views setValue:seqView forKey:identifier];
[seqView release];
}
self.sequenceViews = views;
}
In order to "see" intermediate results while using the debugger, you'll have to change the structure of your code to return to the main run loop, so that the UIImageView has the chance to run and display the new image. You could set up a timer that fires periodically, and in the timer routine you change the UIImageView's image to the next one in the sequence. Then you set your breakpoint in the timer handler method in gdb, and when you hit that breakpoint each time you should see the previously displayed image.
EDIT: here's some more detail, based on what I think you want to do. Realize, though, that I think there's no "magic wand" to be able to see changes on the screen while you're in the debugger. You have to return to the Run Loop so that your UIKIt widgets get cycles to draw themselves.
For example, say you have this method:
- (void) updateTwoLabels:(NSString *)newText
{
self.label1.text = newText;
self.label2.text = newText; // put breakpoint on this line
}
and you want to stop in the debugger just before label2's text is set, and see that label1 has changed. Well, I think there's no way of doing that, except by modifying your code, and calling yourself again with performSelector:withObject:afterDelay: so that the main run loop can run for a while then call your code again:
BOOL callAgain = NO;
BOOL setLabel2 = YES;
- (void) updateTwoLabels:(NSString *)newText
{
self.label1.text = newText;
if ( setLabel2 )
self.label2.text = newText; // put breakpoint on this line
if ( callAgain )
[self performSelector:#selector(updateTwoLabels:) withObject:newText afterDelay:0];
}
in this way, when you're debugging, you can set callAgain to YES and setLabel2 to NO in gdbg, and keep "continuing" until you see that label1 has changed. That's the basic technique.
Do the whole method in a background thread but call GUI stuff on the main thread. For example: [self performSelectorOnMainThread:#selector(addSubview:) withObject:seqView waitUntilDone:YES]. Don't forget to make an autorelease pool at the beginning of the method (the one that runs in the background thread) and drain it at the end of it.

iPhone loading view from nib only works once

I'm trying to add a «loader-view» to my app which shows a spinner while doing stuff.
This works fine the first time, but it doesn't work a second time.
here's what I do:
I have a viewController for the spinner (spinnerViewController) and a nib-file which I made in IB (spinner.xib).
I load the nib in the viewDidLoad-event:
spinnerView = [[spinnerViewController alloc] initWithNibName:#"spinner" bundle:nil];
[spinnerView retain];
spinnerView is declared in the .h-file (spinnerViewController *spinnerView;)
next, I show the spinner-view:
[self.view addSubview:spinnerView.view];
[self.view bringSubviewToFront:spinnerView.view];
which works fine...
And now the trouble starts. No matter what I do, I can't show the spinner view again.
I tried just hiding it (self.view sendSubViewToBack: spinnerView.view) which works for hiding, but when I try to bring it to the front again (self.view bringSubViewToFront: spinnerView.view) it doesn't work.
I also tried removing the spinner-view and add it again with no success (within the spinnerViewController: [self.view removeFromSuperview] and to show it again [self.view addSubview... )
[EDIT]
I changed the whole setup a little and just made the spinner-view a subview in IB - again, hiding works, but showing again fails.
What I found out: After the bringSubViewToFront-command, I call some web-service to get some data. When I comment the following code out and just show the spinnerView, it works. So now I'm trying to figure out how to determine when the spinner-view appeared and then continue with the code - but naturally, this doesn't work (yet) :)
Any ideas what I'm doing wrong??? ;)
Problem solved.
This page gave the answer: http://urenjoy.blogspot.com/2009/05/uiactivityindicatorview.html
Apparently, the update has to happen in a separate thread, as the web-stuff blocks the current one, hence the view did not appear.
[NSThread detachNewThreadSelector:#selector(doWork) toTarget:self withObject:nil];
- (void) doWork {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
.....Time Consuming Code here .....
[pool release];
}
I might be not exactly on your question, but in general creating a ViewController class in order to show a spinner on the screen is a huge overkill ... just try to discover the logic behind what you do : you create a viewcontroller, but you never use it, you use the view.
So in short I believe you need only a UIView (the view property of the UIViewController)
Why don't you try something like :
... in your class interface...
UIActivityIndicator* activity;
... when the activity needs to happen ...
activity = [[UIActivityIndicator alloc] initWithActivityIndicatorStyle: ....
[activity startAnimating];
[self.view addSubview:activity];
[activity release]
... when the activity is finished
[activity removeFromSuperview]; //this will also decrease the retain count