Perform selector:withObject:afterDelay firing immediately, no delay - iphone

I have a very strange error that I have never seen before. I have used this code hundreds of times before, but for some reason, the afterDelay is being completely disregarded.
My Code:
- (void)runIntro
{
if([self introDone])return;
UIImageView* back = [[UIImageView alloc] initWithFrame:[[AppDelegate shared] frame]];
[back setImage:[[AppDelegate shared] bgImage]];
[back setContentMode:UIViewContentModeScaleAspectFill];
[self.view addSubview:back];
UIImageView* target = [[UIImageView alloc] initWithFrame:CGRectMake(([[AppDelegate shared] width]-250)/2.0, ([[AppDelegate shared] height]-350)/2.0, 250, 72)];
[target setImage:[UIImage imageNamed:#"bpside.png"]];
[self.view addSubview:target];
Parabolic* wally = [[Parabolic alloc] initWithFrame:CGRectMake(([[AppDelegate shared] width]-200)/2.0, ([[AppDelegate shared] height]-314)/2.0+250, 200, 200)];
[wally setImage:[UIImage imageNamed:#"wallyside1.png"]];
[self.view addSubview:wally];
[self performSelector:#selector(moveWallyIntro:) withObject:wally afterDelay:1];
[self performSelector:#selector(moveWallyIntro:) withObject:wally afterDelay:2];
}
- (void)moveWallyIntro:(Parabolic*)wally
{
[wally runPBM:50 withDY:300 withDuration:0.5];
}
I know that all of the other code is working properly, but for whatever reason, the selectors are being performed immediately, which causes a major problem. This code is being run at the end of the viewWillAppear method. I have programmed this way many times before and this is the first time I've seen this happen.
UPDATE:
If I remove the code that adds the back UIImageView, this no longer is a problem. How can I add a giant image (3000x2000) in aspect fill format like this so it doesn't cause the program to hang? I'm using the same background image for iphone, ipad, etc., so it needs to be large for the retina iPads...

Related

How to consume UI Events while loading data in the background

I have a little iPhone app that loads data from a web service. To make sure that nothing goes wrong while loading the data I create a semi-transparent view over the app and use CFRunloopRun() to wait until all the data is loaded in the background. This is the code for that:
self.connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
// Now show an animation
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
UIView *window = [[UIApplication sharedApplication] keyWindow];
UIView *shield = [[UIView alloc] initWithFrame:window.bounds];
shield.backgroundColor = [UIColor blackColor];
shield.alpha = 0.5f;
[window addSubview:shield];
spinner.center = shield.center;
[shield addSubview:spinner];
spinner.hidden = NO;
NSLog( #"JCL.callServerWithRequest(), spinner view: %#, shield view: %#, window: %#", spinner, shield, window );
[spinner startAnimating];
// Hand over to the Runnloop to wait
CFRunLoopRun();
[spinner stopAnimating];
[spinner removeFromSuperview];
[spinner release];
[shield removeFromSuperview];
[shield release];
This works fine except that any clicks on a button somewhere is played after the loading so if the users clicks on the download button twice he will do the download twice as well.
Any idea how to consume the UI events before the shield is removed.
Thanks - Andy
Try it without messing with runloops. I suspect that the UI events are coming in to the normal loop on the window but not being processed until your custom loop returns, at which point the "shield" view is no longer there to catch them. If you put the shield in place and then let the main runloop handle things, the shield should catch them all as normal.
Thanks to Anomie I finally tried out to go without the CFRunLoopRun() and it is quite difficult because the execution is split into two parts: - the Invocation and the Return of the Result through a callback. But then I shot myself in the proverbial foot because I tried to block the returning thread to slow down the execution which did not work because that was executed again in the main thread.
Eventually I did slow down the Web Service and then everything worked as expected.

Image not appearing in detachNewThreadSelector

I am having trouble with my game and was wondering if anyone can help?
I am loading an overlay image on the screen when a user clicks on an answer in my quiz game, the image is of either a tick or cross to indicate if the answer was correct. The image is being loaded in a new thread.
The problem is that the image does not appear every-time the user clicks on an answer, it only appears about 20% of the time in one round.
Whats even more weird though is that i had the game a few months ago ready on iOS3 and this problem didn't occur, it only seemed to start when updating to iOS4.
Here is the code:
The images are created in the loadView method:
tick = [UIImageView alloc];
[tick initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
[tick setImage:[UIImage imageNamed:#"correct.png"]];
cross = [UIImageView alloc];
[cross initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
[cross setImage:[UIImage imageNamed:#"wrong.png"]];
The threads are called here:
-(void)answerAttempt:(UIButton *)b{
ansQues++;
if([b.currentTitle isEqualToString:ans]){
[NSThread detachNewThreadSelector: #selector(showTick) toTarget:self withObject:nil];
[appDelegate playSound:1];
score++;
...
...
...
}else{
[NSThread detachNewThreadSelector: #selector(showCross) toTarget:self withObject:nil];
[appDelegate playSound:0];
}
Here is the function the two threads call:
-(void)showTick{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[tick setHidden:NO];
[levelPage addSubview:tick];
[levelPage bringSubviewToFront: tick];
usleep(500000);
[tick removeFromSuperview];
[pool release];
}
-(void)showCross{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[cross setHidden:NO];
[levelPage addSubview:cross];
[levelPage bringSubviewToFront: cross];
usleep(500000);
[cross removeFromSuperview];
[pool release];
}
As you might be able to tell from the code, ive tried various ways to try and get it to display everytime, but have not found any solution for some time now. I have checked to make sure that the correct thread is being called each time, and it does - just UIImage for tick and cross does not display.
Please feel free to ask any further questions.
Thanks.
All UI updates should be done on the Main Thread. Also, if you are sleeping in your code, the design is probably not right, but if you are stuck on doing it this way, then a simple fix is to do this:
[levelPage performSelectorOnMainThread:#selector(addSubview:) withObject:cross waitUntilFinished:NO];

iPhone FBConnectDialog box disappears

So I've been trying to integrate FB with my iPhone application with FB connect. However, after using the tutorial (http://www.mobisoftinfotech.com/blog/iphone/iphone-fbconnect-facebook-connect-tutorial/), it seems when the FBDialog box is called, it pops onto the screen for a few seconds and then disappears. I'm not sure what is going, becuase IT WORKS in the Simulator perfectly. I have an iPhone 4 device. Here is my code below:
(void)viewDidLoad {
[super viewDidLoad];
session = [[FBSession sessionForApplication:#"MYAPIKEY" secret:#"MYSECRET" delegate:self] retain];
FBLoginButton *button = [[[FBLoginButton alloc] init] autorelease];
[button setFrame:CGRectMake(100, 100, 100, 50)];
[self.view addSubview:button];
}
Nevermind, I'm sorry for being a noob. Turns out my three hour struggle to figure this out was a complete waste. The problem -- my iPhone wasn't connected to Wifi/cell network.

UIActivityIndicator question

The apple documentation made this seem like it was pretty easy but it's not working. My code is:
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[activity startAnimating];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:#"http://www.myserver.com"]];
[request setPostValue:name forKey:#"key"];
[request startSynchronous];
NSLog(#"%#",[request responseString]);
[activity stopAnimating];
[activity release];
I didn't set anything up in UIBuilder because frankly, I don't understand exactly how it works with the UIActivityIndicator. Building and Running the app with the code I have above doesnt send off any warning indicators and runs just fine but I am not seeing the activity indicator.
#Eiko's right that your UI is going to block waiting on your synchronous web request to finish, but there's another problem. You're not even adding your activity view to your main view!
You need to give UIActivityIndicatorView *activity a .frame value, and then add it to the view heirarchy. Thusly:
activity.frame = CGRectMake(0,0,50,50); //that'll be top-left corner
[self.view addSubview:activity];
[activity startAnimating];
Now, it'll sit there NOT spinning while you do your web request, because you're doing that request on the main thread. I'm VERY glad you're using ASI, but you're going about it the ugly way.
Make your UIViewController conform to the ASIHttpRequestDelegate protocol.
Set request.delegate = self;.
Implement -requestFinished(ASIHttpRequest *)request to handle the response you get, and in THAT method, hide your activity view. You will PROBABLY wish at that point that you'd made it a named property, or at least an iVar, so you have a handle on it later.
The UI won't update before finishing the method call (well or until the next iteration in the run loop to be more precisely), so this is effectively adding and removing it in the same step.
You should time your request asynchronously to see something happen, or at least schedule the different tasks (adding activity, the request itself and removing it) independently on the main thread. Blocking the main thread is a very bad idea though, and your app will get killed completely if your request takes too long to respond (for whatever reason - easy to think of with web services).
Try putting UIActivityIndicatorView creation in a different thread.
UIView *acty_view=[[UIView alloc]initWithFrame:CGRectMake(130,220, 60, 60)];
acty_view.backgroundColor=[UIColor blackColor];
acty_view.alpha = 0.6;
acty_view.layer.cornerRadius = 10;
acty_view.layer.masksToBounds = YES;
activitidicator = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(15, 15, 30, 30)];
[activitidicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhite];
[activitidicator setColor:[UIColor whiteColor]];
UILabel *lbl=[[UILabel alloc]initWithFrame:CGRectMake(8, 42, 100, 15)];
lbl.text=#"Loading";
[lbl setFont:[UIFont systemFontOfSize:10]];
lbl.textColor = [UIColor whiteColor];
// [lbl setFont:[UIFont fontWithName:#"Helvetica" size:13]];
[acty_view addSubview:lbl];
[acty_view addSubview:activitidicator];
[self.view addSubview:acty_view];
[activitidicator startAnimating];
[self performSelector:#selector(CallWebserviceMethod) withObject:activitidicator afterDelay:0];`
-(void)CallWebserviceMethod
{
[self JsonResults];
[activitidicator stopAnimating];
}

How do I get a UIView to appear instantly?

I'm trying to create an activity indicator in iPhone app. The problem is that I cannot get it to appear before the actual task i want it to diplay during is already done. Is there something funky about the order in which the iPhone does stuff?
Here is my problematic code (in my app delegate):
-(BOOL)showProgressView: (NSString *) message {
self.progress = [[UIView alloc] initWithFrame:window.frame];
UIImageView *img = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"squircle.png"]];
[img setAlpha:0.5];
[img setFrame:CGRectMake(94, 173, 133, 133)];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(51.5, 51.5, 30, 30)];
spinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[img addSubview:spinner];
[self.progress addSubview:img];
[spinner startAnimating];
[img release];
[spinner release];
[window addSubview:self.progress];
return YES;
}
I then call this code like this:
if ([appDelegate showProgressView:#"Loading..:"])
{
//My actual code loads data and stuff here but that is not important
//drawCtrl is a UIViewController subclass that is instantiated here
UINavigationController *navController = [appDelegate navigationController];
[navController pushViewController:drawCtrl animated:YES];
[drawCtrl release];
}
The problem is that my activity indicator does not appear until the new view controller is pushed onto the navController's stack. Can I control this in some way?
Thanks in advance!
-Mats
You need to call your loading method periodically via a timer. UI changes require a trip through the event loop to be seen. I have done this usually in a timer I set up in the AppDelegate, it calls the "methodThatTakesSomeTime" every n seconds, the "methodThatTakesSomeTime" method does some work for a specified time slice and then exits which is usually about the same as the timer trigger time. This gives the event loop time to refresh the UI and also give your method time to do its stuff.
Takes some fiddling with keeping the state of the "methodThatTakesSomeTime" so it can continue on its way, but that is what it takes.