Reputation: 1542
I have the following popover in my app:
But I want it to look like this edited image (note that even the arrow has a border):
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?
Upvotes: 10
Views: 2751
Reputation: 101
I found Alexander-Langer's answer worked great except it resulted in two borders, one of which was across the middle of the popup. I found viewWillLayoutSubviews was being called multiple times during setup with two different view sizes and setting the a clear color for the smaller view resulted in a much neater outcome.
// View Will Layout Subviews
//---------------------------------------------------------------------------------------------------------------------------------------
override func viewWillLayoutSubviews() {
// Add a colored border around popup
guard let shapeLayer = view.superview?.superview?.mask?.layer as? CAShapeLayer else { return }
// Found this is called alternately for two different borders and one is smaller than the final
// Views are about 53 pixels bigger than desired guide size,
var colr = UIColor.green.cgColor
if view.bounds.height < (262.0+40.0) {
colr = UIColor.clear.cgColor
}
print("🔲 Rolling view bounds \(view.bounds.height) < ? pref size \(262.0+40.0))")
let borderLayer = CAShapeLayer()
borderLayer.path = shapeLayer.path
borderLayer.lineWidth = 4
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.frame = shapeLayer.bounds
borderLayer.strokeColor = colr
view.superview?.superview?.layer.addSublayer(borderLayer)
}
In my case "262" was my preferred height and the popup view will be 53 larger to accommodate everything. I just made sure that the smaller view was less than 40 more than my preferred size and the smaller view is given a clear boundary.
Also, setting the view background to the same border color will fill in the little triangle beside the button.
Upvotes: 0
Reputation: 330
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.
Upvotes: 5
Reputation: 85
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)
Upvotes: 0
Reputation: 216
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:
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:
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?
Upvotes: 1