Preserve custom tabbar view state between view controllers - ios

We have a custom view, which looks like a tabbar but is ultimately a subclass of UIView.
The view is shown as a tabbar would at the bottom of a UIViewController. When an image is touched in the view controller we transition to another view controller.
The second view controller has the same fake tabbar view being shown at the bottom. The user can close the second view controller and it will transition back to the first.
What is the best way to keep the same view and its state for both view controllers? For example part of the fake tabbar might be a usable button with a badge icon showing (2). If that is touched it would go down to (1). This would need to be reflected on both instances of the view.
Would the correct approach be to just use prepareForSegue as normal and keep updating the view state or passing the views instance around? Or is there a better approach?

I think the best approach is to implement something similar with the native tab bar. You can achieve this by implementing a container view . How you do that is a long story to post here but there are many resources on the internet. Basically you will have the same fake bar and your view controller will be shown in container view that should be put just above the tab bar. The view controller with both the container view and the tab bar should manage the transitions and update the bar.

Yeah, just as Jelly said I'd go the parent/child view controller route, with the 'tab bar' managing adding/removing the view controllers and associated views in response to touch events.

I am working on the same scenerio. In a UIViewController take your tabBar view at the bottom and above that take a blank UIView. Now on click of tabBar button, add and remove your new ViewController's view using AutoLayout like as -
#pragma mark - TAB BAR METHODS
-(void)setSelecedView:(VIEWSELECTION)selecedView
{
[self RemoveChildViewControllers ];
switch (selecedView)
{
case VIEWSELECTION_HOME:
{
HomeViewController *homeVC = [[HomeViewController alloc]initWithNibName:#"HomeViewController" bundle:nil];
self.titleString=#"Wellborn Company App";
[self displayContentController:homeVC OnView:self.DumpingView];
}
break;
case VIEWSELECTION_SEARCH:
{
SearchViewController *searchVC = [[SearchViewController alloc]initWithNibName:#"SearchViewController" bundle:nil];
self.titleString=#"Search";
[self displayContentController:searchVC OnView:self.DumpingView];
}
break;
}}
#pragma mark - VC Adding/Removing Methods
- (void)RemoveChildViewControllers
{
NSArray *childVCArray = [self childViewControllers];
for ( __strong UIViewController *childvc in childVCArray)
{
[childvc willMoveToParentViewController:nil];
[childvc.view removeFromSuperview];
[childvc removeFromParentViewController];
}
}
- (void)displayContentController:(UIViewController*) content OnView:(UIView*)parentView
{
[self addChildViewController:content];
[parentView addSubview:content.view];
NSDictionary *views = #{
#"childView" : content.view,
};
NSArray *arr;
[content.view setTranslatesAutoresizingMaskIntoConstraints:NO];
arr = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[childView]-0-|" options:0 metrics:nil views:views];
[parentView addConstraints:arr];
arr = [NSLayoutConstraint constraintsWithVisualFormat:#"|-0-[childView]-0-|" options:0 metrics:nil views:views];
[parentView addConstraints:arr];
[content didMoveToParentViewController:self];
}

If it is just a view and your simply pushing view controllers on a navigation stack, then add your view to your navigation view controllers view.
[self.navigationController.view addSubview:view];

Related

Is it possible to add a table view controller to a part of a view controller?

Let's say that I have a UITableViewController which is mostly reusable, and should be used from many UIViewControllers, but it should cover only part of the total view (e.g. 90% of the total height). Normally I would do this with navigation, but if I want to keep the top 10% of the UIViewController visible, and show the UITableViewController for the remaining 90%, it is possible and if yes how to do it?
Yes. The big view controller is container view controller, and the small view controller (table view controller in this case) is child view controller. We can add or remove child view controller in the container view controller.
Add a child view controller to a container
- (void)displayContentController:(UIViewController *)content {
[self addChildViewController:content];
content.view.frame = [self frameForContentController];
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self];
}
Remove a child view controller from a container
- (void)hideContentController:(UIViewController *)content {
[content willMoveToParentViewController:nil];
[content.view removeFromSuperview];
[content removeFromParentViewController];
}
We can also remove an old child view controller and add a new child view controller at the same time. Here is the example code (with animation).
- (void)cycleFromViewController:(UIViewController *)oldVC
toViewController:(UIViewController *)newVC {
// Prepare the two view controllers for the change.
[oldVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
// Get the start frame of the new view controller and the end frame
// for the old view controller. Both rectangles are offscreen.
newVC.view.frame = [self newViewStartFrame];
CGRect endFrame = [self oldViewEndFrame];
// Queue up the transition animation.
[self transitionFromViewController:oldVC toViewController:newVC
duration:0.25 options:0
animations:^{
// Animate the views to their final positions.
newVC.view.frame = oldVC.view.frame;
oldVC.view.frame = endFrame;
}
completion:^(BOOL finished) {
// Remove the old view controller and send the final
// notification to the new view controller.
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
}];
}
Yes, you can. Just add UITableViewController as child controller to your parent UIViewController.
Also, you can read about it here Apple Documentation

Remove viewcontroller & move to next view controller

Hi i have been working on an iOS app.What i am doing is navigating among diffrent view controllers. But the problem is i want finish the current view controller from emoery and then move to the next view controller.
I am using `[self.view removeFromSuperview]; for finishing the cureent view & using
self.loginView = [self.storyboard instantiateViewControllerWithIdentifier:#"LOGIN"];
[self presentViewController:self.loginView animated:NO completion:nil];
for moving to next view controller but the thing is i am not able to remove it from memory.
Please tell me how can i do it?
Thanks in advance.
`
It's better to create a container view controller which manages your view controllers. For example, in viewDidLoad: of container controller you add current controller:
[self addChildViewController:self.currentViewController];
[self.currentViewController didMoveToParentViewController:self];
[self.view addSubView:self.currentViewController.view];
//here set up currentViewController view's frame or constraints if needed
When you need to open login controller, do this:
[self addChildViewController:loginViewController];
[self.loginViewController didMoveToParentViewController:self];
[self.view addSubView:loginViewController.view];
//here set up loginViewController view's frame or constraints if needed
//then remove current view controller
[self.currentViewController willMoveToParentViewController:nil];
[self.currentViewController removeFromParentViewController];
[self.currentViewController.view removeFromSuperview];
Remove from superview will remove it from the current view, but OS won't remove it until he needs to (this is topic for more explanation, let's say it won't remove it asap).
If you want something deleted just call it nil:
self.view = nil;
This will make the pointer to nil, so view won't be there any more. (the view really will be somewhere but you won't have access to it)
I am revising your code
self.loginView = [self.storyboard instantiateViewControllerWithIdentifier:#"LOGIN"];
[self presentViewController:self.loginView animated:NO completion:nil];
What you are doing here is presenting your login viewcontroller.
self: This is the instance of the viewcontroller you are currently working on. So how could you remove self from memory. (Not Possible)
You can approach alternate ways.
For example: 1. Changing root view controller
Pop to root view controller and then Push Login View controller.
If you try to remove not only UIView but the whole UIViewController from a navigation controller stack use this snippet
NSMutableArray *stack = [[NSMutableArray alloc] initWithArray: self.navigationController.viewControllers];
[stack removeObject:yourController];
self.navigationController.viewControllers = stack;
Be aware of using this only when you've already pass to the next controller view.
UPD:
Oh, now I see what you are trying to do. And I can't figure out, why you're trying to step your controllers this way (modally). I think you should use UINavigationController with navigation segues defined directly from your storyboard. Look at this article where apple purely explains what navigation is. That article is about modal views

Containing UIViewController in UIView

I am attempting to make a facebook/youtube-like slide-out menu for my app. My main view controller has two UIViews, one for the menu tableview and those, and one where I plan to contain whatever view the user has switched to.
I tried to make another UIViewController view made in interface builder as a subview contained in the UIView for content. I did that like this
[self.contentViewLayer addSubview:[[PersonViewController alloc]init].view];
with and without the .view at the end. It didn't work. Is it possible to have a UIViewController from storyboard contained in a UIView?
You should probably take a look at Child View Controllers from the Apple documentation.
Here's some code that exemplifies this technique:
- (void)addContentViewController:(UIViewController *)viewController {
/* This should be inside your slide out view controller manager */
[self addChildViewController:viewController];
viewController.view.frame = [self frameForContentController];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
}
You can use a container view:
[self addChildViewController:vc2];
[self.container addSubview:vc2.view];
Or you can do [vc1.view addsubview:vc2.view];

Change Container View Content with Tabs in iOS

I'm trying to make a form that spans three tabs. You can see in the screenshot below where the tabs will be. When the user taps a tab, the Container View should update and show a particular view controller I have.
Tab 1 = View Controller 1
Tab 2 = View Controller 2
Tab 3 = View Controller 3
The view controller shown above has the class PPAddEntryViewController.m. I created an outlet for the Container view within this class and now have a Container View property:
#property (weak, nonatomic) IBOutlet UIView *container;
I also have my IBActions for my tabs ready:
- (IBAction)tab1:(id)sender {
//...
}
- (IBAction)tab2:(id)sender {
//...
}
- (IBAction)tab3:(id)sender {
//...
}
How do I set the container in those IBActions to change the view controller that the Container View holds?
Among a few other things, here's what I've tried:
UIViewController *viewController1 = [self.storyboard instantiateViewControllerWithIdentifier:#"vc1"];
_container.view = viewController1;
...but it doesn't work. Thanks in advance.
Switching using Storyboard, Auto-layout or not, a Button of some sort, and a series of Child View Controllers
You want to add the container view to your view and when the buttons that 'switch' child view controllers are pressed fire off the appropriate segue and perform the correct setup work.
In the Storyboard you can only connect one Embed Segue to the Container View. So you create an intermediate handling controller. Make the embed segue and give it an identifier, for example EmbededSegueIdentifier.
In your parent view controller wire up the button or whatever you want and keep are reference to your child view controller in the prepare segue. As soon as the parent view controller loads the segue will be fired.
The Parent View Controller
#property (weak, nonatomic) MyContainerViewController *myContainerViewController;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"EmbeddedSegueIdentifier"]) {
self.myContainerViewController = segue.destinationViewController;
}
}
It should be fairly easy for you to delegate to your container controller the button presses.
The Container Controller
This next bit of code was partly borrowed from a couple of sources, but the key change is that auto layout is being used as opposed to explicit frames. There is nothing preventing you from simply changing out the lines [self addConstraintsForViewController:] for viewController.view.frame = self.view.bounds. In the Storyboard this Container View Controller doesn't do anything more that segue to the destination child view controllers.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%s", __PRETTY_FUNCTION__);
[self performSegueWithIdentifier:#"FirstViewControllerSegue" sender:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
if ([self.childViewControllers count] > 0) {
UIViewController *fromViewController = [self.childViewControllers firstObject];
[self swapFromViewController:fromViewController toViewController:destinationViewController];
} else {
[self initializeChildViewController:destinationViewController];
}
}
- (void)initializeChildViewController:(UIViewController *)viewController
{
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[self addConstraintsForViewController:viewController];
[viewController didMoveToParentViewController:self];
}
- (void)swapFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
[self transitionFromViewController:fromViewController toViewController:toViewController duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:^(BOOL finished) {
[self addConstraintsForViewController:toViewController];
[fromViewController removeFromParentViewController];
[toViewController didMoveToParentViewController:self];
}];
}
- (void)addConstraintsForViewController:(UIViewController *)viewController
{
UIView *containerView = self.view;
UIView *childView = viewController.view;
[childView setTranslatesAutoresizingMaskIntoConstraints:NO];
[containerView addSubview:childView];
NSDictionary *views = NSDictionaryOfVariableBindings(childView);
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[childView]|"
options:0
metrics:nil
views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[childView]|"
options:0
metrics:nil
views:views]];
}
#pragma mark - Setters
- (void)setSelectedControl:(ViewControllerSelectionType)selectedControl
{
_selectedControl = selectedControl;
switch (self.selectedControl) {
case kFirstViewController:
[self performSegueWithIdentifier:#"FirstViewControllerSegue" sender:nil];
break;
case kSecondViewController:
[self performSegueWithIdentifier:#"SecondViewControllerSegue" sender:nil];
break;
default:
break;
}
}
The Custom Segues
The last thing you need is a custom segue that does nothing, going to each destination with the appropriate segue identifier that is called from the Container View Controller. If you don't put in an empty perform method the app will crash. Normally you could do some custom transition animation here.
#implementation SHCDummySegue
#interface SHCDummySegue : UIStoryboardSegue
#end
- (void)perform
{
// This space intentionally left blank
}
#end
I recently found the perfect sample code for what I was trying to do. It includes the Storyboard implementation and all the relevant segues and code. It was really helpful.
https://github.com/mhaddl/MHCustomTabBarController
Update: UITabBarController is the recommended way to go, as you found out earlier. In case you'd like to have a custom height, here is a good start: My way of customizing UITabBarController's tabbar - Stackoverflow answer
As of iOS 5+ you have access to customize the appearance via this API; UIAppearance Protocol Reference. Here is a nice tutorial for that: How To Customize Tab Bar Background and Appearance
The most obvious way to achieve what you're looking for is to simply manage 3 different containers (they are simple UIViews) and implement each of them to hold whatever content view you need for each tab (use the hidden property of the containers).
Here is an example of what's possible to achieve with different containers:
These containers "swapping" can be animated of course. About your self-answer, you probably chose the right way to do it.
have a member variable to hold the viewController:
UIViewController *selectedViewController;
now in the IBActions, switch that AND the view. e.g.
- (IBAction)tab1:(id)sender {
UIViewController *viewController1 = [self.storyboard instantiateViewControllerWithIdentifier:#"vc1"];
_container.view = viewController1.view;
selectedViewController = viewController1;
}
to fire view did appear and stuff call removeChildViewController, didMoveToParent, addChildViewController, didMoveToParent
I got this to work by using a UITabBarController. In order to use custom tabs, I had to subclass the TabBarController and add the buttons to the controller in code. I then listen for tap events on the buttons and set the selectedIndex for each tab.
It was pretty straight forward, but it's a lot of junk in my Storyboard for something as simple as 3 tabs.

How to navigate inside a UINavigationController which is inside a UITabBarController?

I have a project that is a tab-bar controller. Each tab usually has a UINavigationController. The problem I have is this: I need a new tab with alot of navigation (roughly 30 navigation items grouped into 4-8 groups. Problem: My navigation bar is already full (can't use the navigation controller (or bar). What I need is navigation below the navigation bar (which has a global search bar and other global icons filling it). How can I implement this best?
What I have now: I have created a UIScrollView just under the navigation bar to serve as my "hand-rolled" navigation bar. It's a scrollView because I don't know (going forward) how many "groupings" of navigation items I will have (currently only 4). Each of these groups is represented by a UIButton, some of which should immediately present a view, and others which present a popover with further navigation items, which when selected will present a view.
Problem: I want a "content view" under my navigation view mentioned above, where I can present content based on the user's navigation choices. I have to support iOS 5.0, so I can't use the storyboard container view (unfortunately). I will have 3 types (maybe more later) of content views I will present, that I would like to create as individual view controllers and then push the appropriate one as it's selected in my navigation mentioned. Is there a 3rd party navigation controller I can use? Do I have to "roll my own"? Any advice would be greatly appreciated.
Here is a "slapped-together" picture of what I need to achieve:
I would make what you're calling the content view a subview of your main view, and use it as the view to which you will add a childViewController's view. If you haven't already read up on custom container controllers, you should do so, but the basic way of using them is like this.
The controller's whose view you show in your question would be the custom container controller. You could load up an initial controller in the viewDidLoad method, then switch the controller in your subview (I'm calling it self.containerView) in response to the user choosing something from your scroll bar:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIViewController *initial = [self.storyboard instantiateViewControllerWithIdentifier:#"InitialVC"];
[self addChildViewController:initial];
[initial.view.frame = self.containerView.bounds];
[self.containerView addSubview:initial.view];
self.currentController = initial;
}
-(void)switchToNewViewController:(UIViewController *) cont {
[self addChildViewController:cont];
cont.view.frame = self.containerView.bounds;
[self moveToNewController:cont ];
}
-(void)moveToNewController:(UIViewController *) newController {
[self.currentController willMoveToParentViewController:nil];
[self transitionFromViewController:self.currentController toViewController:newController duration:.6 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{}
completion:^(BOOL finished) {
[self.currentController removeFromParentViewController];
[newController didMoveToParentViewController:self];
self.currentController = newController;
}];
}
This should give you the basic idea. I took this from one of my test projects, so it will probably need a little tweaking.

Resources