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
Related
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.
I have AViewController. Inside A, I have a MapViewController instance. MapViewController is A's Child View Controller.
Now when a button inside A got TouchUpInside, I init a BViewController. and present B.
When init BViewController I pass in the MapViewController to B and stored inside B as an unowned variable.
I need to display the same map for both A and B. Inside B's viewDidLoad, I add MapViewController as Child View Controller for B.
The app freezes when I press the button. No exception, No crash, No error log.
Anyone have any ideas why it happens?
My Attempts:
I tried to use a navigation controller and use pushViewController methods, same result, app freezes
I tried to add MapViewController as child inside B at ViewWillAppear, same result, app freezes
I tried to add MapViewController as child inside B at ViewDidAppear, no more freeze, but map is not there at all.
Instead of present B I tried to add B as childViewController of A, same result, app freezes
I checked all the instructions on running on main threads.
I tried to remove mapViewController as child in A before present B, same result, app freezes.
I tried to add mapViewController as child in completion block when present B, same result, app freezes
This may be a better structure to take. When you switch from A content to B content, you can move / size the elements all you want... And since they will be separate subviews of your "main" view, they can overlap if your interface would benefit from that layout:
I have 3 view controllers with the NSUserDefault bool values for detecting first launch and calling the tutorial func. But, in one of view controllers, it doesn't work as it should:
I have a custom animated segue with VCs
main vc -> img vc
img vc exit -> main vc
main vc -> sett vc
sett vc exit -> main vc
I tried to write println in the viewDidLoad in main vc and I find out that the viewDidLoad method works only when the app opens, all other time when I come back to main vc it doesn't work. (in two other vcs it works every time when I segue to them)
I have no idea how to correct it.
Your question is hard to understand. I try an answer here, and if it doesn't satisfy your needs, please provide more code.
I believe you mix up a didLoad and a didAppear delegate method.
If you want to trigger code every time a view will be shown to the user, use the viewDidAppear delegate-method.
viewDidLoad is called only once, when the view is loaded to memory. If you use a navigationcontroller and segue back to the view (which is already loaded in memory), viewDidLoad will NOT be called again. But viewDidAppear will
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.