Getting the spinner to begin as soon as view is loaded - objective-c

in my app, I have a table in one of the viewControllers. When one of the rows are tapped, it takes the user to a different view/viewController and it works. Within this new viewController, data is being parsed from a php script in the background.
This takes about 7-10 seconds and in this time I want the user to IMMEDIATELY see a spinner that says "Loading..". I have implemented this myself but the spinner does not start to load until 4-5 seconds in. During this time, the screen is completely frozen and I cannot tap anything or go back until the spinner/data is displayed.
I have tried to put the following code within the method that does the actually fetching of the data, within both (not at the same time) the viewDidAppear and ViewDidLoad methods as well but the same thing happens.
If anyone knows how to fix this, it would be much appreciated.
Thank you
[web loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString:#"http://xxx.xx.xx.xxx/stuff/xxx.php"]]];
[web addSubview:spinner];
timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector: #selector(tick) userInfo:nil repeats:YES];
load_message = [[UIAlertView alloc] initWithTitle:#"Loading..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
spinner= [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.center = CGPointMake(135.0, 60.0);
[load_message addSubview:spinner];
-(void) tick
{
if(!web.loading)
{
[spinner stopAnimating];
[load_message dismissWithClickedButtonIndex:0 animated:TRUE];
}
else {
[load_message show];
[spinner startAnimating];
}
}

You could start your spinner then start your long process after a delay:
- (void)someMethod
{
[spinner startAnimating];
[self performSelector#selector(doLongProcess:) withObject:someObject afterDelay:0.0];
}
- (void)doLongProcess:(id)someObject
{
//Some really long process
}
Your spinner was most likely blocked because a long process may be occurring on the same thread.

You have to do one of the following (assumes you put your web request in a method called 'startWebRequest'):
Start the UIActivityIndicatorView on the the main UI thread (if not already running from the main thread):
[self performSelectorOnMainThread:#selector(startWebRequest) withObject:nil waitUntilDone:NO];
Start the web request on a background thread:
[self performSelectorInBackground:#selector(startWebRequest) withObject:nil];
Pause like a 1/10th of second before starting the web request like with:
[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(startWebRequest) userInfo:nil repeats:NO];

Related

Is it possible to create an UIAlertView without any buttons?

I need help with UIAlertView :-). Currently I have an UIAlertView showing up when the user shakes the device using the -(void)motionEnded: function. I want to make the alert view disappear after 0.5 seconds using a NSTimer so I don't need any button at the bottom of the alert view to dismiss it. Is there a way to create an UIAlertView without any button? [A way to remove the space with the arrow in the image below?]
Here's the code:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
resetAlert = [[UIAlertView alloc] initWithTitle:#"Reset!"
message:nil
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:nil, nil];
[resetAlert show];
alertHideTimer = [NSTimer scheduledTimerWithTimeInterval:10.5 target:self selector:#selector(dismissWithClickedButtonIndex:animated:) userInfo:nil repeats:NO];
self.label.text = #"0";
numero = 0;
[self.label2 setHidden:YES];
}
Yes it is. Try the following: iToast
You can create your own view. Show and hide/close it as per your requirement, timers etc.
I'm not sure if having the NSTimer with a multiple argument selector is correct. I add this method:
-(void)dismissAlertView:(UIAlertView*)alertView {
[alertView dismissWithClickedButtonIndex:0 animated:NO];
}
and replace your NSTimer with:
[self performSelector:#selector(dismissAlertView:) withObject:resetAlert afterDelay:0.5];
As far as I know removing the space entirely is not possible.
However... here are some things you may want to try;
Adding top padding
Add a few newlines to get top padding: initWithTitle:#"\n\nConfiguring Preferences...
(source: iosdevelopertips.com)
Adding a spinner
Add a UIActivityIndicatorView to the UIAlertView
alert = [[UIAlertView alloc] initWithTitle:#"Configuring Preferences\nPlease Wait..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
[alert show];
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
Adjust the indicator so it is up a few pixels from the bottom of the alert
indicator.center = CGPointMake(alert.bounds.size.width / 2, alert.bounds.size.height - 50);
[indicator startAnimating];
[alert addSubview:indicator];
(source: iosdevelopertips.com)
Hope that helps.
You can hide the alert simply by calling the delegate method after 5 sec
[alert dismissWithClickedButtonIndex:0 animated:YES];

NSTimer Not Firing in Subthread

I am trying to get a NSTimer to fire in a subthread. My code essentially looks like this:
-(void) handleTimer:(NSTimer *)theTimer {
NSLog(#"timer fired");
}
-(void) startMyThread {
// If I put timer in here, right before I start my new thread, it fires.
// [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
[NSThread detachNewThreadSelector:#selector(run) toTarget:self withObject:nil];
}
// This method is called from inside the new thread
-(void) playNote:(Note *)theNote withTemplate:(NSString *)theTemplate X:(int)x andY:(int)y {
NSLog(#"before timer");
// The timer doesn't fire in here. (But the print statements do.)
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
NSLog(#"after timer");
}
I would really like to (read:need to?) fire the timer in the subthread because it is going to be used to stop notes from playing (and all of the note playing needs to happen in a subthread).
I must be missing something with how a NSTimer runs in subthreads...
Premature thanks for the help!
A timer won't fire unless it is scheduled in a run loop. You'll also want to avoid spinning up threads willy-nilly.
Unless your run method fires up a run loop, your timer won't fire.
A better solution, btw, might be to use GCD's dispatch_after(). It is lighter weight than a thread, but it depends on what you need to do. In general, though, queues are a more efficient means of adding concurrency to an app.
Grabbing the code from the comment for proper formatting (good find):
double delayInSeconds = .2;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"dispatch popped");
});
One additional note; you might consider using one of the global concurrent queues. They won't block when the Main Event Loop might block. Of course, if you are updating the UI, you have to go back to the MEL anyway.
You should add the NSTimer to currentRunLoop. Your playNote method should look like this:
-(void) playNote:(Note *)theNote withTemplate:(NSString *)theTemplate X:(int)x andY:(int)y
{
NSLog(#"before timer");
// The timer doesn't fire in here. (But the print statements do.)
NSTimer myTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(#"after timer");
}
That should do it :)
Add the timer to the current runloop, and run.
-(void) handleTimer:(NSTimer *)theTimer {
NSLog(#"timer fired");
}
-(void) startMyThread {
// If I put timer in here, right before I start my new thread, it fires.
// [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
[NSThread detachNewThreadSelector:#selector(run) toTarget:self withObject:nil];
}
// This method is called from inside the new thread
-(void) playNote:(Note *)theNote withTemplate:(NSString *)theTemplate X:(int)x andY:(int)y {
NSLog(#"before timer");
// The timer doesn't fire in here. (But the print statements do.)
NSTimer *Timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
NSLog(#"after timer");
[[NSRunLoop currentRunLoop] addTimer:Timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
Try to addTimer: within the [NSRunLoop mainRunLoop], not the current one !

UIBarButtonItem after animating disappears

I have the UIBarButtonItem (configurated in interface builder). If a user click this button, the "heavy process" will be started and for better user experience I want to change this button with (UIActivityIndicatorView). I do it in the following way:
self.indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
indicator.hidesWhenStopped = YES;
[self.heavyBarButton initWithCustomView:self.indicator];
[self.indicator startAnimating];
[NSThread detachNewThreadSelector:#selector(animateHeavyProcess) toTarget:self withObject:nil];
animateHeavyProcess:
[self heavyProcess];
[self.indicator stopAnimating];
UIBarButtonItem *originalButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"maximize.png"] style:UIBarButtonItemStylePlain target:self action:#selector(startProcessClick:)];
self.heavyBarButton = originalButton;
[originalButton release];
What happens: after a user clicks the BarButton animation will be started and after processing button disappears. However, I want that the original button will be shown again.
what if you use Grand Central Dispatch for the heavy process? I would think it's more convenient. But remember that you can not use any of the UI in that block. Here is an example: iphone ios running in separate thread
You should not be doing UI updates in a secondary thread; calls to UIKit should be on the main thread.
You can split out the UI updating part of your code:
- (void)restoreBarButtonItem
{
[self.indicator stopAnimating];
UIBarButtonItem *originalButton = [[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:#"maximize.png"]
style:UIBarButtonItemStylePlain
target:self
action:#selector(startProcessClick:)];
self.heavyBarButton = originalButton;
[originalButton release];
}
And then in your heavy process (running on the secondary thread) just call this new UI update method on the main thread:
- (void)animateHeavyProcess
{
[self heavyProcess];
[self performSelectorOnMainThread:#selector(restoreBarButtonItem:)
withObject:nil
waitUntilDone:NO];
}
You should use UIToolbar's setItems:animated: method to replace buttons.

how to make a UIAlertView to hide automatically without User Interaction?

When the focus in a particluar rect of the screen , it need to show up a message and need to hide automatically . Whether alertview will be flexible or any other way is there to implement the stuffs in iPhone and iPad .
You can use timer to close alert after some time, for example:
[[NSTimer scheduledTimerWithTimeInterval:2
target:self
selector:#selector(closeAlert:)
userInfo:nil
repeats:NO] retain];
For more information look here: NSTimer Class Reference
You can dismiss the alert after displaying for some seconds.
Something like this : (Dismiss after 5 seconds)
UIAlertView *yourAlert = [[UIAlertView alloc]initWithTitle:#"title" message:#"message" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
[yourAlert show];
[self performSelector:#selector(dismiss:) yourAlert afterDelay:5.0];
-(void)dismiss:(UIAlertView*)alert
{
[alert dismissWithClickedButtonIndex:0 animated:YES];
}

How to make the splash screen sleep for 4 seconds until the user touches the screen in iphone sdk

I am using splash screen for my application when it starts begining and I made it to sleep for 4 seconds.My need is if user taps on the splash screen before the 4 seconds he should navigate to the next screen.Can any one suggest me with some ideas.
My code I tried was:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
// Override point for customization after application launch
[self createEditableCopyOfDatabaseIfNeeded];
[self initializeDataStructures];
mainController = [[FavoritesViewController alloc] init] ;
navController = [[UINavigationController alloc] initWithRootViewController:mainController];
navController.navigationBar.barStyle = UIBarStyleBlack;
[window addSubview:navController.view];
splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
splashView.image = [UIImage imageNamed:#"default.png"];
[window addSubview:splashView];
[window bringSubviewToFront:splashView];
sleep(4);
// Do your time consuming setup
[splashView removeFromSuperview];
[splashView release];
[window makeKeyAndVisible];
}
Anyone's help will be much appreciated.
Thank's all,
Monish.
Well, you haven't shown us any code or told us what your real problem is, but in pseudo-code I would do something like this:
- (void) start {
[self showSplashScreen];
fourSecondsRunning = YES;
// In four seconds, call stop.
[self performSelector:#selector(stop) withObject:nil afterDelay:4.0];
}
- (void) stop {
// View wasn't tapped during the last 4 seconds. Do something.
fourSecondsRunning = NO;
[self hideSplashScreen];
[self doSomething];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (fourSecondsRunning) {
fourSecondsRunning = NO;
// Touched within the four seconds. Make sure "stop" is not called.
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget:self];
[self hideSplashScreen];
[self goToNextScreen];
}
}