Detect if my controller is presented as popover - ios

Using Xcode 10.1 and Swift 4.2.1, iOS 12
I got a tableview embedded in a navigation controller. In the upper right corner a bar button and a segue (popover) from this button to a navigation controller which holds a static table view, here the cells has further segues to other table views.
On an iPhone the static table view is displayed as a full screen modal, on an iPad it's a popover, which is okay so far.
I've set everything up in the storyboard, don't know if this could be the reason, but now I'm struggling to check if the view is a popover or a full screen modal.
I've tried:
print("\(presentationController)")
if presentationController is UIPopoverPresentationController {
// Do something
}
But this doesn't work -> print("\(presentationController)") gives me Optional(<_UIFullscreenPresentationController: 0x7fd00ad45770>) on iPhone and iPad.
I've tried also with:
if popoverPresentationController != nil {
print("popover")
}
But popover is printed if launched on iPhone or iPad.
Am I doing something wrong here or am I missing something?

Currently I'm using this:
if (popoverPresentationController?.arrowDirection != UIPopoverArrowDirection.unknown) {
tableView.sectionHeaderHeight = CGFloat.leastNormalMagnitude
tableView.sectionFooterHeight = CGFloat.leastNormalMagnitude
}
It's easy, and it works. In case someone has a real possibility to find out "if is a popover", you are very welcome to post it in here.

Use "UIModalPresentationStyle" of the presentedViewController to detect presentation style is fullscreen or popover.
You can find more details here: https://developer.apple.com/documentation/uikit/uimodalpresentationstyle

Simply you can check this in the presented ViewController
if self.modalPresentationStyle == .popover {
print("Popover presentation")
}
In case if the presented view controller is enclosed in a navigation controller, then use this
if self.navigationController?.modalPresentationStyle == .popover {
print("Popover presentation")
}

Related

Navigation bar buttons set during viewDidLoad don't appear until after view has appeared in iOS 11

In iOS 10, I could determine the list of navigation bar buttons I want to appear in viewDidLoad, and they would appear in the navigation bar as the view controller transitioned in.
In iOS 11, I can no longer do this. Whatever navigation bar buttons were set in interface builder are displayed as the view controller transitions in, and my custom list of buttons are not displayed until after the view finishes sliding in. Updating the buttons in viewWillAppear does not help.
Is this simply a bug in iOS 11, or is there something else I should be doing? Is there a workaround so I can continue to display buttons while the screen loads?
In the following example, I have set a button "Default Button" in the storyboard, and override it with an "Edit" button in viewDidLoad. The project is available on Github.
iOS 10
iOS 11
It looks like the issue is that navigation bar icons displayed during the transition appear to be fixed when the view controller is passed off to the navigation controller. By the time that viewDidLoad is called, the icons have already been fixed.
In order to fix this, we need to update the navigation bar icons on the view controller's navigationItem before the view controller is pushed onto the navigation controller stack.
One way to do this would be to setup the navigation bar icons in awakeFromNib. This is what #Joe's answer was effectively doing, because apparently viewDidLoad is called from awakeFromNib when isViewLoaded is true during awakeFromNib.
However, doing this in awakeFromNib prevents you from taking into account any properties set on the view controller in prepareForSegue. So another option (and the one that I am using) is to force the entire view to load in prepareForSegue by adding the line _ = controller.view after setting any desired properties.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
...
_ = controller.view
}
}
}
Move your Edit barButton code from viewDidLoad to isViewLoaded method as below.
override var isViewLoaded: Bool {
self.navigationItem.rightBarButtonItem = self.editButtonItem
return true
}
Output:
Note: Above code will fix the transition delay issue. Really, don't have much to explain why this happening. I experienced similar issue in iOS 10 as well NavigationBar delay updating barTintColor iOS10. It could be another bug in iOS11.
PS:
After reading Apple Doc about isViewLoaded and comparing other view loading methods. Its all about loading memory upon view loads.
You don't really need to move you barButton code at all.Just implement the isViewLoaded method and return to true as below:
override var isViewLoaded: Bool { return true}

Swift Modal View Controller with transparent background [duplicate]

This question already has answers here:
Transparent background for modally presented viewcontroller
(5 answers)
Closed 7 years ago.
I know this topic is quite popular, but I'm a little iniciate problem in a programming language, the fact is that I still do not understand where I put the code. Well, I'll tell the whole case:
I'm trying to make a modal Swift in a little different from normal: By clicking on a button, the ViewController is displayed (following modal type) on the screen, but with transparent background. Only the blue View with label will be displayed. When this ViewController is presented, it is with transparent background, but as soon as it completes the transition, it will stay with the black background. Already deactivated the opaque option, and tested some options, but nothing this troubleshooting.
Some can help me?
The video is a test in the simulator on the case (https://www.youtube.com/watch?v=wT8Uwmq9yqY).
I'm starting with swift, and I'm still pretty lost with how to program in Xcode, I read an answer to a question that has the following code to solve this:
self.presentingViewController.providesPresentationContextTransitionStyle = YES;
self.presentingViewController.definesPresentationContext = YES;
modal.modalPresentationStyle = UIModalPresentationOverCurrentContext;
Where do I put this code?
You can do it like this:
In your main view controller:
func showModal() {
let modalViewController = ModalViewController()
modalViewController.modalPresentationStyle = .overCurrentContext
presentViewController(modalViewController, animated: true, completion: nil)
}
In your modal view controller:
class ModalViewController: UIViewController {
override func viewDidLoad() {
view.backgroundColor = UIColor.clearColor()
view.opaque = false
}
}
If you are working with a storyboard:
Just add a Storyboard Segue with Kind set to Present Modally to your modal view controller and on this view controller set the following values:
Background = Clear Color
Drawing = Uncheck the Opaque checkbox
Presentation = Over Current Context
As Crashalot pointed out in his comment: Make sure the segue only uses Default for both Presentation and Transition. Using Current Context for Presentation makes the modal turn black instead of remaining transparent.

How can I tell programmatically if a ViewController presented UIModalPresentationFormSheet opened on full screen or not?

Modally presenting a ViewController on iPhone 6+ with a style of UIModalPresentationFormSheet opens differently according to orientation.
In portrait mode it seem like regular Modal (same as smaller iPhones).
But in landscape mode it opens as form (similar to iPads).
How can I tell programmatically what state was actually used (anywhere with in the VC's life cycle).
The controller displays as a form sheet when the size class for the controller has regular width.
So on an iPhone 6+ in landscape, or on an iPad in any orientation the horizontal size class is regular and the form is displayed less than full screen width.
You can test for this in a controller using:
if (self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {
// ... Its showing as per the form specification
}
else{
// ... Its showing as a modal full screen.
}
Replace self with the variable for a controller if being called from somewhere else.
This also covers the case where you might use a popover, since when you use a popover on an iPad the size class changes to compact within the popover itself.
A clean Swift solution is below. A UIViewController extension that adds the isBeingPresentedInFormSheet method.
extension UIViewController {
func isBeingPresentedInFormSheet() -> Bool {
if let presentingViewController = presentingViewController {
return traitCollection.horizontalSizeClass == .Compact && presentingViewController.traitCollection.horizontalSizeClass == .Regular
}
return false
}
}
This method returns true if the view controller is currently being displayed in a Form Sheet.
In my experience this occurs when
modalPresentationStyle = .FormSheet
and the device is an iPad or iPhone 6 Plus in landscape orientation.

UINavigationController without navigation bar?

I have a universal app, and on the iPad version I'm using UISplitViewController to create an interface similar to the Mail app.
I was having trouble pushing new Detail views, so I decided to use a UINavigationController so I could just push and pop views as needed. However, I do not want to use the navigation view or a toolbar. But no matter what I do, I can't hide the navigation bar.
I've tried unchecking "Shows Navigation Bar" in IB, and I've also tried setting:
[self.navigationController setNavigationBarHidden:YES];
in the viewDidLoad/viewDidAppear/viewWillAppear. I've also tried it in each of the views that will be pushed. Nothing works.
Is there something I'm missing here? Is it possible to have a UINavigationController without a toolbar or navigation bar?
You should be able to do the following:
self.navigationController.navigationBar.isHidden = true //Swift 5
where self.navigationController is (obviously) an instance of UINavigationController. Seems to work for me, but I only briefly tested it before posting this.
In Xcode 4.3.2:
Select the navigation controller in the storyboard
Select the Attributes Inspector in the (right) Utilities panel
Under the Navigation Controller category you have two check boxes:
[] Shows Navigation Bar
[] Shows Toolbar
Worked for me...
If you want no navigation bar, and you want the content to be adjusted up to where the navigation bar normally would be, you should use
self.navigationController.navigationBarHidden = YES;
This gives you a result like this:
Whereas self.navigationController.navigationBar.hidden = YES; gives you a space where the navigationBar should be. Like this:
Swift 4
I hide it in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = true;
}
Then you can put it back when you push a segue (if you want to have the back button on the next view)
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
self.navigationController?.isNavigationBarHidden = false;
}
Swift 3 Programmatically
self.navigationController.isNavigationBarHidden = true
or
self.navigationController.navigationBar.isHidden = true
Note: I didn't see a difference between these two approaches testing on iOS 10.
All these answers still leave a space at the top for the status bar - add this line to remove that as well:
navController.navigationBar.isHidden = true
navController.accessibilityFrame = CGRect.zero

Why is [UIViewController presentModalViewController:animated:] transitioning the modal view in from the left?

I'm building an application that supports only Landscape orientation on the iPad under iOS 4.3, though the bug was present under iOS 4.2 as well.
Several places in the application, I am showing UIViewControllers as modal views. All of them are shown using this pattern:
viewController.modalPresentationStyle = UIModalPresentationFormSheet;
viewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:viewController animated:YES];
In most places, they work as expected - the modal form sheet slides in from the bottom of the screen upward.
However, in two cases, the modal form sheet slides in from the bottom left of the screen. The form sheet slides nearly all the way to the right with the bottom of the form sheet out of view. If you focus a text field and show the onscreen keyboard, the form sheet moves to the top center of the screen where you would expect it to be.
I don't think the Simulated Metrics of the XIBs affect their behaviour, but I have set the orientation for all of them (both the calling UIViewController self and the modal view controller viewController) to Landscape.
Any ideas why these two modal form sheets are behaving differently from the others?
I think here you dint return landscape orientation in shouldAutorotate function so use this code if you wan view as landscape left/right:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight||interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
// return YES;
}
Try this it may help you..
Make sure if you have a viewController embedded in a NavigationController that the navController shouldAutotorate is also set to landscape

Resources