I am trying to make a splash screen with timer and segue, that wil be performed after 2 seconds.It is a piece of code frome my SplashScreenClass.m. What is wrong?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(performSegue)
userInfo:nil
repeats:NO];
}
-(void)performSegue{
[self performSegueWithIdentifier:#"splash" sender:self];
In Apple's Human Interface Guidelines, it is recommended that splash screens not be used. Your app could get rejected for this reason.
It's not the best solution to use timer for solving this problem. I recommend performing selector after delay instead. It's quite easier to use. Just put this line in your viewDidLoad method.
[self performSelector:#selector(performSegue) withObject:nil afterDelay:2];
Related
I've got a login screen I want to hide like:
[self dismissModalViewControllerAnimated:true];
But the problem is that I need to display it like this:
-(void)viewDidAppear:(BOOL)animated {
[self presentModalViewController:loginScreen animated:false];
}
Which means I'll flash the current screen before I popup the login screen.
So what I'm looking for is a way to show the login screen instantly and transition to the main screen with the same animation as dissmissModalViewControllerAnimated:true.
You should be able to just disable the animation so its instant?
[self presentModalViewController:loginScreen animated:NO];
Do it in viewWillAppear if it still flashes briefly.
What I've done (maybe not the best solution):
- (void)viewDidLoad
{
// Initial set to hidden for avoiding a flickering UI
self.view.hidden=YES;
}
-(void)viewWillAppear:(BOOL)animated
{
[NSTimer scheduledTimerWithTimeInterval:0 block:^{
[self presentModalViewController:self.loginViewController animated:NO];
} repeats:false];
}
And just before you dispatch the ModalViewController you set self.view.hidden=NO.
See https://github.com/jivadevoe/NSTimer-Blocks for the NSTimer using blocks
I'm having some random crashes at this part of my code:
-(void) goBack {
[self performSelectorInBackground:#selector(addActivityIndicator) withObject:nil];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)addActivityIndicator {
#autoreleasepool {
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:activityView];
[activityView startAnimating];
self.navigationItem.leftBarButtonItem = barButton;
}
}
When I want to exit the screen where these method exists, the other ViewController have to process some data. To inform the user that processing is occurring I add an activity indicator to the left button in my navigation bar.
The problem is that sometimes I get an exc_bad_access in addActivityIndicator method. The frequency is very random, sometimes the XCode shows the error at the end of #autoreleasepool, sometimes at the line self.navigationItem.leftBarButtonItem = barButton;
I imagine that sometimes my viewController is destroyed but the thread is still running and try to access the navigationItem of a object that don't exists anymore. But I'm not sure if that is the problem and I don't know how to fix it.
I'm using ARC in my project and this problem occurs in all iOS versions that I tested.
Please, anyone can explain me what is happening and how can I fix this?
Thanks.
You should never do UIKit stuff in the background.
By calling [self performSelectorInBackground:#selector(addActivityIndicator) withObject:nil]; you are updating the UI on a background thread. You should only ever update the UI on the main thread.
Edit
Based on your comment you are trying to have the UI update before the view pops. The way to do that would be:
[self addActivityIndicator]
[navigationController performSelector:#selector(popViewControllerAnimated:) withObject:[NSNumber numberWithBool:YES] afterDelay:0];
You could also look into dispatch_after
I have four NSTimers that work fine. The only problem is that two should fire and then the other two should fire in the middle of the first two. Here is my code:
initTimer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector: #selector(initText) userInfo:nil repeats:YES];
animateTimer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector: #selector(textGlow) userInfo:nil repeats:YES];
initTimer1 = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector: #selector(initText1) userInfo:nil repeats:YES];
animateTimer1 = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector: #selector(textGlow1) userInfo:nil repeats:YES];
The initText and initText1 methods create a UILabel with lazy instantiation and place them on the screen on the same spot. The textGlow and textGlow1 methods animate the text to make it grow bigger and fade away. I want initText1 and textGlow1 to fire in the middle of initText and textGlow. For example, let's say each of these methods takes 2 seconds (I set the animation duration to 2 seconds). I want initText and textGlow to fire, and then one second in, initText1 and textGlow1 to fire.
Thanks. If you need any of my other code to help answer the question, just ask.
----------EDIT-------------
#MDT That worked but there is a problem. What I did was I placed the code above of initTimer1 and animateTimer1 in new functions called initTimer1Wrapper and animateTimer1Wrapper. Then I did your code [self performSelector:#selector(initText1Wrapper) withObject:nil afterDelay:1.0]; and duplicated it but changed the selector to animateText1Wrapper. What happens is initText and textGlow will fire as planned, and then also as planned initTimer1 and animateTimer1 will fire one second in, but what also happens unexpectedly is that initText and textGlow do not finish their remaining one second; the animations just stop. I believe this is because they are UIView animations with [UIView beginAnimation:#"name" context:nil] and [UIView commitAnimations] and more then 1 UIView animations can't run at the same time.
To fix this, should I use Core Animation (which I do not know anything about) or is there another solution? Thanks.
----------EDIT2-------------
Also, just if it is important for you to know, I will dismiss this animation with a UIGestureRecognizer.
Something like:
if(screenIsTapped){
[initTimer invalidate];
[animateTimer invalidate];
[initTimer1 invalidate];
[animateTimer1 invalidate];
}
Try placing something like this inside initText or textGlow.
[self performSelector:#selector(initText1) withObject:nil afterDelay:1.0];
I think you should do this with 2 timers instead of 4. Inside the methods, initText and initText1 just call textGlow and textGlow1, respectively, using performSelector:withObject:afterDelay: with whatever delay you want. And, do you really want these timers to be repeating? Do you want to keep creating new UILabels?
I have the following code to create an NSTimer which should update a label each time it fires:
.h file
#interface Game : UIViewController
{
NSTimer *updateTimer;
UILabel *testLabel;
int i;
}
-(void)GameUpdate;
#end
.m file
#implementation Game
-(void)GameUpdate
{
i++;
NSString *textToDisplay = [[NSString alloc] initWithFormat:#"Frame: %d", i];
[testLabel setText:textToDisplay];
NSLog(#"Game Updated");
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:#selector(GameUpdate) userInfo:nil repeats:YES];
}
//other methods (viewDidUnload, init method, etc.)
#end
When I run it, a label appears in the top that says "0" but does not change. It makes me believe I missed something in how the NSTimer is to be setup. What did I miss?
I used breakpoints and (as you can see) logging to see if the method is actually running, rather than some other error.
I was having a similar problem, and it had a different root cause, related to the run loop. It's worth noting that when you schedule the Timer with the code:
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:#selector(GameUpdate) userInfo:nil repeats:YES];
The timer will get scheduled with the current thread's runLoop. In your case, because you make this call within the viewDidLoad, it is the main thread, so you are are good to go.
However, if you schedule your timer with a thread other than the main thread, it will get scheduled on the runLoop for that thread, and not main. Which is fine, but on auxiliary threads, you are responsible for creating and starting the initial run loop, so if you haven't done that - your callback will never get called.
The solution is to either start the runLoop for your auxiliary thread, or to dispatch your timer start onto the main thread.
to dispatch:
dispatch_async(dispatch_get_main_queue(), ^{
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:#selector(GameUpdate) userInfo:nil repeats:YES];
});
To start a runloop:
After creating a thread using your API of choice, call CFRunLoopGetCurrent() to allocate an initial run loop for that thread. Any future calls to CFRunLoopGetCurrent will return the same run loop.
CFRunLoopGetCurrent();
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:#selector(GameUpdate) userInfo:nil repeats:YES];
Your callback must have this signature:
-(void)GameUpdate:(NSTimer *)timer
This is explicitly in the docs. And the #selector() reference when you setup the timer should be #selector(GameUpdate:) (notice the trailing :).
Try that.
Just in case anyone stumbles across this, I want to point out that this:
[[NSString alloc] initWithFormat:#"Frame: %d", i];
Needs memory management.
Safely replace with:
[NSString stringWithFormat:#"Frame: %d", i];
for the same effect but no need for memory management.
P.S. At time of writing I cannot comment on the original post, so I've added this as an answer.
EDIT: As adam waite pointed out below, this isn't really relevant anymore with the widespread usage of ARC.
I have had a little bit different issue with NSTimer - scheduled method call was ignored during UITableView scrolling.
Timer had been started from main thread. Adding timer explicitly to main run loop resolved the problem.
[[NSRunLoop mainRunLoop] addTimer:playbackTimer forMode:NSRunLoopCommonModes];
Solution found here https://stackoverflow.com/a/2716605/1994889
UPD: CADisplayLink fits much better for updating UI.
According official documentation, CADisplayLink is a:
Class representing a timer bound to the display vsync.
And can be easily implemented like:
playbackTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector(updateUI)];
[playbackTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
and removed like
if (playbackTimer) {
[playbackTimer removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
playbackTimer = nil;
}
I'm having some trouble with a UITableView. It loads perfectly the first time I open it, but after the second try its not getting into the viewDidLoad method which causes the data not be refreshed on my tableview. I also made the proper release on my dealloc method. Any ideas about this? I've looked thru google but I didnt get anything useful. Thanks a lot for all the help you can provide me!
Here are my viewDidLoad and viewDidUnload methods
- (void)viewDidLoad
{
[super viewDidLoad];
saved_news.rowHeight =85;
addButton = [[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStyleBordered target:self action:#selector(edititems:)];
[self.navigationItem setLeftBarButtonItem:addButton];
[self setSaved:[CoreData sharedInstance].selectItems];
[saved_news reloadData];
}
- (void)viewDidUnload
{
[super viewDidUnload];
saved = nil;
saved_news = nil;
addButton =nil;
}
viewDidLoad and viewDidUnload are typically called only once during the lifetime of a controller (unless there's a low memory situation). I think what you are looking to use here are the viewDidAppear: and viewDidDisappear: methods.
Call
- (void)viewWillAppear:(BOOL)animated;
- (void)viewDidAppear:(BOOL)animated;
as you require load table here
[Yourtablename reloaddata];
Hope this helps..