Container View Controller Programmatically - objective-c

I have been researching this for a while but can't quite find what I need. I would like to learn how to create a container view with a child view controller programmatically. I'm still fairly new to this and learning the basics, but from what I gather, this used to be done using resuable views and attaching them to child view controllers prior to the container view object being added to the library (right?), I am looking for either a tutorial or example code that shows how to do it from scratch, using xib's, but without any complications, like adding table cells etc... Just the container and the child programmatically. Does that make sense? I'm sure there must be something on S.O. Thanks if you can help.
UPDATE ----------------------------------------------------------------------------------------------------------------------
I have managed to create a child view controller that appears with a UIButton action. Relevant code:
- (IBAction)Pressed:(id)sender {
ChildViewController *childViewController = [[ChildViewController alloc]init];
[self displayContentController:childViewController];
}
- (void) displayContentController: (UIViewController*) content {
[self addChildViewController:content];
content.view.frame = CGRectMake(0, 115, 320, 240);
content.view.backgroundColor = [UIColor redColor];
CATransition *transition = [CATransition animation];
transition.duration = 1;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[transition setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[content.view.layer addAnimation:transition forKey:nil];
[self.view addSubview:content.view];
[content didMoveToParentViewController:self];
}
So that's working fine. I click the button and a red square, child view controller, occupying a small part of the screen appears. What I would like to know is if this is best practice.

Basically this involves having 1 Parent View Controller who'll orchestrate the appearance of his child view controllers.
To be honest, I find the Apple Docs quite complete on this part.
Quote from the same docs :
Adding a Child View Controller to Your Content
To incorporate a child view controller into your content programmatically, create a parent-child relationship between the relevant view controllers by doing the following:
Call the addChildViewController: method of your container view controller.
This method tells UIKit that your container view controller is now managing the view of the child view controller.
Add the child’s root view to your container’s view hierarchy.
Always remember to set the size and position of the child’s frame as part of this process.
Add any constraints for managing the size and position of the child’s root view.
Call the didMoveToParentViewController: method of the child view controller.
Codesample :
- (void) displayContentController: (UIViewController*) content {
[self addChildViewController:content];
content.view.frame = [self frameForContentController];
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self];
}
Responding to update about best practices :
Objc.io has some pretty neat articles in this regard.
For example, the article talking about View Controller Containment notes :
View controllers should be reusable and self-contained entities.
Child view controllers are no exception to this rule of thumb. In
order to achieve this, the parent view controller should only be
concerned with two tasks: laying out the child view controller’s root
view, and communicating with the child view controller through its
exposed API. It should never modify the child’s view tree or other
internal state directly.
Child view controllers should contain the necessary logic to manage their view trees themselves – don’t treat them as dumb views. This will result in a clearer separation of concerns and better reusability.
They also talk about transitions between view controllers while using this pattern.

- (void)addViewControllerToContainer:(UIViewController *)viewController {
[self addChildViewController:viewController];
viewController.view.frame = self.container.bounds; //important!!
[self.container addSubview:mapVC.view];
[viewController didMoveToParentViewController:self];
}

Related

Passing data between multiple uiview in uiscrollview

I have a ViewController that contains a uiscrollview and I create 2 uiview as pages inside the uiscrollview with paging and scrolling enabled:
_scrollView.frame = CGRectMake(_scrollView.frame.origin.x, _scrollView.frame.origin.y, _scrollView.frame.size.width, _scrollView.frame.size.height);
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width, _scrollView.frame.size.height*PAGES_NUMBER);
_scrollView.delegate = self;
[self.view addSubview:_scrollView];
//PAGE 1
genderViewController = [[GenderViewController alloc]init];
genderViewController.view.frame = CGRectMake(0.0,
_scrollView.frame.size.height*0,
_scrollView.frame.size.width,
_scrollView.frame.size.height);
[_scrollView addSubview:genderViewController.view];
//PAGE 2
ageViewController = [[AgeViewController alloc]init];
ageViewController.view.frame =CGRectMake(0.0,
_scrollView.frame.size.height*1,
_scrollView.frame.size.width,
_scrollView.frame.size.height);
[_scrollView addSubview:ageViewController.view];
The thing is that I'm wondering how can I pass data between this 2 uiview.
For example, I have a UITextField in the first page "genderViewController" and I want to show this content in and UILabel in the second page "ageViewController" once I scroll to the second page.
I know how to pass data between 2 different view controllers in a navigation view, but I don't know how to do this at this stage.
I would appreciate if you guys could help me with that.
Thanks!
It is pretty much the same as it is in a navigation view. Aparently your two view controllers are nested in that scroll view which is owned by a third, lets say the master, view controller.
The most clean way of implementing this would be if the view controller with the UITextField (genderViewController) would call any delegate method that you define of the master view controller. For that the master would set itself as delegate of genderViewController (you may need to add a delegate protocol and a setter).
If you want to implement it as in the books, then declare the callback/delegate method in a protocol which the genderViewController conforms to.
Upon this method being called your master view controller would call a setter of the ageViewController. Within this setter ageViewController will set the value of the UILable
Delegate methods maybe suitable for some specific operations. But I would find it - conceptually - more appropriate to use Key/Value Observing in such cases.
Documentation can be found at: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOCompliance.html
For your case, ageViewController can observe changes for a keyPath (say textField.text) in genderViewController.

Adding a header view to every view in an application

I have a header that I would like to add to every view in my program. Rather than manually do this I've been trying to find a way to insert this "header view" above every view that gets loaded. In the sample below, I tried using the insertSubView method but this inserts the entire view and not just the tiny bit of header content I'm interested in.
[self.view insertSubview:self.headerController.view aboveSubview:self.indexController.view];
[self.view insertSubview:self.indexController.view atIndex:0];
Does anyone know how to do this correctly? I'm using Xcode 5.
You're inserting the views of the headerController and indexController on your other view controllers' views. Generally, though, you want to be careful about keeping your view hierarchy synchronized with your view controller hierarchy. The importance of this is discussed in some detail in WWDC 2011 video Implementing UIViewController Containment.
So, generally you'd instantiate the appropriate indexController and headerController, call addChildViewController, add the subviews, and then call didMoveToParentViewController for each of those two view controllers. For more information, see Creating Custom Container View Controllers section of the View Controller Programming Guide for iOS. But you app's view controllers might do something like:
HeaderController *headerController = ... // instantiate however is appropriate
[self addChildViewController:headerController];
headerController.view.frame = [self frameForHeaderController]; // define the frame/constraints as appropriate
[self.view addSubview:headerController];
[headerController didMoveToParentViewController:self];
You'd then repeat that process for the indexController.
Just make sure to (in addition to doing the custom container calls) either set the frame of the view controller's frame or define auto-layout constraints that will dictate the placement of the view (like you do for any programmatically added view). If you don't specify the frame, it may well end up being CGRectZero, which is obviously not what you intended.
You might, though, consider flipping this around. Make a custom container view controller that includes your header and any other UI elements that persist for every view. Then make your app's view controllers as child controllers of that container "parent" view controller. If either the header controller's view or the index controller's view navigates your app between various content view controllers, this sort of structure might make more sense.
I think if you watch that WWDC video and/or review the Creating Custom Container View Controllers, this will make more sense.
I'd suggest to create a base view from which you will subclass for every views that need this header.
You could have a BaseView.h that would look like:
#interface BaseView : UIView
#property (nonatomic) UIView *someHeaderView;
#end
And the implementation (.m) looking like this:
#implementation BaseView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(frame), 150.0)];
[self.view addSubview:self.headerView];
}
return self;
}
#end
Then have every views that requires a header subclass it:
#interface SomeViewRequiringHeader : BaseView
#end
Try reversing the order of the two:
[self.view insertSubview:self.indexController.view atIndex:0];
[self.view insertSubview:self.headerController.view aboveSubview:self.indexController.view];
Also, since headerController is a UIViewController, it probably has a nib file with a default size of full screen. If you want to only be a certain height, you have to modify that nib file with the appropriate height.

how to make a view not programmatically as cameraoverlay view

I need to create a cameraoverlay view(to add on ZBar sdk reader), my question is how can I create all objects I need not programmatically. right now I create all objects programmatically and then add them to myView, and use myView as cameraoverlay view.
[self.myView addSubview: myImage];
[self.myView addSubview: self.mylabel];
[self.myView addSubview: myButton];
reader.cameraOverlayView=self.myView;
I tried to add another control view and added a view to it then made a tabbar and tried this code which doesn't work:
TestViewController *test=[[TestViewController alloc]init];
reader.cameraOverlayView=test.testView;
making objects programmatically is hard for me is this possible to find source code for objects which created in xcode, for example when I create a custom button in xcode can I find source code which has generated for this button and just copy it in my program.
There are several approaches that will work
Create a custom conainer view in code. Add the zbar view, and any custom views (scanner graphics, etc on top). This is what i recently did.
Create a xib-baased view, and include the zbar view in that. See the 'object' component.
Load custom views from xibs and add them.
If you have the time, I recommend you invest to learn how to create views programmatically.
This involves:
in your view contoller override the loadView method.
return a custom sub-class of UIView that contains sub-views. These include the zbar view and your overlays.
learn how to use the layoutSubviews method on UIView. Hint set your child view's frame relative to thecparent views bounds.
There are loads of custom components on Github. Check out DCSwitch or CMPoptip, or read up on contributions to ManiacDev.
* As requested - adding subviews after successful scan *
- (void)readerView:(ZBarReaderView*)view didReadSymbols:(ZBarSymbolSet*)symbols fromImage:(UIImage*)image
{
for (ZBarSymbol* symbol in symbols)
{
[self presentScannedOverlay];
[_scanInProgressOverlay setAnimating:NO];
[_readerView stop];
[_delegate didScanPayload:symbol.data];
break;
}
}
- (void)presentScannedOverlay
{
//Be sure to override layoutSubviews, so that you can position the view below,
//relative to its parent. . if you already know the size of the parent, just replace
//CGRectZero with CGRectMake(some values)
_scannedOverlayView = [MyOverlayView alloc] initWithFrame:CGRectZero];
[self addSubView:scannedOverlayView];
}

How to implement more than one controller in objective C

First of all I don't know If controller is the right word. What I want to achieve is this.
#interface ClubViewController : CoreDataTableViewController :NRGridViewController
{
I know that this is not possible in objective-C. But is there a way to work around this?
Because I want to use CoreDateTableViewController and NRGridViewController.
Kind regards
Stef
EDIT
This is how my storyboard Hierarchy looks like.
-ViewController
-TableView
-View
-TableViewCell
So I have a tableview Controller but above this tableview controller you find a small view with three buttons. When I push on button 1 I want to take the tableview away and draw a gridView with the NRGridview Controller. But when I push on button 2 and 3 I fill up my tableview using the CoreDataTableViewController.
I hope this explains more my problem.
I think one way to do this is with a container view with a container view controller inside it. That container controller would have 2 child controllers which would be your CoreDateTableViewController and NRGridViewController. I've implemented something like this, and I can show you some code if you're interested.
After Edit: In a test app, I started with a single view template and a storyboard. I added two buttons to the top of the view and a container view to the bottom half of the view (this first controller is of class ViewController). I then dragged out a new view controller, and control dragged from the container view to the new controller and chose the "embed segue" (this will resize the view to be the same size as the container view). The class of this controller was changed to my subclass, ContainerController. I then created 2 more controllers for the 2 views that will be managed by the container controller (the views need to have their size set to "freeform" in IB so you can set the size to be the same as the container view). Here is the code in ContainerController:
- (void)viewDidLoad
{
[super viewDidLoad];
self.cont1 = [[FirstController alloc]initWithNibName:#"FirstView" bundle:nil];
self.cont2 = [[SecondController alloc]initWithNibName:#"SecondController" bundle:nil];
[self addChildViewController:self.cont1];
self.currentController = self.cont1;
[self.view addSubview:self.cont1.view];
}
-(void)switchToFirst {
if (self.currentController != self.cont1) {
[self addChildViewController:self.cont1];
[self moveToNewController:self.cont1];
}
}
-(void)switchToSecond {
if (self.currentController != self.cont2) {
[self addChildViewController:self.cont2];
[self moveToNewController:self.cont2];
}
}
-(void)moveToNewController:(id) 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;
}];
}
The only code I have in ViewController are the IBActions for the 2 buttons that switch the views. Those methods just call methods in the container controller:
-(IBAction)chooseFirstController:(id)sender {
[self.childViewControllers.lastObject switchToFirst];
}
-(IBAction)chooseSecondController:(id)sender {
[self.childViewControllers.lastObject switchToSecond];
}
What you are trying to do in your code is creating a class that is a subclass of multiple other classes, which is not possible. If you really want to do this, check out this question: Inherit from two classes
If you are trying to create multiple instances:
CoreDataTableViewController and NRGridViewController are just classes, which you will have to instantiate to get an actual object.
You can instantiate e.g. an NRGridViewController using
NRGridViewController *controller=[[NRGridViewController alloc] init];
I hope this answers your question, it is a bit difficult to understand your question.
Instead of taking tableViewController, take normal TableView (drag and drop from the storyboard to the particular position on the view).
when the button 1 is pressed Make the Table View hidden in the buttons action method. and initialize the grid view / or set grid view hidden to NO. (all views have the property of hidden in ios)
when you press on the 2nd and 3rd button set the grid view hidden and set tableview hidden equal to NO. and fetch the coredata and store it in array or dictionary or you can reload the tableview.
(Initially, before pressing the button 2 & 3 , the table view has no values. so you can set a bool property that when you press the button 2 or 3 set the bool and use the bool to reload your tabe view )
if you did not get my explanation ping me back.

Why need to use 'addChildViewController:' method

please explain me, why I need to use addChildViewController: method?
After all, when I put subview to view [some_obj.view addSubview:some_view], these subview knows about his controller.
Thanks.
addChildViewController
Needs to be called, cause the parent controller needs to hold a reference to its child view controller for lifetime / background management... this reference cannot / shouldn't be obtained from UIView, when added as a subview
Also you can than reference parent from the child via self.parentViewController
Apple Says about addChildViewController
Adds the given view controller as a child. If the new child view
controller is already the child of a container view controller, it is
removed from that container before being added. This method is only
intended to be called by an implementation of a custom container view
controller. If you override this method, you must call super in your
implementation.
For adding / removing, you can refer to this great category and have no worry when to call it:
UIViewController + Container
- (void)containerAddChildViewController:(UIViewController *)childViewController {
[self addChildViewController:childViewController];
[self.view addSubview:childViewController.view];
[childViewController didMoveToParentViewController:self];
}
- (void)containerRemoveChildViewController:(UIViewController *)childViewController {
[childViewController willMoveToParentViewController:nil];
[childViewController.view removeFromSuperview];
[childViewController removeFromParentViewController];
}