UIViewControllerHierarchyInconsistency View Controller exception - objective-c

I'm trying to change the detail view in a splitViewController, i have the UIViewControllerHierarchyInconsistency exception when i do
self.detailView.view=view.view;
I've controlled if there are other viewControllers in xib files and i also used method removeFromParentViewController, but i've the same exception.
The error is only in iOS 6 but not in iOS 5, in iPad simulator.

Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'A view can only be associated with at most one view controller at a time! this is the error, view.view is the view i want to load in the splitViewController, there is no other code, i do only the change of the view in the split, i used removefromparentviewcontroller because i want to remove the association to the viewController to remove the exception
Is your splitViewController an instance of UISplitViewController?
http://developer.apple.com/library/ios/#documentation/uikit/reference/UISplitViewController_class/Reference/Reference.html
If so, you're doing something fundamentally wrong. The UISplitViewController is simply a container for two view controllers, a master view controller and a detail view controller. It is then up to your view controller to decide how to deal with interactions and such. removeFromParentViewController is only used in View Controller Containment, and as such doesn't seem applicable here.
Read the documentation in the link above to see if it meets your needs, but if I understand, you need to set your view controllers in the split view, not your views.
To do you, you need to do:
// Assume this is initialised correctly
UISplitViewController* splitViewController = [UISplitViewController alloc] init...];
// Assign the two view controller you want to be used in the split view controller
splitViewController.viewControllers = #[masterViewController, detailViewController];
If this isn't applicable, then please provide your code so that a correct solution can be made. Your descriptions have been rather vague as to what you're actually doing.

Related

Does dismissViewControllerAnimated remove the controller's instance

For my app, I want to have a few different instances of the same view controller. For now, I am just creating a new instance like this:
iSafeViewController *tab = [[iSafeViewController alloc] init];
[tab setModalPresentationStyle:UIModalPresentationFullScreen];
[tab setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentViewController:tab animated:YES completion:nil];
Great. And since this is done in the iSafeViewController class anyway, I have another button that currently just dismisses the latest controller on the stack.
- (IBAction)closeTab:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
Okay, however, I really want to be able to go back to these instances. So, I have two questions.
Does dismissViewControllerAnimated remove that controller's instance from memory. If not, is there a way I can re-present it.
There is probably a better way to navigate through viewController instances then presentViewControllerAnimated. At the very least, is there a better way to create new instances of one's viewController and be able to navigate to each of them, hopefully not in a stack. In other words, if there are three viewController instances, is there a way I can go from the third to the main one?
Any ideas would be appreciated. Thanks.
"Does dismissViewControllerAnimated remove that controller's instance from memory? If not, is there a way I can re-present it."
Calling dismissViewControllerAnimated does not explicitly remove a view controller from memory, but if no other part of the code is storing a strong reference to the view controller, once the presenting view controller dismisses your VC, it may be deallocated as per the normal memory management system.
So if you ensure something in your code has a reference to your view controller (aside from the VC which is presenting it modally), it won't disappear after being dismissed, and yes this means you can re-use it.
As for "random access" to view controllers: you could use UINavigationController and use methods like popToViewController:animated: and multiple calls to pushViewController:animated: (without animation!) to create the effect of travelling to arbitrary view controllers. This feels like a bit of a hack.
Alternatively, and preferably, you could write your own custom container view controller. This is a view controller that deals with presenting other view controllers. See Apple docs.
Here's a good WWDC video on the subject: Implementing UIViewController Containment
Further reading:
Container View Controller Examples
http://subjective-objective-c.blogspot.co.uk/2011/08/writing-high-quality-view-controller.html
Custom container view controller

Get UIViewController to reload different UIView programmatically

I am overriding the loadView method within a UIViewController as follows:
-(void)loadView
{
NSLog(#"HPSFormController loadView starting");
HPSFormView* viewForThisController = [ [ HPSFormView alloc ] initWithFrame:_frame ] ;
self.view = viewForThisController;
}
When a certain button is pressed within the view then the same UIViewController gets control again and at this point I wish to completely change the view that the controller is showing. As follows:
-(void)buttonTapped
{
ABCFormView* newview = [ [ ABCFormView alloc ] initWithFrame:_frame ] ;
self.view = newview;
}
However, the buttonTapped method does not load the second view. A completely blank view is shown instead.
How do I get the view controller to reload a completely different view when the button is pressed?
However, the buttonTapped method does not load the second view. A completely blank view is shown instead.
Is it not possible that the problem is in the way you create ABCFormView? I mean, it seems that the original view is replaced by an empty view, so check how the latter is created...
EDIT AFTER YOUR COMMENT:
if you say that the view is "created within a viewDidLoad method within the view controller", then you should instantiate your view controller:
#property (...) ABCFormViewController* newviewController;
....
-(void)buttonTapped
{
self.newviewController = [ [ ABCFormViewController alloc ] init] ;
self.view = newviewController.view;
}
keep in mind that newviewController must be around as long as you are using its controlled view, otherwise you will get a crash. That is the reason why I store its reference in a property.
Obviously you can't get your new view visible by simply setting self.view = newView; because the newView has never been added as a subview to any other views yet - i.e. not in the window.
If you need to switch to a different view, you should probably add APSFormView as a subview to your viewcontroller.view, and when you need to switch, remove APSFormView from superview then add ABCFormView as a new subview to viewcontroller.view.
If your loadView implementation needn't do much else, it may be better to use the storyboard to set it up initially. It is easy to miss, but you can specify in the storyboard that the view should be of a custom type (in the "identity inspector" with the view selected). Further, it may be worth evaluating why a completely different class of view is necessary for the same view controller instance; to me this may be a red flag regarding the application design. You may be better served by a flow between two view controllers, or else write some state-changing logic in this custom UIView-extending class. The decision for me would be made based on the model being represented by the views, along with which behaviors each is designed to facilitate.
If the models are different (i.e. your first view shows a list of accounts, second shows one account detail), or if the behaviors are significantly different (i.e. the first is viewing an account and the second is creating a new one), then I would use two distinct view controllers.
If the models and behaviors are similar, and the style should change, then I would likely write state-changing code in the custom view class to rearrange things, etc.
If you are coming from a different platform, it can seem silly at first, but we really do throw around view controllers without much hesitation. They are elegantly handled by the framework, and are designed to manage "a screenful of content" and be easily swapped for another screenful.
It is hard to tell without knowing what is inside ABCFormViewController. I had some timing issues once on a view controller which I just needed to generate the view because I wanted to capture its content to create a pdf file (its a view that never gets displayed onscreen). In that case I needed to insert a code like this:
[newviewcontroller.view setneedsrefresh];
Before I do
otherVC.view = newviewcontroller.view;
Otherwise I get a blank page.
I believe I get this because by the time everything is sorted out ARC deallocates newviewcontroller so the view is nil. In your case this may not be the problem. Is there a reason why you need a 2nd and 3rd view controller to put into your view because a much simpler way of doing this is to just transfer control to the other view controllers via modal, pop view or a navigationController. Another more usual way is to create multiple views in your XIB and then just load it into a blank view instead of creating view controllers for each of them.

SIGABRT error when setting Tab Bar View Controller to custom view controller

I have been developing an iPhone application using the Detail View template. At some point I realized that I wanted my application to have a Tab Bar View controller as the root view controller. It seems that you do not want to push a tab bar view controller onto a navigation view controller. So I decided to start a new tab bar project and migrate my existing application code over. I thought this would be easiest.
So I have done that and the project builds without a problem. But I cannot seem to reassign the tab bar view controllers to ones which I already have. So I am replacing, "FirstViewController" with one of my custom ones. When I attempt to run the application it calls:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
And bombs with a SIGABRT error. I'm not sure why this is. I cannot see what the default view controller (firstviewcontroller) is implementing that my custom view controller is not.
Can anyone give me some ideas of what I should check?
Thanks!
update
It seems that my problem is related to the fact that my custom viewcontroller is of type UITableViewController. If I change this to be of type UIViewController it seems to work.
Update
I added a Navigation controller to the storyboard and created a relationship from it to a blank TableViewController. Then I assigned this TableViewController to my custom UITableViewController class. This seems to work. I'm not sure what plumbing was missing before.

UIViewController within a UIViewController

So I have a viewControllerA, and I want to add another View managed by viewControllerB to it. There is only one UISlider activating a simple action in viewControllerB. It won't crash if I don't touch this UISlider, it will once I use UISlider. I am using ARC. I am using:
[self.view addSubView: viewControllerB.view];
to add viewControllerB to viewControllerA. Am I missing something? Thanks.
OK. It looks like a really simple situation. I just added one view controller and one action. Here is the demo project code on github: https://github.com/randomor/Demo
The reason why I want this to work is because I have another app that will create a view controller on the spot and add it to anther view. And I don't want to do it modally, because I don't want the new view controller to cover the whole screen. Thanks.
SOLUTION: So I'm now just using the latest ViewController containment API:
[self addChildViewController:viewControllerB];
It works! as long as I added this line, the event will be passed to its own controller and it stopped crashing.
i recommend you, to use the following code
in ViewControllerA.h
#import "ViewControllerB.h"
in ViewControllerA.m (where you want to push the new controller)
ViewControllerB *newController = [[ViewControllerB alloc]init];
[self presentModalViewController:newController animated:YES];
in ViewControllerB.m you will need
[self.presentingViewController dismissModalViewControllerAnimated:YES];
to make it vanish again.
concerning multiple controllers for one open screen (Apple ViewController Programming Guide):
Each custom view controller object you create is responsible for managing exactly
one screen’s worth of content. The one-to-one correspondence between a view controller
and a screen is a very important consideration in the design of your application.
You should not use multiple custom view controllers to manage different portions
of the same screen. Similarly, you should not use a single custom view controller
object to manage multiple screens worth of content.
You should try and avoid the practice of nesting UIViewControllers. While it is technically supported in iOS5, it is ill-advised, for many reasons, including the type of problem that you're having (you have a dangling pointer to a UIViewController, which is why you are crashing).
http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/
Although this question is extremely vague, I imagine that you are not keeping a reference to View Controller B, and so when view B tries to interact with it, it causes EXC_BAD_ACCESS.
What's the object that is set as the target for the slider? If it's a EXC_BAD_ADDRESS, then you may not be retaining the target, most probably the view controller for the slider.

iOS 5 storyboard, programmatically determine path

I'm having trouble to achieve the following using a storyboard:
When setup is not done:
run app -> show settings view controller -> show main navigation controller
When setup is done:
run app -> show main navigation controller
So basically, I want the app to programmatically start with the settings view in certain cases, and otherwise skip right ahead to the main navigation controller.
I did manage to show the settings view with a modal style segue from the main navigation controller, but I don't know how to display it before the main navigation controller is displayed. Any ideas?
By default, the initial view controller from your main storyboard is instantiated and displayed automatically when your app starts up. To prevent this happening you need to remove the UIMainStoryboardFile setting from your info.plist file.
With no default view controller, you are now free to create one programmatically at app startup. See the UIStoryboard documentation. Use +storyboardWithName:bundle: to load the storyboard and then use –instantiateViewControllerWithIdentifier: to create the correct view controller. You will also need to create a main UIWindow and add the view controller's view to it just like you used to do with .nib based UI. Note that without the UIMainStoryboardFile setting a main window is not created for you - read the explanation.
I managed to do it a bit different:
Use a UINavigationController as the initial view controller.
Create a root view controller that will manage the decision of what to load.
Create a Storyboard Segues from the root view controller to the main view and to settings view, and give the segues proper identifiers.
Call the performSegueWithIdentifier with the proper identifier from your root view controller.
Just another solution, hope this helps.
I did something similar to amoshaviv, his advice is sound. I did it slightly different though, and I'll give some more info.
I created a custom MyInitialViewController class, derived from UIViewController, and made this the initial view controller.
In the storyboard file, I created modal segues with appropriate names to all (in my case three) possible 'real' first view controllers.
In the MyInitialViewController class, I implemented the
- (void)viewDidAppear:(BOOL)animated;
method, to first perform the check which view to switch to, and then do the correct
[self performSegueWithIdentifier:#"NameOfSegue" sender:self];
Effectively, this makes the MyInitialViewController nothing more than a switch performed when it's brought into view. I first tried doing this when loaded because I don't care for actually seeing this view, but that did not work, while viewDidAppear does.
To make this visually smooth, I tried the following. In the properties of the segues, I disabled animation. The view I left empty, and I gave it a background color matching to that of the startup image.