With multi-tasking in iOS4, the home button puts the app into background and when it comes back into foreground I want the View Controller to 'refresh' (and hence viewWillAppear to be called). I put this in the app delegate thought this should work but nothing happens
- (void)applicationDidFinishLaunching:(UIApplication *)application {
//
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[viewController.view reloadInputViews];
}
Can anyone help me with forcing a view controller to 'execute'/refresh when it is already showing?
You could call viewWillAppear explicitly e.g.
- (void)applicationWillEnterForeground:(UIApplication *)application {
[viewController.view reloadInputViews];
[viewController viewWillAppear];
}
Related
I have an app that works in all directions.
For iOS 6 I use
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
And for iOS 5
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
This part seems to work.
Then, because I want to stop the rotation animation, I implement these methods:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
[UIView setAnimationsEnabled:NO];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[UIView setAnimationsEnabled:YES];
}
Everything is OK, but if I show an UIAlert and then the device rotates, the background black shadows appears in the wrong direction.
This happens only in iOS 6 and not on iOS 5, on the actual device and in the simulator, and only if I prevent the rotation from animating.
Any idea?
EDIT: Here my rootViewController
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Other things
self.rootViewController = [[[RootViewController alloc] initWithNibName:#"RootViewController" bundle:[NSBundle mainBundle]] autorelease];
self.window.rootViewController = self.rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
Window is stored in NIB.
To close this question...
I don't know what was the problem causing this issue, I suppose something related to iOS version.
I solved it by implementing my custom alert (a subclass of UIViewController).
How to achieve this stuff below? Please give me some guidance for it. I describe my issue below.
When I tap home button and remove app from tray and while I am opening app I get the login screen. I know how to use NSUserDefaults well.
But my issue is that when I navigate 3rd or 4th viewController and I press Home Button and remove app from tray, Then whenever I open app than I want to open with last open viewController.
Also same when my app is Crashing and I am opening it again then I want to open app with last open viewController state.
So I just want to know that is that possible or not? If yes, then please guide me how to achieve this stuff.
Thank you
Yes, both cases are possible.
On crash, you can use UncaughtExceptionHandler to perform some code. In you app delegate, register you handler like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
// Other didFinishLaunchingWithOptions code
And add your handler method to the same .m file
void uncaughtExceptionHandler(NSException *exception)
{
// App crashed, save last selected tabbar index to the to the NSUserDefaults
[[NSUserDefaults standardUserDefaults] setInteger:tabBarController.selectedIndex forKey:#"LastSelectedTabbarIndex"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
While app runs, to keep track of last selected tab bar controller, use UITabBarControllerDelegate and save newly selected tabbar's index to NSUserDefaults. Short example:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSUInteger tabIndex = [[tabBarController viewControllers] indexOfObject:viewController];
// I have newly selected index, now save it to the NSUserDefaults
}
This code will save last selected tabbar's index to the NSUserDefaults every time tabbar's selected index changes.
Finally, when you app starts (in your didFinishLaunchingWithOptions), read last saved tabbar index from NSUserDefaults and set tabbar's selected index accordingly
self.tabBarController.selectedIndex = lastSelectedIndexFromDefaults;
Edit:
If you also need to restore UINavigationControllers controllers stack, its pretty difficult task. I give you just a quick overview what comes to my mind.
There are 2 cases:
You have custom view controllers initializers and need to pass custom object to those controllers - In this case, its almost impossible (in some reasonable time) implement this
You use only -init or -initWithNibName...: to initialize view controllers in navigation stack. You could enumerate controllers from the root UINavigationController of the tab, get their classes names using NSStringFromClass and save them to NSUserDefaults. On apps start, you would reverse procedure (initialize controllers using their names strings read from NSUserDefaults using something like this: UIViewController *vc = [[NSClassFromString(#"aa") alloc] init];).
I understand you are ok with the code part so i will just give my suggestion
on viewDidLoad of every view controller set a nsuserdefault value of the top most object on navigation array.
if their are not too many branches then you can manage the push at root view controller easily
This is not the proper answer but you can use it for Navigating view after launching.
In AppDelegate file use below codes:---
#import "NewSAppDelegate.h"
#import "NewSViewController.h"
static NewSAppDelegate *globalSelf;
#implementation NewSAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[NewSViewController alloc] initWithNibName:#"NewSViewController" bundle:nil];
self.navController=[[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = self.navController;
[self.window makeKeyAndVisible];
globalSelf=self;
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
return YES;
}
void uncaughtExceptionHandler(NSException *exception)
{
UIViewController *currentVC = globalSelf.navController.visibleViewController;
[[NSUserDefaults standardUserDefaults] setObject:NSStringFromClass(currentVC.class) forKey:#"lastVC"];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIViewController *currentVC = self.navController.visibleViewController;
[[NSUserDefaults standardUserDefaults] setObject:NSStringFromClass(currentVC.class) forKey:#"lastVC"];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"appDidBecomeActive" object:nil];
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
In your login viewController's init method add an observer for notification and in notification method , you can apply if conditions for viewController's name received.and push to that viewController on launching LoginView controller as:---
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(openLastVC)
name:#"appDidBecomeActive"
object:nil];
// Custom initialization
}
return self;
}
-(void)openLastVC
{
NSLog(#"val ==%#",[[NSUserDefaults standardUserDefaults] valueForKey:#"lastVC"]);
if ([[[NSUserDefaults standardUserDefaults] valueForKey:#"lastVC"] isEqualToString:#"GhachakViewController"]) {
GhachakViewController *gvc=[[GhachakViewController alloc] initWithNibName:#"GhachakViewController" bundle:nil];
[self.navigationController pushViewController:gvc animated:NO];
}
}
May this help you....
This is the code I'm using to present a modal view when the app first starts
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
Security *security = [[Security alloc] initWithNibName:#"Security" bundle:nil];
[self.tabBarController.selectedViewController presentModalViewController:security animated:YES];
[security release];
return YES;
}
This is what the log says
Unbalanced calls to begin/end appearance transitions for <UITabBarController: 0x171320>.
Is there a better way to achieve this?
Also I have this method in my app delegate
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
if (viewController == [tabBarController.viewControllers objectAtIndex:2]) {
//The Log Out tab locks the app by presenting a modalviewcontroller that can't be dismissed unless there is a password.
Security *security = [[Security alloc] initWithNibName:#"Security" bundle:nil];
[self.tabBarController presentModalViewController:security animated:YES];
[security release];
return NO;
} else {
return YES;
}
}
Basically one of the options on my tabbarcontroller is a logout button. The above code works fine and doesn't throw a warning to the log.
Why are you presenting it from your tab bar controller? Assuming your above code is from a UIViewController, try
[self presentModalViewController:security animated:YES];
or
[self.navigationController presentModalViewController:security animated:YES];
Background: App has a shake to go home feature. Home view Only supports portrait.
If you shake a bit harder than usual, the view that you are on starts to rotate (which is fine) , but then it detects a shake and does a popViewControlller to the home view. When it does this it loads the navigation controller just fine, but the view under (the home content) gets loaded behind the bar and is stretched up (it's basically loading underneath the navigation bar, so it gets stretched up)
The back button handles this just fine from landscape to portrait (since its not mid transitions)
How should I go about handling this orientation change (from the shake) so I can pop back into the root view controller, without the view loading under the navigation bar?
Edit:What's happening is the content thinks that it has the entire view to load, so it stretches itself to take the entire screen, not realizing theres a navigationbar above it. I can tell since the images loading are stretched out
added a bounty of 50.
Edit Here's How I'm detecting Shakes and Popping
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake )
{
UINavigationController *navController = self.navigationController;
[[self retain] autorelease];
HomeViewController *home = [[HomeViewController alloc]init];
[navController popViewControllerAnimated:YES];
home.title =#"Home View Controller";
[home release];
}
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
Here's my App Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
navController = [[UINavigationController alloc]init];
[self.window addSubview:navController.view];
HomeViewController *home = [[HomeViewController alloc]init];
[[self home] setFrame:[[UIScreen mainScreen] applicationFrame]];
I'll include a mockup here.
Normal View:
Stretched View After a Shake/Pop:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
I'm a bit puzzled by your code so I'd really suggest starting from the beginning. As Lukya mentioned, there's no reason to recreate the HomeViewController. I'm also baffled by the "[[self retain] autorelease];" bit. That shouldn't be necessary unless you're doing something incorrectly elsewhere.
So I would start with this... In application:didFinishLaunchingWithOptions: do something like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
HomeViewController *home = [[[HomeViewController alloc] init] autorelease];
UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:home] autorelease];
[self.window addSubview:navController.view];
}
The window will retain a your nav controller and the nav controller will retain your HomeViewController.
Then in motionEnded:withEvent: do something like:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake)
{
[self.navigationController popViewControllerAnimated:YES];
}
}
That should be it.
If that does not work then can you give any other info? For example, does HomeViewController implement (and return YES) in shouldAutorotateToInterfaceOrientation:? If so, can you return no so it doesn't rotate since your first line says "Home view Only supports portrait"?
Edit: An example of willRotateToInterfaceOrientation:duration: and didRotateFromInterfaceOrientation: as well.
In the header for whatever controller you're detecting shakes in add a boolean:
BOOL isRotating;
In your implementation file add the two UIViewController methods we want to override -- something like:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
isRotating = YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
isRotating = NO;
}
Now, do something like this for your event handler:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake && !isRotating)
{
[self.navigationController popViewControllerAnimated:YES];
}
}
in your home view controller's xib, go to the inspector for the view and set top bar as navigation bar.. and in view did load set self.navigationBarHidden = NO;...
NOTE:
there are many thing wrong with the code you've posted.. but none of them causes the orientation problem... in fact this seems to be the only code you need in that method:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake)
{
[navController popViewControllerAnimated:YES];
}
}
so you might want to change this code as well..
Have you tried calling [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:YES]; in your home view controller? You could also try to place this in where you detect a shake.
I have come across this issue with underlapping the navigation bar. I am not sure what causes it but you can work around it by calling,
[[self loadingView] setFrame:[[UIScreen mainScreen] applicationFrame]];
after the problem view is added to window in the application delegate.
I have made a view based application which is loading a default view ...
My default view is a splash screen ..
What I want to achieve is once default view (splash view) finished loading, after few seconds it loads another view which is either a privacy policy or application screen.
Code in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
No Change as usual ...
Above code load a view from splashscreen.xib file
Following code is in splashscreen.m
- (IBAction)loadPrivacyScreen {
NSLog(#"Switching To Another View");
PrivacyPolicyView *modal = [[PrivacyPolicyView alloc]initWithNibName:nil bundle:nil];
modal.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:modal animated:YES];
[modal release];
}
- (void)viewDidLoad {
[super viewDidLoad];
sleep(3);
[self loadPrivacyScreen];
// Do any additional setup after loading the view from its nib.
}
After three second it does get in to the loadPrivacyScreen funciton but doesn't load the view.
- (IBAction)loadPrivacyScreen;
I have created a method as IBAction because I want to hook that method with a button on privacy screen to check that function works ...
And surprisingly it works when you click the button. But it doest work on time.
Can anyone suggest me what am I doing wrong ?? or any other alternative to achieve same thing??
Note: I have also try changing
- (IBAction)loadPrivacyScreen;
to
- (void)loadPrivacyScreen;
But still same result. It is not switching ....
First of all iOS provides a simple way to load a splash screen.
Just add a Image with 320x480 resolution in the name called default.png and add that to your project it will automatically use this image as splash screen image.
In your way call the loadPrivacy screen with a timer.
- (void)viewDidLoad {
[super viewDidLoad];
NSTimer *theTimer = [NSTimer scheduledTimerWithTimeInterval:3.00 target:self selector:#selector(loadPrivacyScreen) userInfo:nil repeats:NO];
}
- (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
[viewController showSplash];
}
And in the view controller, define a method like
> IBOutlet UIView *modelView;
- (void)showSplash;
- (void)hideSplash;
-(void)showSplash {
UIViewController *modalViewController = [[UIViewController alloc] init];
modalViewController.view = modelView;
[self presentModalViewController:modalViewController animated:NO];
[self performSelector:#selector(hideSplash) withObject:nil afterDelay:2.0];
}
//hide splash screen
- (void)hideSplash {
[[self modalViewController] dismissModalViewControllerAnimated:YES];
}