TabBarController NAvigationController and UIViewController - objective-c

I have the next question:
In my project I have the next:
UItabbarController
....Some UINAvigationControllers....
*(1) UINavigationController
UIViewController (UItableView) - When select one row it goes to...(by push) to:
UIViewController (UItableView) - And here the same than before, for each row i open a new tableview....
My problem is when i click in the tab bar item, I see the viewController view like last time that i saw this, and no reload to the *(1) first view another time( like i would like)
Where I need to write sth for each time that i click in a tab bar item i reload the first view of this tab bar item.
PD: I have the call: [theTableView reloadData]; in method "viewWillAppear".
The thing I'm doing is:
In my navigation Controller I have a View Controller (like tableview) and when i click in one row, in the "didSelectRowAtIndexPath" method I create another View Controller calling "myController" and i push this element like this ( [[self navigationController] pushViewController:myController animated:YES];)
And this for each time i click in one row in the next tables.
Then I think the problem is not to reload the table view in the method viewWillAppear, it's to take out from the screen the next views controller that I inserted to the root one.
I'm rigth?
IN RESUME:
My app has the next:
Tab bar to move between screens
Navigation inside each tab bar (as far as you want), why? because all the tabBarItems show Tables, and if you click in one row you open another table,.....
My problem then is that I would like to come back to the 1st Main table when i click in the tab bar. For the moment the app doesn't do this, it continue in the scree(table view) that was the last visit in this tab. (Is not completely true, because if i click two time, Yes, it comes back but don't enter in the "viewWillLoad" or "didSelectViewController" methods, because i made NSLogs and it doesn't show them).
The sketche can be this:
AppDelegate -> WelcomeScreen ->VideosTableViewController ->RElatedVideosTableViewController -> ..... ..... ....
The 1st thing is to show the Welcome screen (not so important, only some buttons) and in this class i have the TabBArController initialized with "localViewControllersArray" that is a NSMutableArray of NavigationControllers each initialized with one ViewController .
Then when i press one of the buttons in this welcome Screen i sho the tab bar Controller (Shows the VideosTableViewController)
In the next step, when I click in one row, in "DidSelectRowAtIndexPath" I create a RElatedVideosTableViewController, and I push this by "[[self navigationController] push....: "The relatedvideo table view i create" animated:YES];
AND I ALSO HAVE:
Add: UITabBarControllerDelegate
Add:
(void)tabBarController:(UITabBarController*)tabBarController
didEndCustomizingViewControllers:
(NSArray*)viewControllers
changed:(BOOL)changed { }
(void)tabBarController:(UITabBarController*)tabBarController
didSelectViewController:(UIViewController*)viewController
{ if ([viewController
isKindOfClass:[UINavigationController
class]])
{ [(UINavigationController *)viewController popToRootViewController:NO];
[theTableView reloadData];
NSLog(#"RELOAD");
} }
And at the initialization of the class:
[super.tabBarController setDelegate:self];
But in the console I don't see the NSLog I'm making then is not going in this method.

Make your app delegate the tab bar controller's delegate, either in Interface Builder or in code:
- (void)applicationDidFinishLaunching
{
...
self.tabBarController.delegate = self;
}
Then, when the tab bar switches to a different view, you get notified, at which point you pop to the root of the selected nav controller thus:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[UINavigationController class]])
{
[(UINavigationController *)viewController popToRootViewController:NO];
}
}
Each view controller should have its own table view, so I don't know what you are trying to do by the reload.

Related

Storyboard - Popping to a View Controller then Pushing Causes multiple pushes

In Objective C / iOS;
We have a process similar to this (set up in xcode storyboard);
Menu View Controller -> Enter in a code -> Process/Validate -> Present a Failed Code page
The (->) arrows signify a push segue setup in storyboard
When in failed state I want to pop to the Enter in a code view controller.
UIViewController *vc = nil;
NSUInteger index=0;
for (UIViewController *viewController in self.navigationController.viewControllers) {
if ([viewController isKindOfClass:[SomeViewController class]]) {
vc = viewController;
break;
}
index++;
}
if (vc) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.navigationController popToViewController:vc animated:YES];
});
return;
}
This pops me back to the VC I want to go to.
Except now when I press a submit on the Enter code page it does 3 or 4 more "pushes", when it should only be 1.
Do I need to unwind the segue? I tried emptying the navigational view controller stack, and I even tried ridding it of its last active view controller -- both of these return a blank or black window view frame.
Why would popping a view controller in the navigation stack affect the segues in my view controller to the point where whenever I try to do a push segue action it will try to push multiple view controllers onto the stack?
Turns out I had the following issue
Button press causes segue action in storyboard
I did the same segue action in code on the button, hence pushing multiple times
I have now solved this issue

Why UINavigationController keep popping out the top controller

I have UITabBarController, one of the tab points to a UINavigationController. The UINavigationController rootViewController is of class BGProfileView which shows users' profile
At viewDidAppear, I arranged that if users didn't logged in it will push a BGLogin view controller.
[BGLogin alreadyLoggedin:self.navigationController hideBackButton:YES anddoBlock:^{
[self whatToDoAfterLogin];
}];
Now everything is fine but with one minor issue. If I press the tab again, BGLogin will be poped out of UINavigationController.
I have no idea what makes that BGLogin poped out.
If I select a different tab and then click back to the BGProfile tab, this doesn't happen. It just happens when I click the same active tab. So I am in BGProfile tab, and I click that tab again. Basically it happens when I select the active tab that should simply do nothing. In fact, it does do nothing on others.
I put a breakpoint in viewWillDisappear and this is what I see:
As you see, viewDidAppear is called by the main loop. But why the hell the mainloop call viewDidAppear? Usually there is a code saying things like nav popViewController
Chances are there . that your tab bar controller is pushing new navigation controller with root view controller. and you interpreting that it's popping out. When same tab is selected you need to tell your tabBarController to not to do anything explicitly.
Example
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
UIViewController *tbSelectedController = self.tabBarController.selectedViewController;
if ([tbSelectedController isEqual:viewController]) {
return NO;
}
return YES;
}

UISearchBar with ScopeBar in Modal Form Sheet

My ViewController is a custom UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate>. I have set up the UISearchBar to display a scope bar.
I instantiate the view controller once and then show the same view on demand via [self presentModalViewController:mySearchController fromView:self.view];
Now, when I configure the view controller to be displayed as a form sheet, the scope bar appears only the first time the view is shown. The next time the scope bar is not displayed.
Displaying the view as a page sheet works fine. However in this case I observe that the first time the scope bar is already in place, while thereafter it is animated into the search bar as soon as it becomes the first responder.
I fiddled around with the view and controller settings in the inspector without any success.
What is causing this behavior?
How can I make the scope bar appear in the form sheet the next time I am showing the view?
Is there a way to prevent the UISearchBar from going into a different state when its view is hidden/redisplayed?
Set the scope bar titles in search delegate for begin editing and make it nil when the search ends. And add the showScopeBar method right after this method. Try and check if that helps.
For eg:-
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
[searchBar setScopeButtonTitles:[NSArray arrayWithObjects:#"Title1", #"Title2", nil]];
[searchBar setShowsScopeBar:YES];
//...
}
And in didEndEditing,
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
[searchBar setScopeButtonTitles:nil];
[searchBar setShowsScopeBar:NO];
//..
}
In addition to this, whenever you want to display the scopebar you can use the above lines to show and hide it.

Pushing a navigation controller is not supported- performing segues

I created a new navigation controller in my storyboard (not programmatically!) and set it to be "Root View Controller" to a regular UIViewController and added a button in it which says- forward to the next view controller (this second view controller is a view controller which I want that will have a back button to link to the initial view controller).
Now, whenever I try to link the button to the next view controller- "Pushing a navigation controller is not supported".
Help me please, and thanks
EDIT:
I accidentally subclassed UINavigationController, and not UIViewController in my class.
Thank you anyway.
I've tried this and have no problems, its all done in IB with no additional code required ...
Start a new project, "Single View Application" using story boards
Select storyboard and delete the views its produced.
Drag on a new Navigation Controller (it will bring a table view with it)
Delete the table and the table view controller, so you are just left with the Navigation Controller
Drag on a normal view controller
Right Click and drag from the Navigation controller to the new View and choose "Relationship - Root View Controller"
Drag a "Bar Button Item" on to the Navbar which should be visible on the top of your ViewController, you can rename this Forward if you wish.
Now drag on another view controller which is the one your "Forward" button will push in to view.
Right Click and drag from the bar button to the 2nd View Controller, and choose "Push"
Run the project and you will get a Single view with a Navbar and your button, clicking your button will Push the other view and give you a Back Button to return to the first View Controller. I'll try and post a picture of my storyboard if it helps.
Plasma
I had the same trouble. I wanted to have a navigation controller on each storyboard, so that each could run independently, be individually debugged, and so that the look would be right with the navigation bar.
Using other approaches, I found the UINavigationController would be retained from the original storyboard -- which I didn't want -- or I'd get errors.
Using the AppDelegate in your view controller to set the rootViewController worked for me (borrowing segue naming conventions from Segue to another storyboard?):
- (void)showStartupNavigationController {
NSLog(#"-- Loading storyboard --");
//Get the storyboard from the main bundle.
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Startup" bundle:nil];
//The navigation controller, not the view controller, is marked as the initial scene.
UINavigationController *theInitialViewController = [storyBoard instantiateInitialViewController];
NSLog(#"-- Loading storyboard -- Nav controller: %#", theInitialViewController);
//Remove the current navigation controller.
[self.navigationController.view removeFromSuperview];
UIWindow *window = [(AppDelegate *)[[UIApplication sharedApplication] delegate] window];
window.rootViewController = theInitialViewController;
To swap views Programatically you would need to select the segue and give it an Identifier like "PushView" then call it like this ....
[self performSegueWithIdentifier:#"PushView" sender:self];
That will programatically do the same as clicking the forward button. I've created you an example project with the code discussed above. Has an -(IBAction) with code in you can use for programatially changing the view.
PushView.zip
I also wanted to do this, present a screen (that had an embedded navigation controller) when the user pushes a button.
At my first attempt, I connected the segue from the button in the fist screen to the Navigation Controller, and the app was crashing with this error "Pushing a navigation controller is not supported".
This is the solution I found:
Select the segue from the button in the first screen to the navigation controller.
If it had an identifier, copy its name. Then delete that segue.
Then create a new segue by CTRL-clicking the button in the first view controller and dragging to the VIEW CONTROLLER YOU WANT TO PRESENT (not to the Navigation Controller that is pointing at it), and select Push in the small pop up window.
Then click the icon in the middle of the segue and paste the name you copied in the first step as an identifier for it.
IB is going to give you a warning "Scene is unreachable due to lack of entry points and does not have an identifier for runtime access via -instantiateViewControllerWithIdentifier:." Don't worry, it works perfectly.
If you want to customize the string that is shown as the Back button to return, you can add this line in the viewDidLoad method OF THE VIEW CONTROLLER THAT IS BEING SHOWED AFTER THE BUTTON IS PRESSED, that is the Child view controller.
(replace "Settings" with the name you need)
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBar.topItem.title = #"Settings";
...
}

Should I use the same detail ViewController to work both modally and when pushed?

This seems to be the patten used throughout Apples applications; Creation of a new record is done through a modal View which needs to be saved or canceled to continue, and editing a record is done through a view pushed onto the navigation stack.
It doesn't seem right to be basically duplicating my ViewController for 'add' and 'edit' but there are several differences in how pushed and modal ViewControllers work which complicate things.
How should I be doing this so it can cover both bases?
-
Differences include.
When pushed onto the stack the navBar appears at the top of the View and can be configured to contain the cancel/save buttons. When presented modally this is not the case so to duplicate the interface a toolbar needs to be created separately and close/save buttons added to this instead.
When dismissing a pushed view we send a message to the navigation controller [self.navigationController popViewControllerAnimated:YES];, when dismissing a modal view we send a message to self [self dismissModalViewControllerAnimated:YES];
You could add the UIToolbar in InterfaceBuilder, and then just hide it in viewDidLoad when self.navigationController is not nil.
As for dismissing, you could have something like:
- (void)didCancel {
[self.navigationController popViewControllerAnimated:YES] || [self dismissModalViewControllerAnimated:YES];
}
This will shortcircuit if your viewcontroller is part of a navigationcontrol, and use dismissModalViewControllerAnimated otherwise.
This should work for your cancel button. For your save button, it is useful to call some sort of delegate method such as:
- (void)didSave {
// do your saving juju here
if([self.delegate respondsToSelector:#selector(viewController:didSave:]) {
[self.delegate viewController:self didSave:whatJustGotSaved];
}
[self.navigationController popViewControllerAnimated:YES]; // noop if currently modal
}
In the delegate's implementation then, you can put:
- (void)viewController:(UIViewController*)viewController didSave:(NSObject*)whatJustGotSaved {
// do stuff with parameters
[self.modalViewController dismissModalViewControllerAnimated:YES]; // noop if not modal
}