UIApplicationDidBecomeActiveNotification and setRightBarButtonItem:animated: - ios

TL;DR Update: Basically what I need is to delay my code until iOS finishes its "app startup" animation.
I would like to animate content of a navigation bar when my app becomes active. In my controller, I'm subscribed to UIApplicationDidBecomeActiveNotification and use setRightBarButtonItem:animated: to perform the change.
The problem is that the change is not animated.
I did some experimentation and discovered that if I wait a little ([NSThread sleepForTimeInterval:.3]), it's animating without any issues.
Here is a simple view controller demonstrating the problem:
#interface TESTViewController ()
#property (strong, nonatomic) IBOutlet UINavigationBar *navigationBar;
#end
#implementation TESTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UINavigationItem *item = [UINavigationItem new];
UIBarButtonItem *oldItem = [[UIBarButtonItem alloc] initWithTitle:#"Old" style:UIBarButtonItemStyleBordered target:nil action:NULL];
[item setRightBarButtonItem:oldItem];
[[self navigationBar] setItems:#[item] animated:NO];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidBecomeActiveNotificationAction:) name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (void)applicationDidBecomeActiveNotificationAction:(NSNotification *)notification
{
// [NSThread sleepForTimeInterval:.3];
UIBarButtonItem *newItem = [[UIBarButtonItem alloc] initWithTitle:#"New" style:UIBarButtonItemStyleBordered target:nil action:NULL];
[[[self navigationBar] items][0] setRightBarButtonItem:newItem animated:YES];
}
#end
I'd like find a better solution than blocking the thread or performing the change after a fixed delay.

Simply use dispatch_after on the main queue instead of calling
+[NSThread sleepForTimeInterval:] class method.
Pass your animation as a block and it should work perfectly.

When posting notifications, I don't use your methods.
I use:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changeButton) name:#"changeButton" object:nil];
on the receiving end.
And on the posting end, I call
[[NSNotificationCenter defaultCenter] postNotificationName:#"changeButton" object:nil];
Try moving your code to the method:
-(void)changeButton{
UIBarButtonItem *newItem = [[UIBarButtonItem alloc] initWithTitle:#"New" style:UIBarButtonItemStyleBordered target:nil action:NULL];
[[[self navigationBar] items][0] setRightBarButtonItem:newItem animated:YES];
}
Hope this helps!

Have you tried to performSelectorAfterDelay?
Try something like this:
- (void)applicationDidBecomeActiveNotificationAction:(NSNotification *)notification{
[self performSelector:#selector(yourAnimationAction) withObject:nil afterDelay:1];
}
- (void)yourAnimationAction{
//Set your NavAnimation here
[self.navigationItem setRightBarButtonItem:theItem animated:TRUE];
}
Just change the Delay to fit your apps wakeup delay... maybe this will take the effect?

Try by moving
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidBecomeActiveNotificationAction:) name:UIApplicationDidBecomeActiveNotification object:nil]; to viewDidLoad: method. I think object is not registered for "UIApplicationDidBecomeActiveNotification" while it is fired.

Just call viewDidAppear: manually from within your applicationDidBecomeActiveNotificationAction: method. Place your animation code in the viewDidAppear: method. Hope this helps.

Related

Change Scrollbar background from another view IOS

This is HomeView
enter image description here
When Someone tapped in 'Banking' button . Action will load BaseviewController .
This is the button action.
HomeViewController.m
BaseViewController *clickbutton = [[BaseViewController alloc] initWithNibName:#"BaseViewController" bundle:nil] ;
[self presentViewController:clickbutton animated:YES completion:nil];
This is BaseView
enter image description here
There is a scrollbar which will load every view . The scrollbar
background is Green . And change the background in next here is the
problem .
BaseViewController.m
-(void)ViewDidLoad{
PriorityViewController *obj ;
UINavigationController *nav;
obj = [[ PriorityViewController alloc] initWithNibName:#"PriorityViewController" bundle:nil] ;
nav = [[UINavigationController alloc] initWithRootViewController:obj];
nav.view.frame = rect;
[self.view addSubview:self.nav.view];
}
When tapped 'Service Request' .
OtherBankTransferViewController *obj;
obj = [[OtherBankTransferViewController alloc]initWithNibName:#"OtherBankTransferViewController" bundle:nil];
[self.navigationController pushViewController:obj animated:YES ];
This will load same as like second image I uploaded here .
I just want to change my background color of the scrollbar
I want to change scrollbar into black color .
I have no idea . If someone explain me !
Thanks in advance .
APPROACH 1:
Instead of initialising scrollBar in each ViewController, why not alloc init it in BaseView Controller and pass instance of it to all View Controller.
Since now all ViewControllers will have same instance it will be easy to change background of the scrollBar.
APPROACH 2:
You can post notifications whenever you want to change the scrollBar color,
add observer for the notification you are posting and with the notification you can pass the color you want to be set for all scrollBar.
Posting Notification when you want to change color
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[UIColor blackColor] forKey:#"someKey"];
[[NSNotificationCenter defaultCenter] postNotificationName: #"TestNotification" object:nil userInfo:userInfo];
Adding Observer in viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
Method to handle received Notification
- (void) receiveTestNotification:(NSNotification *) notification{
NSDictionary *userInfo = notification.userInfo;
UIColor *backGroundColor = [userInfo objectForKey:#"someKey"];
// assign this color to your scrollBar in each ViewController
}
REMOVING OBSERVER in viewDidUnload
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"TestNotification" object:nil];

Notify UI update in AppDelegate (UITabBarController badge)

In my app, UITabBarController is an initial ViewController build in storyBoard. I would like to observe data updating in my whole app. So I add an observer in appDelegate...
AppDelegate.m
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(shipCountBadge:) name:kShipCountBadge object:nil];
- (void)shipCountBadge:(NSNotification *)notification{
Groups *vc = [[Groups alloc] init];
[vc addBadgeCount:notification.object];
}
Group.m
- (void)addBadgeCount:(NSNumber *)count{
NSLog(#"d",[count intValue]);
[[self.tabBarController.tabBar.items objectAtIndex:2] setBadgeValue:[NSString stringWithFormat:#"%d",[count intValue]]];
}
Its doesn't change the UI at all. I would like to know how to update UI in AppDelegate actually? Anyone has an idea :)?

Game fps shutting down

I created View Controller for Game Over Scene. fps is shutting down when I restart game from Game Over ViewController. First time my fps is 30+, when I restart game it shutting down to 20, and I restart again it shutting down to 10...
and sometimes i see this error : Unbalanced calls to begin/end appearance transitions for GameOverViewController: 0x9bb8490>.
ViewController.m
- (void)awakeFromNib {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(goToGameOverViewController:)
name:#"GoToGameOverViewController"
object:nil];
}
-(void)goToGameOverViewController:(NSNotification *) notification {
GameOverViewController *gameOverController = [self.storyboard instantiateViewControllerWithIdentifier:#"GameOverViewController"];
[self.navigationController pushViewController:gameOverController animated:NO];
}
------------------------------------------------------------------------------
MyScene.m
- (void)gameOver {
[[NSNotificationCenter defaultCenter]
postNotificationName:#"GoToGameOverViewController" object:self];
}
GameOverView.m
-(void)playPressed
{
GameViewController *gameController = [self.storyboard instantiateViewControllerWithIdentifier:#"GameViewController"];
[self.navigationController pushViewController:gameController animated:YES];
}
-(void)createPlayButton
{
UIButton *playBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.view addSubview:playBtn];
playBtn.frame = CGRectMake(100, 100, 200, 44);
[playBtn setTitle:#"Play Again" forState:UIControlStateNormal];
[playBtn sizeToFit];
[playBtn addTarget:self action:#selector(playPressed) forControlEvents:UIControlEventTouchUpInside];
}
A couple core issues you have with your code and approach:
You continually push UIViewControllers onto your UINavigationController. Instead, maybe after a game finishes, you call popToRootViewControllerAnimated: or other. Every UIViewController you push on the stack stays around forever.
You never remove observers from NSNotificationCenter. That means each time you finish a level, each viewController is trying to push a GameOver viewController. That is what is causing the "unbalanced..." warnings. If you follow my advice from #1, you should only need to override dealloc and call the following code to fix that issue.
In playPressed use this code:
GameViewController *gameController = [self.storyboard instantiateViewControllerWithIdentifier:#"GameViewController"];
[self.navigationController setViewControllers:#[gameController] animated:YES];
And in later in GameViewController
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self]
}
This will make sure your ViewController, and therefore your scene, get deallocated and you don't have multiple ViewController trying to push games at the same time.
Make sure you free your SKScene when leaving from ViewController:
-(void)goToGameOverViewController:(NSNotification *) notification {
[((SKView *)self.view) presentScene:nil];
GameOverViewController *gameOverController = [self.storyboard instantiateViewControllerWithIdentifier:#"GameOverViewController"];
[self.navigationController pushViewController:gameOverController animated:NO];
}

PresentViewController takes a lot of time

I have a really strange situation: a view controller, called by presentViewController takes a lot of time to load. I mean that preparation stage takes a lot of time, though whole animation performed quickly. Going back from the presented controller to the previous one is fine.
What clues to I have?
First af all, I prepared my next view controller in first viewWillAppear:
METAddEventViewController *controller = [self.storyboard
instantiateViewControllerWithIdentifier:#"addEventView"];
self.nextController =
[[UINavigationController alloc] initWithRootViewController:controller];
Where self.nextController is nonatomic and strong.
So, my method that calls the next controller is pretty simple:
- (IBAction)addMeetingButtonPressed:(id)sender {
[self.addMeetingButton setImage:[UIImage imageNamed:#"addMeeting-pressed#2x.png"] forState:UIControlStateNormal];
[self presentViewController:self.nextController animated:YES completion:^{
NSLog(#"Completed");
}];
}
METAddEventViewController is just a UITableViewController with static cells of 4: 2 Basic cells, 1 cell with UIDatePicker and 1 with UITextView.
So, my viewDidLoad and viewWillAppear:
- (void)viewDidLoad
{
self.appointMeetingButton.enabled = NO;
self.title = #"Create Meeting";
self.commentaryView.delegate = self;
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[self navigationController] setNavigationBarHidden:NO animated:NO];
self.commentaryView.text = #"Enter comment for the meeting if needed";
self.commentaryViewFilled = NO;
self.commentaryView.textColor = [UIColor lightGrayColor];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateSelectedPersonCalled:)
name:#"updateSelectedPersonLabel"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateSelectedPersonCalled:)
name:#"updateSelectedLocationLabel"
object:nil];
[self.datePicker addTarget:self action:#selector(meetingAppointable) forControlEvents:UIControlEventValueChanged];
}
I ran Profiling and noticed that the most time consuming operations are allocation ones. Though, both viewDidLoad and viewWillAppear of METAddEventController are fine - about 10 ms. Most of the time in them takes configuring self.commentaryView
What can I do? Which additional information can I present to find the solution?

Switching the view after applicationDidBecomeActive

I am currently developing an app that will need to return to another view after running in the background for more than five minutes. In order to do this, I will have to have a timer running in the background after the the Home button has been pressed or in case of an interruptions such as an SMS or a telephone call, then, after five minutes the app will need to go to another view. I know that the applicationDidBecomeActive method will have to be used, but how? I also know that a view can be refreshed in applicationDidBecomeActive but how is that done? ( I am not using storyboards.)
Actually, you should do this with the applicationDidEnterBackground applicationWillEnterForeground delegate methods of UIAppDelegate or by registering to the appropriate system notifications (didBecomeActive is called on other occasions too, such as when a UIAlertView is dismissed from screen).
This should be something in the lines of (may include syntax problems, I'm textbox-coding here):
In the viewDidLoad method of your view controller, register to the notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
Implement the willEnterForeground: and didEnterBackground: methods. In willEnterForeground: sample the current time using CACurrentMediaTime() or [NSDate date]. In didEnterBackground: sample the time again and calculate the time difference. Since this method is implemented inside the view controller, you can manipulate the subviews of your self.view as you wish.
Do not forget to remove the observers on your dealloc method (viewDidUnload is deprecated since iOS 6.0, so beware):
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]
Here's how you can do it. I just made a test app and I confirm that it works beautifully. Code:
#import "AppDelegate.h"
#import "ViewController.h"
#import "theView.h"
NSTimer *theTimer;
UIViewController *theViewController;
BOOL theTimerFired = NO;
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Set a 5 minute timer (300 seconds)
theTimer = [NSTimer scheduledTimerWithTimeInterval:300.0 target:self selector:#selector(presentVC) userInfo:nil repeats:NO];
}
- (void)presentVC
{
// Set a boolean to indicate the timer did fire
theTimerFired = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Check to see if the timer did fire using the previous boolean we created
if (theTimerFired == YES)
{
theViewController = [[UIViewController alloc]initWithNibName:#"theView" bundle:nil];
[self.viewController presentViewController:theViewController animated:YES completion:NULL];
[theTimer invalidate];
}
}
#end

Resources