I have an event which calls a view to appear, but the -viewdidload event isn't appearing as expected each time it's called. Here's the method I use to call it...
[self presentModalViewController:addItemViewController animated:YES];
then inside the addItemViewController, the method is
- (void)viewDidLoad {
NSLog(#"alright, lad!");
}
To close the view, I have a button with the code
- (IBAction)cancel {
[self dismissModalViewControllerAnimated:YES];
}
the "alright, lad" log is shown the first time the view appears, but never again when it's launched. Is there a method I can use to let the app "forget" about the view? Or should I be using another load method? I tried loadView (I think) but that had a blank screen...
Thanks for any help!
viewDidLoad is only called when the view is first instantiated. If you're not recreating the view controller each time, you'll only get it called once (and called again if you get a memory warning, and the view is nil'd out). You probably want to use viewWillAppear: or viewDidAppear:.
Make sure you call the superclass in each of those methods, e.g.
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"view appeared");
[super viewWillAppear:animated];
}
Related
My Application has a modal view controller, including a search bar. When the view comes up, I want the search bar to be focused. I tried [self.searchBar becomeFirstResponder] in viewDidLoad, but it didn't work. Later on I put it in viewDidAppear, it worked. But with this workaround, there is a visible delay. (after the view fully appeared, the keyboard began to appear)
I can ensure both viewDidAppear and viewDidLoad have been invoked.
What should I do if I want the search bar to be focused instantly with the view appear?
(I'm using StoryBoard)
Followed the answers, I tried to put the code in viewWillLoad, but still didn't work. (in viewWillLoad, self.searchBar.window is nil)
Possibly it does not work in viewDidLoad, as view does not added into view hierarchy yet. But according to apple documentation becomeFirstResponder should be called only on objects attached to UIWindow:
However, you should only call it on that view if it is part of a view hierarchy.
If the view’s window property holds a UIWindow object, it has been installed
in a view hierarchy; if it returns nil, the view is detached from any hierarchy.
So, i assume, the best place to achieve necessary behavior is to place call into viewWillAppear method.
Update.
So, in viewWillAppear controller's view not yet attached to UIWindow... it only notify, that view will be added to view hierarchy
It may be some tricky, but you can make some small delay in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
double delayInSeconds = 0.05;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
make first responder here
});
}
But I believe there is should be a better solution
You should call in viewDidLayoutSubviews(), the code below set textField becomeFirstResponder only at the first time view layout subviews, and it should be.
var isFirstLayout: Bool = true
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if isFirstLayout {
defer { isFirstLayout = false }
textField.becomeFirstResponder()
}
}
all the IBOutlet objects are loaded in viewDidLoad,if you are calling the method in viewDidLoad then that action not performed because before the objects are loaded we can't do anything that's whybetter to write that code in
-(void)viewWillAppear:(BOOL)animated{
//write here
}
then it works fine.
This will help:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
dispatch_async(dispatch_get_main_queue(), ^{
[self.quantifyTextField becomeFirstResponder];
});
}
Making text field/view first responder should be done after all UIViewController animations, which take place when view is loaded and presented. So the best place is viewDidAppear.
Write viewWillAppear instad of viewDidAppear/viewDidLoad.
BecauseviewWillAppear method is call at the time of View will appear (in process), for more information about viewWillAppear read this official Document.
- (void)viewWillAppear:(BOOL)animated
{
[self.searchBar becomeFirstResponder];
[super viewWillAppear:animated];
}
I know it is a bit old thread, but I think it can help someone that is facing some keyboard issue when adding this code.
Remember to set the textfield delegate to nil in viewWillDisappear, otherwise the keyboard will not be shown again if you pop/dismiss the view controller without closing the keyboard.
I'm having a problem getting my modal view controller to properly display and then dismiss. Basically I have a modally displayed login window and I want to dismiss it after all the data that I want to display is loaded from the database. if I call
[self dismissModalViewControllerAnimated:YES] from within the LoginViewController class it works perfectly fine but if I call
[[mainController modalViewController] dismissModalViewControllerAnimated:YES] from within my datamanager class after I have successfully imported the data nothing happens. Which is extremely confusing because [mainController modalViewController] points to the locationManager class.
Does anybody have any ideas as to why this isn't working? I'm programming for iOS 4 if that matters.
Thanks!
OK So I figured this out. Basically what was happening was that the [self dismissModalViewController] call was happening on another thread which for whatever reason means that the object did not properly process the dismiss message. I ended up using a notification and then called dismissModalView controller like so:
- (void)dismissSelf
{
[self dismissModalViewControllerAnimated:YES];
}
- (void)receiveDismissNotification:(NSNotification *) note
{
[self performSelectorOnMainThread:#selector(dismissSelf) withObject:nil waitUntilDone:NO];
}
which works
The proper way to dismiss a modal view controller is to call -dismissModalViewControllerAnimated: on the view controller that presented it. Thus it should be [_splitViewController dismissModalViewControllerAnimated:YES];.
From your comment, you need to call -dismissModalViewControllerAnimated: on the main thread, you can do this like so:
dispatch_async(dispatch_get_main_queue(), ^{
[_splitViewController dismissModalViewControllerAnimated:YES];
});
To close the Model View Controller use following code
[self dismissModalViewControllerAnimated:YES];
This code works with ios 5 also.
For presenting the model view controller
if (self.viewController!=nil)
{
//sanity check for view controller
[self.viewController SOMEVIEW animated:YES];
}
I have a UITableViewController that when a cell is pressed, I want the controller to pop itself, and then have the controller it pop's to, push another view controller onto the stack.
I am invoking this method because the popped-to viewController is the delegate of the tableViewController
I am currently invoking this method with a delay on it, because otherwise, everything gets screwed up waiting for the animation to end. Doing it this way seems a bit hacky and seems to me like it would fail if someone's device didn't pop the view in the allotted wait time I have given it.
Here is some of the code:
//**** code in my tableViewController ***//
[self.navigationController popViewControllerAnimated:YES];
[self.delegate cellPressedInTableViewControllerWithCalculationsModel:(id)anArgmentMyDelegateMethodTakes];
// **** Code in the viewController being popped to ****//
//CalculationsViewController is a subclass of UIViewController
CalculationsViewController *calcViewController = [[CalculationsViewController alloc] init];
//some customization code would go her
[self.navigationController performSelector:#selector(pushViewController:animated:) withObject:calcViewController afterDelay:0.75];
//this seems like the arbitrary part, the 0.75 second delay.
[calcViewController release];
There seems like there should be a better way to pop/push through delegation that will execute after the animation finishes. The wait time seems to me like it could cause unexpected problems.
I have also tried using:
performSelectorOnMainThread:withObject:waitUntilDone
But the code just executes immediately and the view hierarchy screwed up.
I have also looked at this question:
Delegation question
and it has gotten me this far, but I am curious to see if there is a better way to perform such a task,
Thanks.
edit: I have also tried wrapping the method in an instance of NSInvocation, and I couldn't get it to coordinate the method call until after the animation finished without arbitrarily setting the delay
The cleanest way to do more than a single push or pop of a view controller is to set the UINavigationControllers view controllers array.
For example, to pop and then push a view controller:
MyTableViewController *vc = [[MyTableViewController alloc] init];
NSMutableArray *controllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[controllers removeLastObject];
[controllers addObject:vc];
[self.navigationController setViewControllers:controllers animated:YES];
You should use a flag to overcome this situation. You set this flag in viewWillDisappear method of view controller being popped. When this flag is set then and then you can push another view controller on stack. Hope it's clear.
How about when you dismiss your UIViewController containing the table you send a NSNotifcation in your viewDidDisappear method like so:
- (void)viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"loadOtherVC" object:nil];
}
And in your parent view controller that will push a new view controller, you add an observer for that notification like so:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(LoadOtherVC:) name:#"loadOtherVC" object:nil];
You should have a method that matches the selector.
- (void) LoadOtherVC:(NSNotification *) notification
{
// load your other view controller you want here
}
Don't pop it from the top level controller. Call the delegate method that will pop the view off the navigation controller and then push a new one on.
I liked Alexandre's solution but if I were a delegate person, I wouldn't want to use notification. So in that case, we can just use the delegate in the viewDidDisappear method.
So, in the didSelectRowAtIndexPath method, you can pop the controller-
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.navigationController popViewControllerAnimated:YES];
}
and in the same View Controller, the ViewController you are popping, call the delegate method in the viewDidDisappear method like-
-(void)viewDidDisappear:(BOOL)animated{
[self.delegate cellPressedInTableViewControllerWithCalculationsModel:(id)anArgmentMyDelegateMethodTakes];
}
Then in the controller that is pushed can implement the delegate method and inside that delegate method, you can do whatever you want.
I have a tabbar application that has one screen that displays statistics based on the data presented in the tableviews of over tab screens. I would like to refresh this view once the stats view becomes selected again. I have implemented the tabbarcontrollerdelegate protocol to take an action when the viewcontroller.tabbaritem.title isequaltostring:#"foo". which works fine for my nslog statement but when i try and trigger the viewcontroller to execute the viewdidload method it never happens. And the code to refresh the stats view is in the viewdidload method.
From my AppDelegate
- (void)tabBarController:(UITabBarController*)tabBarController didEndCustomizingViewControllers: (NSArray*)viewControllers changed:(BOOL)changed
{
}
- (void)tabBarController:(UITabBarController*)tabBarController didSelectViewController:(UIViewController*)viewController {
if([viewController.tabBarItem.title isEqualToString:#"Summary"]) {
NSLog(#"didSelectViewController %#", viewController.tabBarItem.title);
[viewController viewDidLoad]; //FAIL
}
}
Never call viewDidLoad by yourself. That is a delegate method that is sent to the view controller after the view has loaded, you shouldn't call it manually.
In this case, view controllers that have views which are managed by a tab bar controller are sent the viewWillAppear:, viewDidAppear:, viewWillDisappear: and viewDidDisappear.
You should should use these methods to perform actions when your views are shown and hidden.
Example: implement viewDidAppear: and refresh your stats view.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated]; // don't forget to call super, this is important
// do your refreshing here
}
I have two view controllers in a tabbar which can both edit data. Therefore, I need to call a reload_data function whenever the user makes a switch on the tabbar. How can I catch the switch or the appearance of the viewcontroller. Somehow viewDidAppear is not called on a tabbar switch. And I do not want to use the tabbarController delegate for this, because several viewControllers are affected (and I cannot set them all as delegate). What is a good way to solve this?
e.g. this didn't work:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[self reloadData];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
[self reloadData];
}
If you're using Interface Builder, make sure the class for the viewController your expecting to reload is defined (Select the ViewController in IB, then CMD-4, make sure class is defined to be the class you want viewWillAppear and viewDidAppear to be called in).
If you're not using IB, post your code for init/calling the viewController.