I'm trying to add a view controller container library to the remail email client. The idea is that the view controller container presents its child view controllers in two layers. It provides functionality for sliding the top view to reveal the views underneath it.
In the library description this is the suggested way of attaching a child controllerView to it's parent:
if (![self.slidingViewController.underLeftViewController
isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
where slidingViewController is the top-level instance of the view controller container. With this instance, you can set the view controllers underneath the top view and add panning.
I'm using xib files, not a storeyboard. so my code looked like this instead:
if (![self.slidingViewController.underLeftViewController
isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController =
[[MenuViewController alloc] initWithNibName:#"Menu" bundle:nil];
}
but using that code.. I'm getting this error:
-[__NSArrayM insertObject:atIndex:]: object cannot be nil
which can be traced to slidingViewController doing the following:
[self.view insertSubview:_underTopViewController.view atIndex:0];
looking at the documentation.. I see there is a difference between instantiateViewControllerWithIdentifier and initWithNibName: the former returns an object at all times.. where as the latter only loads the first time the view controller’s view is accessed.
Question: how do I make an initWithNibName return a loaded viewcontroller object regardless if that view has been visited or not.. similar to instantiateViewControllerWithIdentifier?
You should be able to trigger it by just accessing the view property, something like;
if (![self.slidingViewController.underLeftViewController
isKindOfClass:[MenuViewController class]])
{
MenuViewController *vc =
[[MenuViewController alloc] initWithNibName:#"Menu" bundle:nil];
[vc view]; // <-- access the view and trigger loading
self.slidingViewController.underLeftViewController = vc;
}
Related
I searched for this on SO and other sites but didn't find anything useful
What I have is..
One base class say TOPViewController.h/.m
In this class I have created my controls and I am using this class on all my other views, by creating object of this class.
Say,
ViewController1, ViewController2, ViewController3 are my other views and I am using the TOPViewController on all these views.
Now My current view is ViewController2 which is visible. I jumped from ViewController1 to ViewController2.
now in my TOPViewController how I will come to know which is my current viewcontroller visible.
All the view controllers are adding TOPViewController object as [self.view addSubview:topViewObj];
Here is code of adding my TOPViewController in all views,
topBarViewObj = [[TopBarViewController alloc]init];
topBarViewObj.view.backgroundColor = [UIColor whiteColor];
topBarViewObj.view.frame = CGRectMake(0, 0, 320, 50);
topBarViewObj.titleLable.text = #"TEST";
[self.view sendSubviewToBack:topBarViewObj.view];
[self.view addSubview:topBarViewObj.view];
Please guide me for the same.
Thanks in advance..
If you want to know which is the top most UIViewController visible according to your UINavigationController (so the last UIViewController that has been pushed on your UINavigationController stack):
self.navigationController.topViewController;
if you are using UINavigationControoler
you can get baseViewController
baseViewControoer = [self.navigationController.viewControllers firstObject]
But if you did not use the following line
[self.navigationController pushViewController: animated:];
you will not get above result
as you are adding TopBarViewController's view with addSubView to your view so you just try to get the parent class by calling
`id parentClass = [self.view parentViewController]; `
and then try with
`if([parentClass isKindOfClass[ViewController1 Class]]){
// view 1
}
else if ([parentClass isKindOfClass[ViewController2 Class]])
{
// view 2
}
else if ([parentClass isKindOfClass[ViewController3 Class]])
{
// view 3
}
`
It might work for you. Let me know.
I am designing an iOS app with 10 main UIViewControllers in it. Each representing a different section of the app. Its basically for a company and shows info about the company.
One of the things I am doing on bottom section of the app (in all the different view controllers) is displaying a UIView which contains a map. This map shows a certain location.
Now it works, but the problem I have is that I have 10 copies of the same code and 10 copies of the same UIView.
Is there anyway I could make a small view controller with one class attached to it that would handle the map and then just create an instance of the view controller in all my 10 view controllers in my app?
I hope my question makes sense. Basically I want to know how I can go about reusing ONE UIView in all 10 of my ViewControllers. So I can just call it or something and it appears.
Update - this is basically what I am trying to achieve
Thanks, Dan.
View controllers can contain other view controllers. You can either use a container view in a storyboard or setup the relationship programmatically (see: Creating Custom Container View Controllers).
The storyboard container view is easiest, but the programmatic solution isn't too bad.
- (void)displayContentController:(UIViewController *)content
{
[self addChildViewController:content];
content.view.frame = [self frameForContentController];
// NOTE: You could also add it to any subview of self.view.
[self.view addSubview:content.view];
[content didMoveToParentViewController:self];
}
- (CGRect)frameForContentController
{
return CGRectMake(…);
}
- (void)viewDidLoad
{
…
MyMapViewController *mapViewController = …;
[self displayContentController:mapViewController];
…
}
- (void)dismissContentController:(UIViewController *)content
{
[content willMoveToParentViewController:nil];
[content.view removeFromSuperview];
[content removeFromParentViewController];
}
Final Note: Have each parent view create its own instance of the map view controller. Resist the temptation to reuse an instance of the map view controller between parents.
Update to address questions
So lets say I had 2 of the same view controllers open at once and they both were displaying the same imported viewcontroller then it wouldn't work right?
You can't do this. An instance of a view controller can only have 1 parent view controller. Create separate instances for each use.
So if I create different instances, I can reuse the same view lets say 5 times in one view?
Yes, if you create different instances, you can put as many as you need on a view.
Let me be clear, an instance is a distinct memory location created using a constructor.
MyMapViewController *mapViewController1 = [[MyMapViewController alloc] initWithNibName:#"MyMapViewController" bundle:nil];
MyMapViewController *mapViewController2 = [[MyMapViewController alloc] initWithNibName:#"MyMapViewController" bundle:nil];
or
MyMapViewController *mapViewController1 = [self.storyboard instantiateViewControllerWithIdentifier:#"MapViewController"];
MyMapViewController *mapViewController2 = [self.storyboard instantiateViewControllerWithIdentifier:#"MapViewController"];
Updated to demonstrate dismissing a container view controller.
Here is a method for a child view controller, so it can use to dismiss itself.
- (void)dismissFromParentViewController
{
[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
}
Please try below method:
Create "map controller" super class with inherit to UIViewController and define your need common method and variables.
Inherit your 10 child class into "map controller" super class. And connect common IBOutlets and IBActions to super class.
You can access common methods and variable to super class from child class(10 view controller child class).
Please refer below code
#interface mapController : UIViewController
{
NSString *mapControllerVariables;
}
-(IBAction)mapControllerActions:(id)sender;
#end
#interface yourChileView : mapController
{
}
#end
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
I have a UIViewController (which is my apps root view controller) that contains a UINavigationController that I reload using
- (void)presentSelectedViewController
{
[[self navController] popToRootViewControllerAnimated:NO];
[[self navController] setViewControllers:#[[self selectedViewController]] animated:NO];
}
self.selectedViewController is set through this:
- (void)setSelectedViewController:(UIViewController *)selectedViewController
{
if (_selectedViewController != selectedViewController)
{
if ([selectedViewController isKindOfClass:[SNPStandInViewController class]])
{
Class newClass = [(SNPStandInViewController *)selectedViewController actualClass];
if ([newClass class] == [SVWebViewController class])
selectedViewController = [[[(SNPStandInViewController *)selectedViewController actualClass] alloc] initWithAddress:[PREFERENCES urlForDefaultSearchProvider]];
else
selectedViewController = [[[(SNPStandInViewController *)selectedViewController actualClass] alloc] init];
if ([selectedViewController isKindOfClass:[SNPSettingsViewController class]])
[(SNPSettingsViewController *)selectedViewController setDelegate:[self rootController]];
}
Class oldClass = [self.selectedViewController class];
NSInteger oldControllerIndex = [self.viewControllers indexOfObject:self.selectedViewController];
[self.selectedViewController willMoveToParentViewController:nil];
[self.selectedViewController removeFromParentViewController];
[(SNPStandardViewController *) self.selectedViewController releaseResources];
[self willChangeValueForKey:#"selectedViewController"];
_selectedViewController = selectedViewController;
[self didChangeValueForKey:#"selectedViewController"];
[self.viewControllers replaceObjectAtIndex:_selectedIndex withObject:selectedViewController];
if (nil != oldClass && oldControllerIndex != NSNotFound)
{
[self.viewControllers replaceObjectAtIndex:oldControllerIndex withObject:[[SNPStandInViewController alloc] initWithClass:oldClass]];
}
[[self indexTableView] reloadData];
}
[self presentSelectedViewController];
}
(The standin view controller class is an NSObject subclass that basically holds the class of the view controller that should be there and creates it if selected -- though it is visually not a tab interface, think a tab interface where I don't want to have the actual view controllers that are not selected be created and in memory and running)
I have one view controller that has a UICollectionView in it and this is the default view controller that is set using the above code when the app first runs. It runs fine and the UICollectionView loads its data before the -(void)viewWillAppear: of that view controller runs (a -(void)loadView implementation creates the UICollectionView and does a [uiCollectionViewInstance reloadData]).
If I select another view controller above, and then reselect the initial one (which is a new instance -- old instances when moved off of are destroyed), the UICollectionView does not load its data (even though the loadView routine creates it and calls reloadData) until after both viewWillAppear: and viewDidAppear actually run. This is bad because I call a selection routine in viewWillAppear: to set a highlight in the collection view on the item that is the default selection [think a button bar type interface].
The same code path seems to run each time I create this view controller that contains the collection view (with extensive NSLog outputting to show the path). So I assume that maybe something is happening in the actual routine that creates the new view controller and sets it as the active one in the UINavigationController
I am at a loss to know what to explore to see why this collection view has no visible cells after being created and reload data being called on it when it is created after being selected in my pseudo tab controller described above (but it works fine when set initially upon creation of the pseudo tab controller).
I don't know what else people want to see but I'd be happy to try and post more info, code, etc as necessary.
in the -presentSelectedViewController I added this after doing the setViewControllers and now it all works fine.
[[[self selectedViewController] view] setNeedsLayout];
[[[self selectedViewController] view] layoutIfNeeded];
Hopefully someone can help.
I've got an app that uses the UISplitViewController however I've now had to start using multiple storyboards because i've got a large amount of views and Xcode was starting to run really slow. I've moved moved the UIViewControllers to multiple storyboards.
The Master View is built from static cells, so when the user selects the cell I normally change the view by pushing a segue.
I'm now wondering how to programmatically change the detail view of a UISplitViewController?
Thanks
Subclass UISplitViewController and set your root splitViewController to that class. Then add this method to your UISplitViewController subclass:
-(void)setDetailControllerTo:(UIViewController *)detailController withNavControllerTitle:(NSString *)title {
[detailController view]; // this line forces the viewDidLoad method to be called
if (title) {
UINavigationController *navController = [[UINavigationController alloc] init];
[navController pushViewController:detailController animated:YES];
detailController.title = title;
NSArray *viewControllers=#[self.mainController.viewControllers[0],navController];
self.mainController.viewControllers = viewControllers;
} else {
NSArray *viewControllers=#[self.mainController.viewControllers[0],detailController];
self.mainController.viewControllers = viewControllers;
}
}
To call this method do something like this from the master view controller:
FixedSplitViewController *splitController = (FixedSplitViewController*) self.splitViewController;
CurrentEventViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"CurrentEventViewController"];
// add any setup code here
[splitController setDetailControllerTo:controller withNavControllerTitle:#"Current Event"];
A lot of my projects require the splitviewcontroller to always show the master view so I use this subclass to keep the master view from hiding on portrait rotation.