How to add a border color on a popover and its arrow in Swift?

Viewed 1581

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?

3 Answers

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

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?

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)
Related