There is only 1 method about dissmissing the popover in UIPopoverPresentationControllerDelegate, which named "popoverPresentationControllerDidDismissPopover".
I am making some UI update in this method. However,it would spend a little time to dismissing the popover.
Is there any way to make my UI updates before the popover is get dismissed?
Try this delegate method,
func popoverPresentationControllerShouldDismissPopover(popoverPresentationController: UIPopoverPresentationController) -> Bool
and return YES by default, this will be called before the pop over gets dismissed, I mean before the animation happens.
Related
I have a View and a popover that appears on top of it, which alters data. I am trying to update the view/run a function on the main view (that is under the popover) once the popover is dismissed. I have tried numerous things including viewwillappear, but it isn't being registered as technically the view doesn't disappear since the popover is just above (And you can see part of the view from behind). If anyone can suggest how to call a function on the parent view when dismissing the popover (without crashing the app, as most of my attempts have), I would be very grateful! Thanks.
Update: I am attempting to do this with a modally presented vc now, and have attempted to use protocol callbacks but to no avail. Below is the code.
protocol MainVCDelegate: class {
func pushIt()
}
in the modally pushed view:
weak var delegate: MainVCDelegate?
#IBAction func changePartnerButton(_ sender: Any) {
delegate?.pushIt()
dismiss(animated: false)
}
in the main VC I implement the protocol and create the function to be run, but nothing happens.
In iOS 13 and iOS 14, you set yourself as the popover's presentation controller's delegate and implement presentationControllerDidDismiss. In iOS 12 and before, you set yourself as the popover's popover presentation controller's delegate and implement popoverPresentationControllerDidDismissPopover.
In my case, I am using two view controller VC1 and VC2. Here, VC1 button click to Present Modally and Over Full Screen presentation with Cross Dissolve Transition to presenting VC2. Now, from VC2 dismiss then I didn’t get call VC1 viewWillAppear().
I am not using code base for Present model. I am using Storyboard Segue.
Why it happening and how to fix this?
From Docs,
Note
If a view controller is presented by a view controller inside of a
popover, this method is not invoked on the presenting view controller
after the presented controller is dismissed.
So according to the documentation when a ViewController presents another ViewController modally this method will not be called. To fix this you need to use
func dismiss(animated flag: Bool,
completion: (() -> Void)? = nil)
and move(or repeat) some of viewWillLoad logic to completion handler.
Change presentation to Full screen or If you want to stick to Over Full Screen then make vc2 delegate of vc1 and call delegate method on dismiss.
To understand the concept you can refer to : https://medium.com/livefront/why-isnt-viewwillappear-getting-called-d02417b00396
Issue:
An UITabBarController delegate method calls viewDidLoad and negates/ignores the screen selection.
Background:
I am making a split-view controller for small iOS screens. So, far the screen looks something like this:
Custom Split-View Controller
For the Detail View (RS), I'm using a UITabBarController, with the TabBar removed. You can find the code for this here.
The user selects the buttons on left, and a delegate sends the button's tag id to the UITabBarController delegate method.
The delegate method looks like this:
// delegate method in subclass of UITabBarController
-(void) screenSelected:(int)screenNum
{
NSLog(#"delegate arrived: %d", screenNum);
self.selectedIndex = screenNum;
// code goes to viewDidLoad
}
With breakpoints in place, I determined that the screen does not actually change.
If this screenSelected method is instead called from the UITabBarController viewDidLoad method (when the app first opens), the method works fine and the selectedIndex is changed as expected.
I'm trying to figure out why the delegate is triggering viewDidLoad. Does the delegate reset the view?
Thanks
Yes, viewdidload will call every time because you are initializing tabs again. setSelectedIndex intialize tabbarcontroller evrytime from UITabbarcontroller class. You should not do that from uitabbarcontroller class. just initialize tabbarcontroller onece.
You should not use tabbarcontroller like this way. according to your requirement you not need to use tabbar just shows viewController on button click.
hope this will work.
I'm implementing my own 'back' button. Where onClick, the following code is executed in the ViewController (VC) being dismissed:
Dismiss current VC (VC#1)
Pop current VC (VC#1) off my custom navigationStack
Get the last VC (VC#2) from the navigationStack, and present it using
presentViewController
What happens is the back works visually works - i.e. current VC disappears, previous VC appears. However, the viewDidLoad method is not called. So the screen isn't updated with data updates from viewDidLoad.
[self dismissCurrentViewController:self completion:^{
[TWStatus dismiss];
FHBaseViewController *vcToDisplay = [[FHDataManager sharedInstance] popNavigationStack];
[vcToDisplay.homeVC presentViewController:vcToDisplay animated:NO completion: ^{ }];
}];
Questions:
I was under the impression that viewDidLoad always gets called when presentViuewController is used??
I 'build' the screen using a method called ONLY from viewDidLoad in VC#2. How is iOS displaying the screen without coming into viewDidLoad?
btw, I'm not using storyboards. Any help is appreciated!
My guess is that viewWillAppear is being called but viewDidLoad is not, at least not when you expect it is. viewDidLoad should be called once, but depending on how you're managing the view controllers, viewDidLoad may not be triggered every time your view appears (which happens after loading).
The completion handler is called after the viewDidAppear: method is called on the presented view controller. from presentViewController doc
so put this in your code with a breakpoint on the call to super and verify it is getting called when this transition occurs.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
edit: since you verified that viewWillAppear is getting called, then I would say that it's coming down to how you are managing the view controller life cycle. Even with a standard UINavigationController, viewDidLoad is not called when a view is shown as a result of popping items on the navigation stack. I would move your logic to viewWillAppear if you are dead set on not using UINavigationController
When I make a back button pragmatically I use:
[self.navigationController popViewControllerAnimated:YES];
This will invoke the viewDidLoad method. Use that instead of your current code.
When a user taps outside the popover, the dismissal is animated. Is there a way to set that dismissal animation to NO? I have googled and searched on Stack extensively.
The docs for UIPopover state:
When displayed, taps outside of the popover window cause the popover
to be dismissed automatically. To allow the user to interact with the
specified views and not dismiss the popover, you can assign one or
more views to the passthroughViews property. Taps inside the popover
window do not automatically cause the popover to be dismissed. Your
view and view controller code must handle actions and events inside
the popover explicitly and call the dismissPopoverAnimated: method as
needed.
I have implemented the dismissPopoverAnimated: method with NO and that works great for all the cases when I call that method.
The problem is when a user taps outside the popover to dismiss, dismissPopoverAnimated: is not called.
taps outside of the popover window cause the popover
to be dismissed automatically.
And that dismissal is animated. There seems to be no way to control that dismissal. I am using the popover to present a color picker for a drawing app. Taps to draw are not registered until the popover has finished animating out. This creates a noticeable delay as you are not able to draw immediately but must wait for the animation to complete.
I thought that - (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController could work but there is no way AFAIK to set the animation property in this method. Just return yes or no.
Is there a different method I can implement to be able to set the animation to NO?
In the view controller that presents your UIPopoverController, conform to the UIPopoverControllerDelegate protocol and implement the following delegate method. I just tested this and it does dismiss the popover without animation.
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
[self.myPopoverController dismissPopoverAnimated:NO];
return YES;
}
Just make sure that you have set the delegate of your popover controller to the view controller that implements this.
Swift 5
This will disable the animation, when we close the popOver by tapping outside.
extension YourViewController: UIPopoverPresentationControllerDelegate {
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
// to prevent animation, we need to dismiss it manuallly with animated: false
presentationController.presentingViewController.dismiss(animated: false, completion: nil)
return true
}
}
On iOS 9+ as by default modalPresentationStyle = .Popover you can implement this method to prevent dismiss clicking out
public func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool {
return false
}