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];
}
Related
I have a small question about UIViewController.
Is it possible to handle, in a custom UIViewController class, when the controller is added to current view controller using
[self addChildViewController:customViewController];
or when removing
[customViewController removeFromParentViewController];
For now, I have done what I want using viewDidLoad and dealloc methods but I was wondering if there were a better solution.
Thanks,
Your view controller can override this method when it wants to react to being added to a container.
- (void)didMoveToParentViewController:(UIViewController *)parent;
As by Apple doc
If you are implementing your own container view controller, it must
call the didMoveToParentViewController: method of the child view
controller after the transition to the new controller is complete or,
if there is no transition, immediately after calling the
addChildViewController: method.
The correspectiv for dealloc (which is anyway discouraged) is by Apple doc
- (void)willMoveToParentViewController:(UIViewController *)parent;
If you are implementing your own container view controller, it must
call the willMoveToParentViewController: method of the child view
controller before calling the removeFromParentViewController method,
passing in a parent value of nil.
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];
}
I have a UIButton that I create in my sub class ViewController, and add it to my MainViewController.
Now, I added a target method to this button that should push another view controller to my Navigation controller (the one that in the MainViewController).
I know that the method did call when I push the button, but the view wasn't push to the Navigation Controller.
I scanned this drawing - this is the drawing (I also added part of my code):
This is the code I'm using in my button:
(remember it's in a deferent ViewController).
- (void)buttonPressed:(UIButton *)sender
{
Photo_ScreenGlobalView *photo = [[Photo_ScreenGlobalView alloc] init];
[self.navigationController pushViewController:photo animated:YES];
}
Usually I solve these situations with delegation. If there is a view controller which is subordinate to another (i.e. a "sub" view controller) but should have the ability to trigger navigation changes, etc... then it should have a weak pointer back to it's 'parent'. Then the parent VC should implement an appropriately named protocol with a callback for the child to use. The names of these things can be generic, such as #property navigationDelegate and requestNavigationToViewController: or they can be more semantic, such as #property userFormDelegate and userFormDoneButtonPressed:
Generally speaking, a subordinate view controller should not be able to directly modify navigation at it's parent's level; but it can trigger it via more loosely-coupoled interfaces like these.
i came back to let you all know how i actually did it.
after googling a lot found this nice and quick guide how to make DELEGATE
and working with delegate solved all my problems. if you need any help don't hesitate to send me PM.
this is the guide:
http://css.dzone.com/articles/do-not-publishcreating-your
I have a UIView class which I am currently removing from my view by using from inside the class [self removeFromSuperview]. Hopefully that's the correct thing to do.
However, now from my view controller (of which I add this view to) I need to know when it has removed itself so that I can call a method when this happens.
Generally speaking, the view shouldn't be doing things like removing itself. That's the job of the view controller.
If a UIView subclass can produce events that require the view hierarchy to be changed, I would define a delegate property for that view, and when an event occurs, call a method on that delegate. Then, when your view controller adds the view, it would set itself as the delegate and define the relevant method to handle the event.
If you have removed the UIView
view.removeFromSuperview()
you can check if it exists with the following code:
if !(view.superview != nil) {
//no superview means not in the view hierarchy
}
You could have a delegate callback setting the controller as the view's delegate. When you're about to remove the view, make the delegate callback and implement the callback method in your controller.
The 'removeFromSuperview' has always seemed backwards to me… :(
I'm assuming you are making the remove call after some sort of action, like a button press or something. if that is the case, set the buttons delegate to be the view controller, not the view class, and inside the action method in the view controller, call
[yourCustomView removeFromSuperview];
The best choice would be to let the controller remove the view
[self.view removeFromSuperview];
and to know if the view was removed (or never added) you can ask
if(![self.view superview]) {
//no superview means not in the view hierarchy
}
Not sure what sdk you are using - but I am using iOS 5 and I just use the following method in the superview:
-(void)willRemoveSubview:(UIView *)subview{
if([subview isEqual:someView]){
//do stuff
}
//you could do some stuff here too
}
This one is probably something simple, still learning the ins-and-outs on this but I've run out of searches for this one with no available answer.
I've got a UIViewController with several elements displayed on it, one such element is a UITableView. The UITableView has it's own class and is allocated in the UIViewControllers viewWillAppear
- (void)viewWillAppear:(BOOL)animated
{
UITableView *insideTableView = [[UITableView alloc] init];
tableView.delegate = insideTableView;
tableView.dataSource = insideTableView;
}
Everything is working fine in regards to the tableview. Today I am experimenting with a few additions, one of which is a new view popup on cell selection within that tableview.
Inside my TableView Class, I have the following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Cell Pressed, Present View");
PopupView *popupView = [[PopupView alloc] initWithNibName:#"PopupView" bundle:nil];
popupView.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:popupView animated:YES];
}
Now it gets called fine, verified by the NSLog, however the view doesn't appear. I know the problem is related to the fact that I want PopUp to appear over the TableViews Parent rather than itself.
I'm just not sure how to properly call it in this instance.
The delegate is a UIViewController which doesn't have its view property set, which is why presentModalViewController:: doesn't work.
You need the view controller containing the table view to present the modal view controllers, but note that that view controller is not the parent of the table view delegate. This is because you have no view controller hierarchy in place.
The easiest way to fix this is to put those methods inside the view controller whose view contains the table view. Alternatively the table view delegate needs to hold a reference to the view controller so it can call presentModalViewController:: on it.
The latter approach can lead to retain cycle, so you have to use a non-retaining reference. The nicest implementation is the delegate pattern.
Also, you don't want to do the instantiation in viewWillAppear: because that can be called multiple times during the lifecycle of a view controller. Put the code in viewDidLoad and balance it in dealloc. Right now you are leaking memory every time your view appears, which when your modal view controller is working will be every time the modal view controller is presented and dismissed.