29

When I am displaying some view in UIPopoverPresentationController and presenting it as popover

popoverCon?.modalPresentationStyle = UIModalPresentationStyle.popover

the content have moved upward toward and a some part is being display in the arrow.

enter image description here

Further I had border around the popover

popoverCon?.view.layer.borderColor = .orange
popoverCon?.view.layer.borderWidth = 1.0;
popoverCon?.view.layer.cornerRadius = 10.0;
popoverCon?.view.layer.masksToBounds = false;

it is not showing toward the part where arrow is but it displays a little of the border line in the tip of the arrow.

enter image description here

This was working fine until iOS 12 but in iOS 13 this issue is coming.

Any suggestions on how I can solve this?

Hassy
  • 5,068
  • 5
  • 38
  • 63

9 Answers9

9

The top of my tableView content was cut off by the arrow. This is how I fixed it in my case (code inserted in my tableViewController Swift file):

override func viewSafeAreaInsetsDidChange() {
    if #available(iOS 11.0, *) {
        super.viewSafeAreaInsetsDidChange()
        self.tableView.contentInset = UIEdgeInsets(top: self.tableView.safeAreaInsets.top, left: 0, bottom: 0, right: 0)
    }
}
powertoold
  • 1,593
  • 13
  • 19
  • 1
    Setting only top inset won't work, you should use safe areas. With your solution popup will look weird if arrow is anywhere except the top – IntegerOverlord Sep 27 '19 at 11:40
  • "With your solution popup will look weird if arrow is anywhere except the top" - not true in my case, but I agree this solution is a bit of a hack – powertoold Sep 27 '19 at 17:59
  • @SaintMSent Thanks a ton for that comment. Using safeAreaLayoutGuides is the way to go. – Warpzit May 28 '20 at 09:50
7

My solution in Obj-C, for those who need an obj-c solution.

I had previously only popovercontroller, that was creating the error like shown in the question. I renamed it to childController for clarity and created a containing popoverController to make the solution given by @SaintMSent work in my situation of only one view originally. Also used https://stackoverflow.com/a/47076040/2148757 solution and https://useyourloaf.com/blog/self-sizing-child-views/ to resize appropriately since all of my childControllers set the preferred content size frequently.

//Create container popover controller and add child to it
UIViewController* popoverController = [[MyParentPopoverController alloc] init];
[popoverController.view addSubview:childController.view];
[popoverController addChildViewController:childController];
[popoverController setPreferredContentSize:childController.preferredContentSize];
//set popover settings on container
popoverController.modalPresentationStyle = UIModalPresentationPopover;
popoverController.popoverPresentationController.sourceRect = sourceRect;
popoverController.popoverPresentationController.sourceView = buttonView;
popoverController.popoverPresentationController.permittedArrowDirections = direction;
//Fix ios13 'bug' that Apple claims is a feature
UILayoutGuide* guide = popoverController.view.safeAreaLayoutGuide;
childController.view.translatesAutoresizingMaskIntoConstraints = NO;
[childController.view.leadingAnchor constraintEqualToAnchor:guide.leadingAnchor].active = YES;
[childController.view.trailingAnchor constraintEqualToAnchor:guide.trailingAnchor].active = YES;
[childController.view.topAnchor constraintEqualToAnchor:guide.topAnchor].active = YES;
[childController.view.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor].active = YES;
[popoverController.view layoutIfNeeded];
//Show the popover

...

@interface MyParentPopoverController : UIViewController

@end

@implementation MyParentPopoverController

-(void)preferredContentSizeDidChangeForChildContentContainer:(id <UIContentContainer>)container {
    [super preferredContentSizeDidChangeForChildContentContainer:container];
    [self setPreferredContentSize:container.preferredContentSize];
}

@end

Note: I didn't check for ios11 compatibility because my user base is restricted to not use it.

Sashah
  • 418
  • 5
  • 18
  • This answer helped me. I wrapped the constraint portion of the code with `if (@available(iOS 11, *))` and I did not need the `MyParentPopoverController` subclass (since in my app the content size does not change). – Mark Smith Jul 30 '20 at 21:33
4

It is definitely a feature, they want you to use safe area since iOS 11, actually, but it seems now they want to force you to use it

Had the same problem as you, this worked for me https://useyourloaf.com/blog/safe-area-layout-guide/

IntegerOverlord
  • 1,477
  • 1
  • 14
  • 33
1

Definitely a bug. When you have a situation where you use UIPopoverArrowDirectionAny you will see that the problem only exists when the arrow is at the top or left of the popover and not when the arrow appears at the right or the bottom of the popover. If you make adjustments in your code to compensate it will work if you use UIPopoverArrowDirectionUp or UIPopoverArrowDirectionLeft but will not display correctly using that adjustment when using UIPopoverArrowDirectionAny and the popup appears above or to the right of the target rectangle.

1

I don't have an 'answer' yet, but I have identified what's going on and why it's so hard to fix.

ios13 UIPopoverViewController showing UITableViewController - Safe Area problems / Missing parts of table

Basically, any UITableView that has headers or footers is going to be broken in iOS 13 unless there's some way to alter the _UITableViewHeaderFooterViewBackground

That is notoriously problematic and doesn't play nicely with Auto-Layout - it's been known about for years, but Apple have never fixed it or made it easier to deal with and more publicly known.

https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=video&cd=1&cad=rja&uact=8&ved=0ahUKEwibouuozfvkAhVCXRUIHVGsBegQtwIIKjAA&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DajsCY8SjJ1Y&usg=AOvVaw3_U_jy9EWH2dJrM8p-XhDQ

https://spin.atomicobject.com/2016/10/12/auto-layout-uitableview/

I'm unable to push my app to the App Store until I get this sorted.. I hope someone can identify how to manipulate this view so that it stops pushing the boundaries of the table out of whack with AutoLayout which causes this safe area intrusion.

iOSProgrammingIsFun
  • 1,418
  • 1
  • 15
  • 32
0

Searching on the internet I got help from following link

Twitter

so I had to add safe area and manage my views accordingly

CGFloat topPadding = 0.0;
    if (@available(iOS 11.0, *)) {
         topPadding = self.view.safeAreaLayoutGuide.layoutFrame.origin.y;
    }

Swift:

var topPadding: CGFloat = 0.0
    if #available(iOS 11.0, *) {
        topPadding = self.view.safeAreaLayoutGuide.layoutFrame.origin.y
    }

but I haven't got solution to the border problem of mine yet.

Edit: Temporarily I did solved the border problem by creating an invisible view on popover and giving it same frame as safe area and drawing its border.

Hassy
  • 5,068
  • 5
  • 38
  • 63
  • Posted to Apple: Sep 23, 2019 at 5:43 PM – FB7316667 – iOSProgrammingIsFun Sep 23 '19 at 16:43
  • @iOSProgrammingIsFun it is not a bug, somebody provided twitter link where that is stated. Also, if you use safe areas, as they want you to, problem you are describing will not occur (see my answer) – IntegerOverlord Sep 27 '19 at 11:39
  • @iOSProgrammingIsFun it is not my blog. Apple wants you to use safe zones for your popovers since iOS 11, but now they are forcing you to do that. You have your view, which belongs to controller that you present in a popover. Than you have another view, which has your content in it. And in ViewDidLoad method you just add the code that is mentioned in the article and it creates a safe zone around your content. Btw, as for OP case, the better solution is to present that as a modal page, Dismiss and Done buttons at the top are better suited for modal pages – IntegerOverlord Sep 28 '19 at 18:30
  • @iOSProgrammingIsFun if you are using storyboard, you can apply safe zone via interface builder as it is explained in that blogpost. I did it in code because I didn't use interface builder for that viewcontroller – IntegerOverlord Sep 29 '19 at 11:54
0

You should use constraints. And also pay attention to topAnchor. It must be safeAreaLayoutGuide.topAnchor. In my case, it works correctly. For example:

[NSLayoutConstraint activateConstraints:@[
        [toolbar.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
        [toolbar.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
        [toolbar.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
        [toolbar.heightAnchor constraintEqualToConstant:50]
    ]];
Nikita
  • 9
  • 1
0

Embed the contents of the popover in another view with "safe area relative margins" on. This should have -21,-21 as the origin. Turn off vertical and horizontal auto resizing. Seems to work, although you lose auto-stretching.

Gedalia
  • 443
  • 2
  • 5
-1

Setup your popover's content UIViewController like such:

NSLayoutConstraint.activate([
        myContentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        myContentView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
        myContentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
        myContentView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor)
    ])
Bryan
  • 614
  • 1
  • 8
  • 18