UIPopoverPresentationController isVisible equivalent? - ios

UIPopoverController has a .isVisible property to tell if the popover is on the screen or not.
Is there an equivalent for the new UIPopoverPresentationController?

It's a UIPresentationController, so it has a presentedView, so you can ask whether that presentedView has a non-nil window.
But the real answer is that you are looking completely in the wrong place. UIPopoverPresentationController is nothing like the old UIPopoverController; they have almost nothing in common and they are not managed in similar ways. A popover is now just a presented view controller. Concentrate on the view controller. You just dismiss it, like any presented view controller (self.dismissViewController...). This is the whole point. You no longer retain a reference to anything; you no longer have to manage anything; it's just like presenting and dismissing any modal dialog / view.

Related

Make presented view controller to ignore and pass through touch events

edit: I want to make this view independent to be reusable.
The question explains itself. I have searched, but didn't find any answers, even if the question seems to be common.
I have two view controllers VC1 and VC2. My code is like:
VC1.present(VC2, animated: true)
Then, I need my VC2 to totally ignore all touches and pass them through.
Good example is AppStore rating success alert (attached). You can scroll even when alert is presented, I need the same behavior.
You should use an UIView and just overlay it over your VC1. Then the VC1 stays scrollable even when the UIView is presented

Creation of UIAlertController on ViewWillAppear gives warning

I was trying to create a UIAlertController on ViewWillAppear, it was giving me the below warning.
"Warning: Attempt to present <UIAlertController: 0x7f8798c15df0> on <ViewController: 0x7f8798f81450> whose view is not in the window hierarchy!".
My Understanding it is ready to show the view to the user in ViewWillAppear, but fairly expensive.
However when i moved the same UIAlertController code to ViewDidAppear it was showing the Alert Msg.
Can you please clarify whey the alert msg is not getting show in ViewWillAppear.
UIAlertController is unlike the typical UIAlertView, which inherited from UIView. Adding a UIView in viewDidLoad, appear, etc. is no problem because you are adding it that view controller's view hierarchy.
Now, with UIAlertController, it's a first class view controller. This means that you should present it the same way you would other view controllers - all the same rules apply.
In viewWillAppear, the view controller isn't done being added to the window's hierarchy, so it's a poor choice to present the alert controller. As you found, viewDidAppear is the way to go here.
viewWillAppear: is not a good location to present another view controller since the current one being presented is not yet in the window hierarchy and is also in a transition animation. You should either use viewDidAppear: or add a slight delay before displaying the alert controller.
Most answers on here cover the topic pretty well, I just wanted to add additional details. It typically is not problematic to add child view controllers in viewWillAppear: or even viewDidLoad:. In fact if you use a container view controller in IB then it will be integrated before either of these are called.
The problem is more so to do specifically with the alert controller. Alert controllers function on a different window hierarchy than your typical UI (think Z axis) and manipulate the UI in a more intensive way than a standard container (since it is not a container).
In order for the alert to properly present it must happen after the current view is displayed (there could be a number of reasons here). This is why presenting it in viewDidAppear: worked (since the current vc's view will be fully live).
The same error may occur if parameters of UIAlertController are nil, such as title, message

iOS Showing views conditionally

I have a view in which a user selects an action to take and on that next screen there is a save and a back button. For both of the buttons the last line is dismissViewControllerAnimated:.
I need a way to make the 1st screen show only if the back button is used. save should send back to the main screen/rootViewController I am fairly new to iOS but not programming in general and just need a nudge in the right direction.
Could I set a bool flag to show or not? Maybe I can set the Tag on the view and then check that in the other screens on save/back? I assume I can check the parent view.
Sorry if this is a dup but I cant find anything specifically for this.
EDIT: I am not using a nav controller and am showing the views modally.
The answer will vary depending on how your UIViewControllers are structured and setup. If you're using a uinavigationcontroller then you can POP to the root view controller using:
[self.navigationController popViewControllerAnimated:YES];
If you're presenting your UIViewControllers modally, you can try to dismiss the presenting View Controllers of your modal view controller using the presentingViewController property:
[[[self presentingViewController] presentingViewController] dismissViewControllerAnimated:YES completion:nil];
You may also want to take a look at Unwind Segues if you're using a Storyboard:
What are Unwind segues for and how do you use them?
Finally, as far as determining whether the back button is pressed or another button - that depends on how the app is setup. You'll need to use your own logic (probably if / then statements or case / switch) to determine which button was pressed. You also may want to check out the sender argument in IBActions.
John, to have a UINavigationViewController return to it's root viewcontroller, you use:
[nameOfNavController popToRootViewControllerAnimated:YES];
The other guys are correct that the information you've provided is definitely not enough to determine exactly what you need to do.
You can use the presentingViewController property of a modal view controller to access it's presenting controller.
It turns out that I was using the terminology wrong. I am presenting all views modally and that is the issue, there is no navigation controller. I ended up using NSNotification to build a listener and had the main view controller listen and then dismiss the view and hence show itself. Worked a treat.
here is the link to the code I ended up with.
http://iphonedevsdk.com/discussion/114737/view-heirarchy-issues-possibly-from-the-camera
Hopefully this helps someone else.

How do Modal and Child View Controllers interact?

I have a custom container view controller: ContainerVC. Its job is to present one of two content view controllers: ContentPortraitVC or ContentLandscapeVC, depending on the current orientation (though it doesn't matter why the container chooses its view, I presume). ContentPortraitVC, at some point pops up ContentModalDetailVC.
So there are two different methods of displaying new content at work here:
the parent-and-child relationship (instigated via addChildViewController and removed via removeFromParentViewController),
the presenting-and-presented relationship (instigated via presentViewController and removed via dismissViewController).
If the ContainerVC adds the ContentPortraitVC, which then presents the ContentModalDetailVC, and then the ContainerVC decides to switch to the ContentLandscapeVC, the ContentModalDetailVC stays visible (why is it not removed when its parent is removed?)
But then, when the ContentPortraitVC is asked to remove the ContentModalDetailVC, nothing happens. The modal display stays put. What is going on?
When you use addChildViewController to add the ContentPortraitVC:
a. The ContentPortraitVC gets its parentViewController property set.
b. You then (as per the Apple documentation) have to manually display the ContentPortraitVC's view. If you follow the documentation you do this by adding it as a child of the ControllerVC's top level view.
The ContentPortraitVC then calls presentViewController to display ContentModalDetailVC.
a. This sets its presentingViewController property (in the debugger this is shown as the _parentModalViewController ivar -- note the ivar is different from the property), and sets the presentedModalViewController property of the ContentPortraitVC (who's ivar is _childModalViewcontroller).
b. Views wise, on iPhone, the ContentModalDetailVC's view will completely replace the views from ContentPortraitVC and ContainerVC, so only the modal view controller's view will be visible. (on iPad, it layers the new UI over the top, but as a sibling of the ControllerVC's view, which in turn is the parent of ContentPortraitVC's view).
So now, you transition from ContentPortraitVC to ContentLandscapeVC.
a. IOS does a bit of magic. It knows that the thing you are removing (ContentPortraitVC) has a presentedViewController currently active, so it changes its parent. It sets the value to nil on ContentPortraitVC, takes the child (the ContentModalDetailVC) and sets its parent to the new view (ContentLandscapeVC). So now the view controller that presented the modal view is no longer its presenting view controller. It is as if ContentLandscapeVC presented it in the first place!
b. In terms of views, you follow the Apple docs to change over the view from ContentPortraitVC to ContentLandscapeVC. But you are simply changing the subviews of ControllerVC's view. On iPhone, the modal view controller is still the only thing being displayed, so making the change doesn't do anything on screen. On iPad, it does (though you probably won't see it, as the modal view is usually full screen).
Now you come to dismiss the modal view. Presumably you do this in ContentPortraitVC, but it no longer has any reference to the thing it presented. So calling [self dismissViewController... does nothing, because ContentPortraitVC is no longer presenting anything, responsibility for that has been passed on to ContentLandscapeVC.
So that's what happens and why. Here's what to do about it.
You can rewire the delegate manually when you change from ContentPortraitVC to ContentLandscapeVC, so the latter is the one that tries to dismiss the modal controller.
You can have the modal controller dismiss itself with [self dismissModalControllerAnimated:YES completion:nil]. I'm going to ask and answer another question on why that works (how does IOS know which to dismiss?), if that seems strange.
You can have the ControllerVC be the one that pops up the modal view and be responsible for removing it.
If you inspect presentingViewController on ContentModalDetailVC, you will see that it is actually presented by ContainerVC and not ContentPortraitVC.
To fix this, you just need to set definesPresentationContext (or use the "Defines Context" checkbox in Interface Builder) on ContentPortraitVC.
This will tell ContentPortraitVC to handle the modal presentation instead of passing up the responder chain to the next view controller that defines presentation context (your root view controller by default).
You will probably want ContentLandscapeVC to define context as well to avoid the same issue.
With both child controllers defining their own presentation context, when ContainerVC decides to swap children, any modal modal will be removed from the new hierarchy along with the child that presented it. No need to do hacky things to try to dismiss before swapping :)
Edit: I should add that the view controller being presented must have its modalPresentationStyle Set to either currentContext or overCurrentContext,

deallocated controller called after dismissModalViewControllerAnimated

I have a modal view which is launched from the current view controller, as
[self presentModalViewCOntroller:modalViewController animated:TRUE];
The modal view controller dismisses itself when someone hits a button.
[self dismissModalViewControllerAnimated:TRUE];
A couple of screens later, I attempt to swap the root view within the window. I do this all the time with no trouble. But in a certain case, when switching the one view within the window, the picker delegate method is being called on the modal view controller even thought it was dismissed a while ago.
This is very strange because the modal view controller is usually deallocated when dismissModalViewController is called.
Why is a view from the modal view controller being invoked?
It appears that someone, probably the window still has a reference. Are you supposed to do something else in addition to dismissModalViewController?
Thanks
DismissModalViewController should be enough. It does seem like you have a problem with some reference hanging around that you don't intend. Without seeing more code, I can't point to anything specific.

Resources