I have an application that requires to select a client to work with when the application is launched. I am initiating a segue from the app delegate to a modal view that allows the user to make the selection. The view has a left bar button that says "cancel" and dismissed the view. I want this to be disabled when the user first selects a client, but to be enabled all subsequent times the user opens the client select pane.
I have an IBOutlet in the select client view called cancelButton.
In my prepareForSegue method in the view controller that is launching the segue, I have
if ([segue.identifier isEqualToString:#"selectClient"]) {
if (firstSegue) {
SelectClientViewController *select = (SelectClientViewController *)segue.destinationViewController;
select.cancelButton.enabled = NO;
}
firstSegue = NO;
}
However, the button remains enabled on the first launch. Any help would be greatly appreciated.
When prepareForSegue is called the view hasn't been loaded. If you don't do anything to make it during the method then the view won't be loaded till later. If the view hasn't been loaded then the outlets aren't available (they haven't been loaded yet either). So, the problem is that the button you are trying to disable doesn't exist yet.
Set a flag on the destination controller so it disables the button in viewDidLoad or ensure that the view is loaded before you try to set any view properties.
Related
I'm trying to do something which seems simple, but I am having difficulty. I have a tab bar application with a setting page which is a table view. The user can change the settings by selecting a row, which pushes a new table view with new setting options. When the user selects a new setting, I send the information back to the main table view and populate the table view with the new setting.
The problem : I also have a SAVE button. If the user does not save the new settings I want to discard them. If the user selects a new tab (without saving settings) and then selects the settings tab, the last data the user entered is still in the tableView. I understand this is because viewdidload is not called again after the view has been created.
Basically I want logic like this :
If segue was initiated by clicking a tab bar icon : load the table view using dataModel.
Else if the previous screen was the data entry screen : Load the table view using user selected data.
If I wasn't using a tab bar controller I would do this by loading the dataModel using viewdidload and loading the tableview using delegate methods. The problem is I can't use viewdidload. I also can't use viewwillappear because it is called both when opening the screen from the table view and when popping the entry view controller off the stack.
I tried to set up the delegate method for the Tab Bar Controller in AppDelegate
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
if (tabBarController.selectedIndex == 2)
{ let navController = viewController as! UINavigationController
let settingsTableViewController = navController.viewControllers[0] as! SettingsTableViewController
settingsTableViewController.loadFromDataModel = true
println("In did select view controller in app delegate")
}
}
And if loadFromDataModel = true then I load from the data Model in the Settings Table View Controller. Here is the problem - this only works if I go back and forth from the tabs twice. So weird. I put in println statements and it seems like execution is happening in the following sequence:
ViewWillAppear is being called in SettingsTableViewController
tabBarController:didSelectViewController is being called from the
AppDelegate (and the variable loadFromDataModel is updated - but it is too late because viewwillappear has already been called)
In Summary : Is this the best way to determine if the segue came from the tab bar? Why is viewwillappear on my SettingsTableViewController being called before the delegate method? Any suggestions on how to load the data from the data model each time the user selects the tab via the tab bar. Am I missing some obvious method? Thanks for any help!
By the time didSelectViewController is called, it's too late to do what you are looking to do. As you can tell by the viewDWillAppear delegate firing. Try moving your code to the shouldSelectViewController delegate of the tabBarController That should be early enough in the process to set your data.
I'm creating a tab bar application for the iPhone and I want whatever view is currently active to be fully unloaded/released if a new tab is selected. When any tab is reselected I want it to have to reload everything. The reason I seek this feature is because all views interact with a single database, and can modify the database. When the views are built they are built off the current database, which means that they can be out of date without a forced reload of the view.
To see what I'm referring to load the "Phone" app on your iPhone and you can type in a number on the keypad, switch tabs, switch back to the keypad and the number you typed remains there. Which is a desirable trait for the Phone app, but not so much for what I'm designing.
Is there a way to achieve this? Or, should I use another method to update my views when tabs are switched?
I think Neil Galiaskarov comment that you should not think about releasing the view. His idea to put your current reloading logics in -(void)viewWillAppear is sound:
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppeared:animated];
// Any code you are currently triggering in your init / viewDidLoad
// to reload your database and display the result
}
This is triggered every time you return via the tab-bar and should give you the result you are after. There are few caveats however, depending on what you do next, that might not deliver the required functionality:
The screen will be updated when it is reached by popping the stack (which is probably OK from what you are describing).
If you start to navigate backwards in the stack with the swipe-gesture but abort the navigation the screen will also be updated. (As this triggers viewWillAppear and viewDidAppear).
If the latter is a problem one way to handle it through a BOOL _shouldTriggerReload;:
Wrap the viewWillAppear logic inside an if:
if (_shouldTriggerReload){
_shouldTriggerReload = NO; // Preparing for the next round
// Any code you are currently triggering in your init / viewDidLoad
// to reload your database and display the result
}
Then in your viewDidDisappear:
- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
_shouldTriggerReload = YES;
}
And it will also have to be set inside your viewDidLoad for that first entry:
- (void)viewDidLoad{
_shouldTriggerReload = YES;
}
tabBarController has a viewControllers property.
You modify the viewControllers property to achieve such effect.
What i would do is,
For the view controller(say VC) which you want to reload its view, when user selects that tab.
add UITabBarControllerDelegate delegate to VC. and implement below method.
– tabBarController:shouldSelectViewController:
when the user selects this in bottom tab bar, tabBarController calls this method whether should it show this viewController or not.
Here, change the tabBarController.viewControllers property and return YES.
Lets say your tab bar has only two viewControllers attached to it, and user now selects tab 2 and you have to create new viewController and show it.
So,
UITabBarController *tabBarController;
NSMutableArray *array=[NSMutableArray arrayWithArray:tabBarController.viewControllers];
UIViewController *viewController2; //init your viewController type 2
[array replaceObjectAtIndex:1 withObject:viewController2];
tabBarController.viewControllers =[NSArray arrayWithArray:array];
Use delegate method
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
Add your piece of code which you can trigger each time whenever you select tabBarItem.
My situation goes as follows: I have an app which has a PIN screen implemented. Its nothing high-tech just basic 4 UITextFields and implemented logic via UITextFieldDelegate - it works.
I'm showing this screen in app delegate on -(void)applicationWillEnterForeground:(UIApplication *)application event. I am using MMDrawerController from github (link: https://github.com/mutualmobile/MMDrawerController) as the main view controller. If the current view presented when the application enters background and foreground again is this MMDrawerController then the becomeFirstResponder is not working - it doesn't show the keyboard. If there is another view controller presented on top of drawer controller the when entering foreground (let's say settings view) then keyboard appears normally.
I have tried NSLoging the canBecomeFirstResponder property and its set to YES. What is going on? How to fix this?
I can paste code if needed but its nothing ambiguous. Just plain call becomeFirstResponder.
EDIT:
To explain things a bit more clearly. rootViewController is a view controller caleed LoginViewController and it alloc-inits the sidebar and the center view controllers, alloc-inits the drawer controller and hooks everything up so it works. The app delegate view is actually a PIN screen which pops up when the app enters foreground. Now the keyboard appears like it should for the first time the drawer is visible.
When the user pops up SettingsViewController (yes, this is another view controller accessible from the sidebar view controller) it works as well. But when the user dismisses the settings view controller keyboard doesn't appear anymore. It has to do something with the drawer cause I tried without it and it worked (but I only had sidebar or center view controller visible).
Ideally you would want to access the textField within the MMDrawerControllers currentView. I have achieved something similar with this implementation:
In your app delegate store a reference to the DrawerController like so:
In AppDels interface:
#property(nonatomic, strong) MMDrawerController *appDrawController;
Then after you create the local drawer controller in didFinishLaunching (if you followed the setup instructions for this library):
self.appDrawController = drawerController;
In your applicationWillEnterForeground:
THECLASSTHATCONTAINSTHEPINVIEW *yourClass = self.appDrawController.centerViewController.childViewControllers[0];
Now you can access the public methods/properties, so:
[[yourClass pinEntryField] becomeFirstResponder];
Does this help at all ?
Can't seem to figure this out. I'm attempting to load a UIView (preferably sliding in from the right) on button tap but it isn't changing after the first page.
I click the register button on the settings page and it loads the first register page, then on the first register page, when I click the continueButton to load the second page it wont do anything at all..
SettingsPage.m:
- (void)regsiterButton:(UIButton *)standardButton {
MAINVIEWCONTROLLER.mainView = [[RegisterStep1 alloc] initWithFrame:MAINVIEWCONTROLLER.mainView.frame];
}
RegisterStep1.m - Attempting to load the next register page, but it isn't working:
- (void)continueButton:(UIButton *)standardButton {
MAINVIEWCONTROLLER.mainView = [[RegisterStep2 alloc] initWithFrame:MAINVIEWCONTROLLER.mainView.frame];
}
Assuming RegisterStep1 is a UIViewController subclass, you want to use UINavigationController's pushViewController:animated: method, which does the default transition from the right.
If you don't have a UINavigationController already set up, you'll want to wrap SettingsPage in one via initWithRootViewController: before using it.
This Apple guide describes how navigation controllers fit into the overall navigation of an app.
I have an iPhone application which consists of two View Controllers; a main one, and one with the help screen. Each one has a button that performs the segue from one to the other.
The problem I have is, when I segue back from the help screen to the main screen, the main view controller's viewDidLoad method gets called, so all of the initialization I did when the app was first started is repeated. Is there another method in the view controller that gets called just once, where I can do the initialization?
My first thought was, "Have a boolean variable that is initially set to false, then have viewDidLoad test it, and if it is false, do the initialization, then set it to true" - but how do I initialize it to false in the first place?
My guess is that you're doing a "Push" segue (which is the most standard kind of segue one can do in an iOS app), and if you are using a "Push" segue that means you have a navigation controller in your app.
The best thing to do here is not to push another "Main" view controller onto the stack of other view controllers (which is why you are seeing "viewDidLoad" called each time you push the main view), but instead when you click a "go to main" button in your help screen, pop the help screen off and return to the previous one. The call that would do this is UINavigationController's popViewControllerAnimated method.
Doing that means "viewDidLoad" on that view controller only gets called once, as the main view gets loaded once.
on the .m class file create a bool on #implementaition:
#implementation yourClass{
bool initialize = 0;
}
and then test it on view did load:
-(void)viewDidLoad{
if(initialize == 0){
//do everything you need to do
initialize = 1;
}
}
I think it will work...