I'm working on few views in an app that doesn't fit to any of standard Apple ViewControllers and I'm looking for a best solution for my problem.
Here is a quick mockup how it looks like:
http://pl.tinypic.com/r/2va1kbc/5
I have a main ViewController which is build of out 2 parts:
1. Top part which is always visible containing some logo
2. Content part which is changing - every content is a class (extending UIViewController)
Now tricky part is I want to do some animations in between of content changes - when user clicks a button on first one, top part slides down - pushing current viewController down, covers full screen and than slides up with a new viewController content.
So question is how to build it so it's easy to use and extend later (like UINavigationController).
I created a main view as UIViewController and added topView using:
LogoUIViewController *logoViewController = [[LogoUIViewController alloc] init];
[self.view addSubview:logoViewController.view];
LoginViewController *loginViewController = [[LoginViewController alloc] init];
loginViewController.delegate = self;
[self.view addSubview:loginViewController.view];
I'm using delegate to initialize a transition on main view controller, because I couldn't find a better way of doing it.
But with that solution I stucked because of those problems:
1. How to make content view controller to only use part of screen (change it size) ?
2. How to animate those 2 views so it will look like top layer is pushing away content ?
3. How to exchange views in an easy way (like UINavigationController popViewController way) and initialize animation automatically between them.
I know those are 3 problems, but I think it can be solved with better way of constructing the main view controller. Does anyone have experience with custom navigation systems like that ?
A UIPageViewController could be helpful. It would contain your content view controllers. The root vc that contains the page view controller can also be made it's delegate.
When you want to initiate a content view change, the root vc can do any animation it likes with it's page vc's view (like slide it down to reveal a larger header view). When the page transition is complete, as the delegate, the root vc can get notified with
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
and then animate the restoration of it's view. Pretty simple, I think, and saves you a fair amount of effort on the container vc pattern. Xcode's default Paged App template is a really good starting point.
Related
What I'm trying to achieve is to design my "shared" part of UI in interface builder and use child view controllers to show content to the user. This may sound as trying to reinvent UINavigationController, but it is not. In fact, the whole thing is embedded in one.
It looks like this :
Now, what I'm trying to do is change child view controllers of this BaseViewController and indicate this change in the navigation bar, so that all of its functionality remains.
I tried adding such a method :
+ (UIViewController *)baseViewControllerWithChild:(UIViewController *)child {
BaseViewController *base = [BaseViewController new];
[base addChildViewController:child];
child.view.frame = base.childViewControllerContainer.frame;
[base.view addSubview:child.view];
[child didMoveToParentViewController:base];
return base;
}
and then using it like this :
- (void)didTouch:(UIButton *)sender {
[self.parentViewController.navigationController pushViewController:[BaseViewController baseViewControllerWithChild:[DummyViewController new]] animated:YES];
}
(Note : DummyViewController is exactly that - a dummy vc, made just for testing, it only has background color set in viewDidLoad)
This method is a handler of a button in first child view controller. So far so good. Unfortunately, the result is not as expected - the pushed view controller is black. At firs I thought this was because BaseViewController was designed in storyboard and initially set as rootViewController of navigation controller. Moving it to a xib file and setting from code didn't quite work for me, as you cannot add a Container View in a xib.
To summarise, I would like to have a base design governed by BaseViewController class and content would be added as a childViewController of it. Pushing a new view controller would be a result of an action on these childViewController and should update the navigation stack accordingly.
Also, the whole thing needs to work with iOS 7.
Any help as to how to try to achieve this is greatly appreciated!
The issue was casued by base.childViewControllerContainer being nil - this was caused byt he fact that view property of view controllers is loaded lazily. Adding [base view] before accesing base.childViewControllerContainer solved the issue, though I'm not sure if this is the one, only and best way to do this.
I have MainVC than contains 2 tabs: FirstVC and SecondVC.
Then I tap on some of this taps I want to present below desired View/VC.
I am working on this project with Nib, so I have some confusion about it.
That object should I use here? View? How?
How I have MainVC with 2 View that hidden/shown based on tab.
In FirstVC I need to load tableView. In SecondVC - simpleView
So, can somebody give me some advices how to achieve this thing more cleverly?
You might want to use Container View Controllers, I blogged about that awhile back. (See http://www.notthepainter.com/container-view-controllers/)
Text pasted here for the future:
OS5 added something a lot of iOS developers have been needing, container view controllers. This lets you put a view controller inside another view controller. This is wonderful for countless reasons, but the one that draws me is reuse and abstraction.
I was working on a app which had 2 similar windows, they had a top part (body) and a footer. The top part was easy, they each had their own UIViewControllers. But when I went to add the footer to the second one I got to thinking DRY, I was repeating myself and that’s never a good idea. So I abstracted out a parent class and I went to put the footer code in and I got stuck, I wanted my footers to also be view controllers.
I remembered a talk I attended about new iOS5 features and container view controllers was mentioned. After a bit of googling around I had it.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// set up the footer contained view, this has nothing to do with table footers, this is below
// the tableview
//
FooterViewController *vc = [[FooterViewController alloc] initWithNibName:#"FooterViewController" bundle:nil];
vc.view.frame = self.footerFPOView.frame;
vc.delegate = self;
[self addChildViewController:vc];
[vc didMoveToParentViewController:self];
[self.view addSubview: vc.view];
}
Of course there are a few things to note. First, hooray, I’m loading my footer from a xib file. I’ve placed a UIVIew called footerFPOView in the outer view controller’s xib, this is a trick I use all the time. FPO stands for For Position Only and that lets me use interface builder for positioning. I communicate with a protocol, hence the delegate. And then I call addChildViewController to add it, and then I tell the new one that it has a new parent, and finally, add its view.
This is just a few lines of code yet the window should respond to both view controllers and respond to rotations.
I am interested in creating an app that starts with a menu which may possibly contain an options view, then steps from the menu view to a data-item selection view, then to a configuration view, and finally a result view that displays progress or changes. I want to have this process be repeatable like a loop, and have the user be able to jump backwards to a previous view if necessary. Jumping from view to view would of course be a user input / output with a button or something. FYI, I am using Xcode 5.1.1.
What would be the best approach to this? What kind of view controller is going to do the trick? I have heard a lot about navigation controllers, tables, etc.. but am having a hard time figuring out what to use in my case.
Below is a state-diagram similar to what I would like to do...
A UINavigationController should work great as your root view controller. It automatically includes a back button, and you can use the popToRootViewController method to return to the root of the navigation controller. You can set up a navigation controller as your root view controller from the applicationDidFinishLaunching method using this code.
MainMenuViewController *mainMenuViewController = [[MainMenuViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:mainMenuViewController;
self.window.rootViewController = navController;
For more information take a look at apples UINavigationController programming guide https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html
Each of your other screens may use different types of view controllers depending on their specific needs. If you need to display a list of items, definitely look into a UITableView. Apple's documentation for a UITableViewController can be found here https://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewController_Class/Reference/Reference.html
Right I have looked at a few SO questions on the subject and I am finding it difficult to come up with the correct solution here.
Requirements
I have a UITabBar based application. One of the tabs has a UINavigation controller with UISegmentedControl at the top allowing the user to switch between three different views.
Each view will have a UITableView which will allow the user to navigate to another view. These views should be pushed onto to the navigation controller.
Problem
Now all the SO questions and Answers on the subject show how to switch between views. However I need the view I switch to, to allow pushing of another view onto the navigation stack. I am not sure this is even possible. I thought about UIViewController containment - however that would show a view being pushed onto the stack in a smaller window that the screen's bounds. Not what I am looking for.
Any ideas how I can solve this with storyboards and UIViewControllers?
UPDATE
Here is what I am trying to do: In the screenshot the container area is where I need to load other view controllers into. The UISegment control cannot go into the navigation bar as that space is used for something else. So that's why I think UIViewController containment might be better here?
So even though this isn't using separate TableViewControllers, you can use different custom UIViews that are hidden by default and become visible when you select it's corresponding button. This will unfortunately make it so you have all three view's logic in the same VC.
To get around this, you can try setting up some delegates and mimicking the TableViewController logic separation by sending out the didSelectTableAtIndexPath, UIGesture touches, etc into classes outside the ViewController to help keep your code cleaner.
User UITabBarController and hide the tab bar.
- (void)viewDidLoad
{
self.tabBar.hidden = YES;
}
Binding the segment control with method valueChanged
- (void)valueChanged:(UISegmentedControl *)seg
{
if ([seg.selectedSegmentIndex == 0]) {
self.selectedIndex = 0;
} else if ([seg.selectedSegmentIndex == 1] {
self.selectedIndex = 1;
}
}
I achieve this by this way, I hope this will help.
I create a ViewController named "YLJTestViewController" by interface builder ,code is like:
-(IBAction)DoneButtonPressed:(id)sender
{
YLJTestViewController *testViewController = [[YLJTestViewController alloc]initWithNibName:#"YLJTestViewController" bundle:nil];
[self.navigationController pushViewController:testViewController animated:YES];
//[self.view addSubview:testViewController.view];
}
but when I use [self.view addSubview:textViewController.view];it crashed,but use [self.navigationController pushViewController:testViewController animated:YES];it works well,so what's the difference?I thought they are the same...
pushViewController is like adding a piece of paper onto a stack of paper, while addSubView is like gluing a piece of paper onto another paper.
There is no explicit relationships between the previous view and the new view of the view controller which is pushed (like the pieces of paper are still separated in the stack). While the parent view will keep a strong reference to its subviews (like glue).
-addSubview: is a method of UIView. It inserts a view into another view. Like adding a button on a page.
-pushViewController: is a method of UINavigationController. It pushes a view controller onto a navigation stack. Like sliding from a table view to a details view.
In short, -addSubview: composes a view. -pushViewController: is a transition between views.
As sptrakesh states in this Apple Support forum thread:
addSubview is a lower level feature, which you use to add additional
views to your parent/main view. pushViewController replaces the
current main view in your window with the view associated with the new
view controller. You use presentModalViewController when you want to
display a view modally (blocks previous view) on top of your current
view. If you use full screen for your modal view controller, there is
not too much difference between pushViewController and this in terms
how the UI behaves. When you use pushViewController you can "pop" to
any view controller in the array of view controllers that have been
pushed, which is not as easy to do with nested modal views.
In your case the problem is not the use of addSubview: vs. pushViewController:animated:, but simply a typo when you use addSubview:.
[self.view addSubview:textViewController.view]; // misspelled
Should be (replacing x with s)
[self.view addSubview:testViewController.view]; // correct
As for the difference between addSubview: vs. pushViewController:animated:, others have already made good answers. Basically you should use pushViewController:animated: when you replace your entires screen's content, and addSubview: when you add non-full screen UI elements to an existing view.
When we are talking about the view of a UIViewController, pushViewController:animated: should be your preferred method.
I've recently ran into similar problems with addSubview and pushViewController. Everyone here has made great comments, but I would add one things:
Usually addSubview is not used by itself. You usually are using it with presentModalViewController, or in the case of controller containment, addChildViewController.
So in summary:
If you are using navigation controllers, you use pushViewController/popViewController to navigate through your app.
If you are manually switching views, use presentModalViewController.
If you are doing controller containment, use addChildViewController.
If you are using story boards, use Segues.