So I have view controller A, B, and C.
From A, I present VC B modally over the current context.
There is a button in VC B from where I present VC C (using segue)
Now on dismissing Viewcontroller C, I am trying to dismiss VC B and from VC A, run some function which again presents VC B based on some logic.
But in this case, I get this error "Attempt to present UIViewController on UIViewController whose view is not in the window hierarchy"
I have tried to dismiss with a completion handler, dispatchQueue(after:)
but those doesn't work.
On the contrary, I have a UIButton attached in VC A, which runs the same logic for presenting VC B and it is running correctly and not showing any error.
What am I missing in this scenario?
I hope these two solutions help you.
I had the same problem these two solutions helped me with this issue.
First:
Post notification in VC C viewDidDisappear or in your button click method and add its receiver on VC B which you want to close it after closing VC C.
Second:
You can create a protocol While clicking the button call your protocol method and pass your data to your viewController.
Related
I have viewController A
It will present viewController B
I will be layering viewController C over viewController B (tutorial layover)
In order for me to point an arrow image on vc C to an element on vc B I need to know where that element is.
When I try to lay vc C over B to prepare for the animation from vc A to vc B, vc B is not yet laid out because it has not yet been presented. Therefor vc C can't properly place it's arrow image pointing to the element on vc B.
view.setNeedsLayout() and view.layoutIfNeeded() do not work as this only triggers viewDidLoad and viewDidLayout subviews not viewDidAppear. I've tested this and the element I am looking to point to (bar button item) is still not laid out after calling these.
Is there a way to completely layout a viewcontroller in the background before it is presented? I believe this would solve my problem.
update
I want to force draw/layout a view controller
maybe with tbVC.view.setNeedsLayout() and tbVC.view.layoutIfNeeded() ?
Then I want a completion block telling me when this is done.
Then I want to execute some code which accesses the drawn elements (uibuttonbaritem) within completion block.
And I want all of this to be done in the background without presenting the view controller.
Cheers!
To solve this issue you need ViewController C as subview of ViewController B and you need to call ViewController B from ViewContrller A
self.view.addSubView(vcC.view) //instead of vcB use your ViewControler C instance
Hope this will help you
Given:
ViewController A that presents ViewController B
ViewController B has no reference to ViewController A (except implicitly the presentingViewController property)
ViewController B calls dismiss on itself and does nothing else
What I want to achieve:
ViewController A wants to know when ViewController B got dismissed in order to clean up some state
Restrictions:
I do not want to use KVO
I do not want to modify ViewController B or its behavior in any way
What I have found out so far:
dismiss(animated:completion:) according to the documentation forwards the call to its presentingViewController. But as it seems dismiss(animated:completion:) is not called, but rather a private method _performCoordinatedPresentOrDismiss:animated:.
iOS documentation on presentingViewController is misleading. It says "the view controller that was presented has this property set to the view controller that presented it", but that's not true. In iOS 11, this will always point to the root parent VC of the VC that present was called on. Similarly the documentation on presentedViewController is misleading. It says "the view controller that called the method has this property set to the view controller that it presented", that's not the whole story. Every VC in the hierarchy of the VC (all its parent VCs and child VCs) that called present will point to the same presentedViewController.
In your Controller A , kame it as UINavigationControllerDelegate and with navigationController:didShowViewController mark the presentation of Controller B (isControllerBisPresented = true). When viewDidAppear of B , check if isControllerBisPresented is true.
An ugly workaround would be to use a man-in-the-middle which does something in deinit. So A presents M which embeds B as a childVC. When B dismisses itself, M will be dismissed implicitly as well, so it's deinit method should be called. There it can notify A that it was dismissed.
This is fragile, as some reference cycle could prevent M from being deallocated which would result in A not getting notified. So I'd rather want to find a better solution.
I'm presenting UIViewController B modally from UIViewController A. Then, there is a UINavigationController between B and C, and C is flipped from B. I want to dismiss both B and C and animate straight to A, without showing B and hence without calling B's viewDidLoad function. I'm calling dismissViewControllerAnimated from A, but that still shows B first and hence calls B's viewDidLoad function. Forgive me if there is already a SO answer on this; I've been snooping around for hours and just can't find an answer. How can I solve this?
Any Parent ViewController that is presented modally can be dismissed using:
[self.parentViewController dismissModalViewControllerAnimated:YES];
Another way to do it would be using:
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
Let me know if it doesn't works.
Here is my storyboard configuration:
Navigation Controller -> View Controller A -> Push-> View Controller B
^
|
Modal
^
|
View Controller C
What I want to achieve: When a button is pressed in View C, directly View B will be opened modally (No part of View A is to be displayed). Also, View B will have a navigation back button to View A.
To achieve this,
I set up the illustrated storyboard.
I created a segue between View C and the Navigation Controller of View A/B.
In the 'prepareForSegue' method of View Controller C, I get an instance of View Controller A as the first element in the navigation. In this instance, I set a variable like 'directlyProceedToViewB=YES'.
In the viewDidLoad method of View Controller A, I check the variable 'directlyProceedToViewB' and if it is YES, I call 'performSegueWithIdentifier' to segue to View B
The result is so that, first View A is opened modally and after displaying it a very short time, View B is opened with a push animation (View B can navigate back to View A, which is good). But I do not want View A to be displayed any time at all. How can I achieve this?
EDIT:
To better visualize, I'm adding a screenshot with more example cases to support:
Here are some cases I want to support:
We can start with ViewC, click on 'Modally Display B' which opens ViewB, then click 'Back to A' to navigate back to ViewA, then click on 'Dismiss Modal' on ViewA to go back to ViewC
We can start with ViewD, clcik on 'Modally Display A' which opens ViewA, then click on 'PushB' to open ViewB, then go back and forth between A and B and modally dismiss to ViewD.
First of all, some corrections: those are not views but view controllers. And "view A" is not pushed into the UINavigationController but it's the root.
After that, I suggest making the segue in "view C" an unwind segue and implement the IBAction in "view A" by pushing "view B" with [[self navigationController] pushViewController:bViewController animated:NO].
EDIT (adding some details):
I assume that in ViewControllerA's viewWillAppear you present ViewControllerC in a not animated manner.
Implement an unwinding action like (IBAction)unwindAndThenGoToB:(UIStoryboardSegue *)segue in ViewControllerA.
In the storyboard connect the button in ViewControllerC to the Exit icon and select the previously defined method.
Then implement the method with the push call I wrote earlier.
ps: for documentation there is plenty on Apple's website.
Implement this using delegates.Decalre protocol in which class you want and define those methods and call the methods in the view controller you want.There is no many ways of calling some view and showing back button to go different view.modal view is just a concept.and you can use delegate methods to call whatever class you want.
Here I got a way to do so:-
You need to set no animation for segue from viewC to viewA as shown in below image. Then set a segue identifier for segue from viewA to viewB namely, "viewB" and in your viewA .m file add following code,
- (void)viewDidLoad {
[super viewDidLoad];
// Place your conditional check here.
[self performSegueWithIdentifier:#"viewB" sender:self]; //Will directly lead to viewB and viewA won't be shown as no animation is there from viewC to viewA.
}
And your rest flow be like-wise.
I found the solution myself.
First, I discovered that, my original proposal of
In the viewDidLoad method of View Controller A, I check the variable
'directlyProceedToViewB' and if it is YES, I call
'performSegueWithIdentifier' to segue to View B
works as I desired on iOS 7 but does not work on iOS 8.
So the solution is, in the viewDidLoad method of View Controller A, if 'directlyProceedToViewB' is YES, rather than calling performSegueWithIdentifier, use the following code:
ViewControllerB *destVC = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControllerBStoryboardID"];
[self.navigationController pushViewController:destVC animated:NO];
I'm using a UISegue in my application to automatically transition between viewcontrollers in my application. I'll call them A and B.
It worked as expected, however when I wanted to pop-back to the A from B, I attempted to call
[self.navigationController popViewController] however the B's navigationController property reports null.
As a second attempt I attempted to map a button, to a UISegue back to view controller A.
However this just creates a new ViewController.
As a work around, I ended up doing as a work around was to retrieve the B viewcontroller from the UIStoryboard and calling [A.navigationController pushViewController:B]
At which point, calling [B.navigationController popViewController] worked as expected.
This seems wrong, from a storyboard segue how can I return to the previous view controller?
I don't know about the class of your A and B controllers (whether UIViewController, UITableViewController or UINavigationController), but if you follow the following pattern, it should work.
In an empty storyboard, insert a UINavigationController. This will bring in two windows to the storyboard, linked with an arrow (a segue). The one on the right should be controller A.
In A, let's say, you add a button. The button will push B into the navigation stack.
Then, you add the second controller B, and drag from the button in controller A to controller B, and choose "push" from the popped menu.
If you only use a UIViewController (A) and push B, there is no navigationController to take care of popping.
Hope that help.