9

I have the following popover in my app:

enter image description here

But I want it to look like this edited image (note that even the arrow has a border):

enter image description here

Is this possible nowadays? I already tried the following inside the UIViewController:

self.view.layer.borderColor = UIColor.white.cgColor
self.view.layer.borderWidth = 2

But that doesn't work for the arrow. Is there any turn around on this?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
ThiagoAM
  • 1,432
  • 13
  • 20

3 Answers3

4

It took me a while, but finally I was able to solve this problem. It begins with a common UiPopoverPresentationController. Nothing special about it yet.

This Controller masks itself somewhere between viewWillAppear and viewDidAppear. So I took the path of its own mask when the popover was shaped and used this to make a sublayer with a stroke.

    override func viewWillLayoutSubviews() {
        guard let shapeLayer = view.superview?.superview?.mask?.layer as? CAShapeLayer else { return }
        let borderLayer = CAShapeLayer()
        borderLayer.path = shapeLayer.path
        borderLayer.lineWidth = 4
        borderLayer.strokeColor = borderColor.cgColor
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.frame = shapeLayer.bounds
        view.superview?.superview?.layer.addSublayer(borderLayer)
    }

The result looks like this.

Result looks like this

Alexander Langer
  • 320
  • 2
  • 11
  • Hi @Alexander, I used your solution on iPhone 13 Pro (15.5) simulator, the only problem is there is also a border under the arrow, do you have any suggestion on how to remove it, – user3201520 Nov 23 '22 at 11:17
  • Hello @user3201520 the coloring might be your issue. Maybe it helps when I explain what I did there. It is a TableView inside a popover. In my screenshot I did scroll the tableview to the middle. If it is scrolled on top the background color of the tableview appears. I had to match the colors (cellColor, dividerColor, tableViewBackgroundColor, PopupStrokeColor, etc.) to make a proper look. – Alexander Langer Nov 26 '22 at 11:41
1

Have you tried setting the stroke to the UIBezierPath instead? You can do so with the following lines of code before you close your UIBezierPath:

Color.purple.setStroke()
bezierPath.stroke()

Here's the result I manage to achieve on Playground:

enter image description here

However, I realized that I couldn't change the width of it so instead, I created another layer just for the border instead.

let borderLayer = CAShapeLayer()
borderLayer.frame = self.bounds
borderLayer.path = bezierPath.cgPath
borderLayer.lineWidth = 6.0
borderLayer.strokeColor = UIColor.black.cgColor
borderLayer.fillColor = UIColor.clear.cgColor
self.layer.insertSublayer(borderLayer, at: 0)

Playground result:

enter image description here

I think this should help but I can't really suggest much without the code on how the shape is achieved. Are you using UIBezierPath or CGContext?

Li Sim
  • 216
  • 1
  • 6
  • I just realised that my answer was downvoted before. Please leave a feedback on how I could improve or fix this answer to help everybody too – Li Sim Nov 01 '18 at 08:10
  • The question is about a standard view controller presented as a popover. There is no bezier path to stroke or custom view layer to modify. – rmaddy Nov 01 '18 at 21:04
  • But it is not about a standard view controller presented as a popover. It's about adding a border to a custom shape. Unless viewController has another approach/API to handling custom shape, I can't think of other ways as to how a custom shape with border is achieved. @rmaddy – Li Sim Nov 02 '18 at 01:33
  • It's tagged with `UIPopoverPresentationController`. The screenshot shows a standard popover. Curious why you think it's not a standard popover. Of course it would help if the OP posted more relevant code. – rmaddy Nov 02 '18 at 01:39
  • Ah, I see where the misunderstanding comes from now. Thanks for the clarification – Li Sim Nov 02 '18 at 01:44
0

You can use ElegantPopover to accomplish this.

var design = PSDesign()
design.borders = [PSBorder(filling: .pureColor(yourBorderColor), width: yourBorderWidth)]

// Corner radius of popover
design.cornerRadius = 15

// Customise the arrow as you like
let arrow = PSArrow(height: 30, base: 60, baseCornerRadius: 5, direction: .up)

let popoverController = ElegantPopoverController(contentView: yourContentView,
                                            design: design,
                                            arrow: arrow
                                            barButtonItem: barBtnItem)

present(popoverController, animated: true)