Need assistance regarding UIViewController - ios

In an application where several UIViewControllers work together,
firstViewController added to root. Till here its fine now I want to go to secondViewController I dont want to use UINavigationController or UITabBarController. I have already read the View Controller Programming Guide but it does not specify without using UINavigationController, UITabBarController and story board.
and when user want to move from secondViewController to firstViewController how secondViewController will be destroyed?
Apple Doc also does not specify how UIViewController is release or destroyed? It only tell the life cycle inside UIViewController.

If you are concerned about how UIViewController is release or destroyed then here is a scenario for you:-
Here is a button tap method in FirstViewController that presents SecondViewController (using pushViewController,presentModalViewController etc)
In FirstViewController.m file
- (IBAction)btnTapped {
SecondViewController * secondView = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
NSLog(#"Before Present Retain Count:%d",[secondView retainCount]);
[self presentModalViewController:secondView animated:YES];
NSLog(#"After Present Retain Count:%d",[secondView retainCount]);
[secondView release]; //not releasing here is memory leak(Use build and analyze)
}
Now In SecondViewController.m file
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"View Load Retain Count %d",[self retainCount]);
}
- (void)dealloc {
[super dealloc];
NSLog(#"View Dealloc Retain Count %d",[self retainCount]);
}
After Running the code:
Before Push Retain Count:1
View Load Retain Count 3
After Push Retain Count:4
View Dealloc Retain Count 1
If you are allocating and initializing a ViewController, You are the owner of its lifecycle and you have to release it after push or modalPresent.
In the above output at the time of alloc init retain count of SecondViewController is One,,,,surprisingly but logically its retain count remains One even after it has been deallocated (see dealloc method), So require a release in FirstViewController to completely destroy it.

Other way to present a new view controller is doing it like a modal view controller(notice that self is firstViewController):
[self presentModalViewController:secondViewController animated:YES];
then, when you wan to come back to the firstViewController and destroy the secondViewController, you have to dismiss the view controller(from the secondViewController):
[self dismissModalViewControllerAnimated:YES];
Hope that helps.

You can use UINavigationController to move to secondViewController and come back by setting the UINavigationController property 'navigationBarHidden' to YES. Which will hide the navigation bar. Releasing and destroying of view controllers will takes care by this.

Then, you can take other strategy, is not the best to build your view controller hierarchy, but it also could work. You can overlay the secondViewContrller's view over the firstViewController's and make the secondViewController become the child from the firstViewController:
//...
[self addChildViewController:secondViewController];
[self.view addSubview:secondViewContrller.view];
//...
And when you want to remove the view controller, you have to remove the view and ask for the view controller to remove from his parent:
//...
[self.view removeFromSuperview];
[self removeFromParentViewController];
//...
But then you will have to control the view hierarchy by your own(putting and removing views and view controllers by your own).

Related

How to come back from view controller to tabbed view controller

I have an issue the dismissing view controller to the tabbed view controller. I will explain my flow.
view controller to tabbed view controller(push)
I have 5 tabs in the tabbed view controller in the 3 tabbed i have an camera view from their i am presenting the view controller and passing some parameters by using this code
UIStoryboard *storybord=[UIStoryboard storyboardWithName:#"Main" bundle:nil];
shareViewController *shareview=[storybord instantiateViewControllerWithIdentifier:#"share"];
[self presentViewController:shareview animated:YES completion:nil];
//shareview.finalvideourl=videoURL;
shareview.videooutputstring=videoPath;
from the share view controller I want send the data back to the 1st tab for this I am using the below code
UIStoryboard *story=[UIStoryboard storyboardWithName:#"Main" bundle:nil];
TabedfirstViewController *Tabedfirst=[story instantiateViewControllerWithIdentifier:#"id"];
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:Tabedfirst];
[self presentViewController: nc animated:YES completion:^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"ShareArray" object:_selectedimgarray];
[[NSNotificationCenter defaultCenter] postNotificationName:#"SharetitleArray" object:_newtile];
[[NSNotificationCenter defaultCenter] postNotificationName:#"sharevideooutputstring" object:_videooutputstring];
}];
When I do this very thing working good I am sending the data from share view to the tabbed view and I am printing it.
The problem is when I send the second time data from share viewcontroller to the tabbed view controller first data is deleting and the second passed data is replacing the first i.e. I have the 15 objects in an array, from share view controller I am passing array to the tabbed view controller now the array count is 16 and I am printing it, now again I am passing one more object from the share view to tabbed view the array count must increase to 17 but it 16 only.
In the App delegate add/make properties of your arrays like
#property (nonatomic, retain) NSMutableArray *array1;
In AppDelegate.m initiate all your properties
After that in the controller where you are storing the values add instance of AppDelegate as
In
- (void)viewDidLoad
{
[super viewDidLoad];
objAppDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
objAppDelegate.array1= _imagearray=[#[#"bootimages",#"fall-photography-in-hocking-hills", #"gift",#"hillimages",#"Mercedes-classe-S-W116_large_dettaglio_articolo",#"prini‌​mages",#"resplendent",#"tnb4",#"Tomato-plant",#"Vole",#"waterimages",#"fall-photo‌​graphy-in-hocking-hills", #"gift",#"hillimages",#"Mercedes-classe-S-W116_large_dettaglio_articolo"]mutable‌​Copy];
then
[objAppDelegate.array1 insertObject:[userInfo firstObject] atIndex:0];
The values are maintained and can be used anywhere in the whole project. If your number of objects keeps on increasing then you NSMutableDictionary and add as much key/value pairs as you like.
I'm not sure why you are creating new storyboards using your main storyboard. All you have to do is presentViewController from one the viewcontroller you want to return from with the id of a segue from that viewcontroller back to the root of your tabbed view controller. In your storyboard, ctrl+drag from a button (or whatever is triggering the segue) and drag it to the root of the tab view controller. Select the segue and in the attributes editor give it an id. then in the viewcontroller call presentViewcontroller with the id and it should work fine.
In the shareViewcontroller.h write the add the following method
#protocol childDelegate <NSObject>
-(void) sendImage:(UIImage *)img
#end
and the property
#property (assign) id <childDelegate> cDelegate;
once you have done that on the controller where you have the method
-(void) receivedArray:(NSNotification*)notification
add the method
-(void) sendImage:(UIImage *)img
{
// Add image to app.array1 here.
}
In the share view controller where you are sending the image to the last controller call this method
[self.cDelegate sendImage:"your image comes here"];
Still if you face as issue, then refer to How do I set up a simple delegate to communicate between two view controllers?
Let me know if you still face an issue.

viewDidAppear and viewWillAppear methods are not called

I have a programmatically created UIViewController named as "VC" and on top of that I need to load my existing UIViewController.
I used below code to do that, and it's working fine.
I can see my existing UIViewController on "VC" but not detecting any of viewDidAppear or viewWillAppear in existing view controller.
I am getting data from viewDidAppear and viewWillAppear so all the time my existing view controller collection view is empty.
ExistingViewController* presObj= [self.storyboard instantiateViewControllerWithIdentifier:#"oad"];
[vc.view addSubview:presObj.view];
[self addChildViewController:presObj];
[presObj didMoveToParentViewController:self];
Have you initialize the storyboard class?
self.storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
so all the time my existing ViewController collection view is empty --> are you using collection view in your code?
First you need to add the addChildViewController. Then add that childviewcontroller view into your main view.
Just add the [self addChildViewController:presObj]; before adding to the view subview.
ExistingViewController* presObj= [self.storyboard instantiateViewControllerWithIdentifier:#"oad"];
[self addChildViewController:presObj];
[vc.view addSubview:presObj.view];
[presObj didMoveToParentViewController:self];
Ok, If you are loading like this then your ViewDidLoad () of existing view controller will load, and you can put a condition (if needed) then you can call ViewDidAppear () ViewWillAppear() like below
existingviewcontroller *obj=[[existingviewcontroller alloc]init];
[obj viewWillAppear:YES];
[obj viewDidAppear:YES];
This will work

Subview UINavigationController Leak ARC

I'm experiencing a memory leak (the UINavigationController and its root View Controller are both being leaked) when presenting and dismissing a UINavigationController in a subview. My method of presentation of the navigation controller seems a bit non-standard, so I was hoping someone in the SO community might be able to help.
1. Presentation
The Navigation Controller is presented as follows:
-(void) presentSubNavigationControllerWithRootViewControllerIdentifier:(NSString *)rootViewControllerIdentifier inStoryboardWithName:(NSString *)storyboardName {
// grab the root view controller from a storyboard
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
UIViewController * rootViewController = [storyboard instantiateViewControllerWithIdentifier:rootViewControllerIdentifier];
// instantiate the navigation controller
UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController:rootViewController];
// perform some layout configuration that should be inconsequential to memory management (right?)
[nc setNavigationBarHidden:YES];
[nc setEdgesForExtendedLayout:UIRectEdgeLeft | UIRectEdgeRight | UIRectEdgeBottom];
nc.view.frame = _navControllerParentView.bounds;
// install the navigation controller (_navControllerParentView is a persisted IBOutlet)
[_navControllerParentView addSubview:nc.view];
// strong reference for easy access
[self setSubNavigationController:nc];
}
At this point, my expectation is that the only "owner" of the navigation controller is the parent view controller (in this case, self). However, when dismissing the navigation controller as shown below, it is not deallocated (and as a result its rootViewController is also leaked, and so on down the ownership tree).
2. Dismissal
Dismissal is pretty simple, but it seems not to be sufficient for proper memory management:
-(void) dismissSubNavigationController {
// prevent an orphan view from remaining in the view hierarchy
[_subNavigationController.view removeFromSuperview];
// release our reference to the navigation controller
[self setSubNavigationController:nil];
}
Surely something else is "retaining" the navigation controller as it is not deallocated. I don't think it could possibly be the root view controller retaining it, could it?
Some research has suggested that retainCount is meaningless, but FWIW I've determined that it remains at 2 after dismissal, where I would expect it to be zero.
Is there an entirely different / better method of presenting the subNavigationController? Maybe defining the navigation controller in the storyboard would have greater benefit than simply eliminating the need for a few lines of code?
It is best practice when adding a controller's view as a subview of another controller's view, that you make that added view's controller a child view controller; that is, the controller whose view your adding it to, should implement the custom container controller api. An easy way to set this up is to use a container view in the storyboard which gives you an embedded controller automatically (you can select that controller and, in the edit menu, choose embed in Navigation controller to get the UI you're trying to make). Normally, this embedded view controller would be added right after the parent controller's view is loaded, but you can suppress that by implementing shouldPerformSegueWithIdentifier:sender:. I created a simple test app with this storyboard,
The code in ViewController to suppress the initial presentation, and the button methods to subsequently present and dismiss it is below,
#implementation ViewController
-(BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:#"Embed"]) { // The embed segue in IB was given this identifier. This method is not called when calling performSegueWithIdentifier:sender: in code (as in the button method below)
return NO;
}else{
return YES;
}
}
- (IBAction)showEmbed:(UIButton *)sender {
[self performSegueWithIdentifier:#"Embed" sender:self];
}
- (IBAction)dismissEmbed:(UIButton *)sender {
[[self.childViewControllers.firstObject view] removeFromSuperview];
[self.childViewControllers.firstObject willMoveToParentViewController:nil];
[self.childViewControllers.firstObject removeFromParentViewController];
}
#end
The navigation controller and any of its child view controllers are properly deallocated when the Dismiss button is touched.
The navigationController property on a UIViewController is retain/strong, which is presumably the other strong reference.
So try popping all view controllers from the navigation controller and see if that works.

Dealloc is not calling immediately after release

I have a root view that loads two view controller. e.g.:FirstVC,SecondVC.
I am showing FirstVC as the root view controller when the app launches, on some action on FirstVC I load SecondVC by removing first.
For loading SecondVC I first remove FirstVC by
[FirstVCobj.view removeFromSuperView];
[FirstVCobj release];
FirstVCobj = nil;
After that I allocate and create SecondVC
Now only after calling SecondVC's viewdidload() is FirstVC's dealloc() method called.
Is this the right execution path, or is it due to some mistake I have made?
The above is exactly how I remove and create my view controllers.
i assume it is a UIView you're talking about.
addSubview retains the view
removeFromSuperView releases or AUTORELEASES it -- an implementation detail you don't control
to 'see' it: wrap it in a pool of your own
#autoreleasepool {
[FirstVCobj.view removeFromSuperView];
[FirstVCobj release];
FirstVCobj = nil;
}
[FirstVCobj removeFromParentAndCleanup:YES];
Check with this might work.

UIViewController -dealloc method not called

I am working with Automatic Reference Counting.
I have a custom UIViewController subclass and whenever I call -presentViewController: animated:completion: or remove its view from the superview I would like to NSLog something like "I am dealloced" so I know that the view controller has successfully been removed. I have implemented the -dealloc method in my view controller. However I started a test project where I just had two UIViewController instances (no retain cycles) and -dealloc is not called either when I push the second UIViewController modally or when I remove the superview or when I remove it from the parent view controller. Am I missing something ? In my original project (not the test case) Instruments shows me that those controllers leave a memory footprint that I can't get rid off.
If you want to switch view controllers, and have the one you're switching away from be deallocated, then just switch the root view controller of the window. So, if you're in VC1 and want to go to VC2, then do this in VC1:
VC2 *vc2 = [[VC2 alloc] init]; // or however else is appropriate to get an instance of this class
self.view.window.rootViewController = vc2;
If you haven't created any property to point to vc1, then it will be deallocated after making this switch.
If you want to use a modal presentation or a modal segue (to get the animation when you switch controllers), you can still get the initial controller to be deallocated by switching the root view controller after the presentation from the viewDidAppear method of vc2:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.view.window.rootViewController = self;
}
To get a print when the View Controller is deallocated you can implement the dealloc method as
- (void) dealloc {
NSLog(#"The instance of MyViewController was deallocated");
}
Then to get a print when the View Controller left the view you can implement
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(#"The instance of MyViewController left the main view")
}
If you use -presentViewController:animated:completion: you are retaining the parentViewController every time you call this method. ModalViewControllers are simply pushed on top of the other ViewController.
ModalViewControllers are only designed for some kind of information / User Input and stuff like that. If you want to dealloc the ParentViewController you have to deal with your own implementation.
dealloc method isn't called when the class is retained (or something in this class is retained) and not reeleased. It is justly for projects with both ARC and without it. So check your code twice.

Resources