I am using UIBezierPath to have my imageview have round corners but I also want to add a border to the imageview. Keep in mind the top is a uiimage and the bottom is a label.
Currently using this code produces:
let rectShape = CAShapeLayer() rectShape.bounds = myCell2.NewFeedImageView.frame rectShape.position = myCell2.NewFeedImageView.center rectShape.path = UIBezierPath(roundedRect: myCell2.NewFeedImageView.bounds, byRoundingCorners: .TopRight | .TopLeft, cornerRadii: CGSize(width: 25, height: 25)).CGPath myCell2.NewFeedImageView.layer.mask = rectShape
I want to add a green border to that but I cant use
myCell2.NewFeedImageView.layer.borderWidth = 8 myCell2.NewFeedImageView.layer.borderColor = UIColor.greenColor().CGColor
because it cuts off the top left and top right corner of the border as seen in this image:
Is there a way too add in a border with UIBezierPath along with my current code?
You can give it round corners by changing the cornerRadius property of the view's layer . and smaller values give less rounded corners. Both clipsToBounds and masksToBounds are equivalent. It is just that the first is used with UIView and the second is used with CALayer .
Select the view that you want to round and open its Identity Inspector. In the User Defined Runtime Attributes section, add the following two entries: Key Path: layer. cornerRadius , Type: Number, Value: (whatever radius you want)
Any SwiftUI view can have its corners rounded using the cornerRadius() modifier.
You can reuse the UIBezierPath path and add a shape layer to the view. Here is an example inside a view controller.
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Create a view with red background for demonstration let v = UIView(frame: CGRectMake(0, 0, 100, 100)) v.center = view.center v.backgroundColor = UIColor.redColor() view.addSubview(v) // Add rounded corners let maskLayer = CAShapeLayer() maskLayer.frame = v.bounds maskLayer.path = UIBezierPath(roundedRect: v.bounds, byRoundingCorners: .TopRight | .TopLeft, cornerRadii: CGSize(width: 25, height: 25)).CGPath v.layer.mask = maskLayer // Add border let borderLayer = CAShapeLayer() borderLayer.path = maskLayer.path // Reuse the Bezier path borderLayer.fillColor = UIColor.clearColor().CGColor borderLayer.strokeColor = UIColor.greenColor().CGColor borderLayer.lineWidth = 5 borderLayer.frame = v.bounds v.layer.addSublayer(borderLayer) } }
The end result looks like this.
Note that this only works as expected when the view's size is fixed. When the view can resize, you will need to create a custom view class and resize the layers in layoutSubviews
.
As it says above:
Here's a drop-in solution.
This
correctly addresses the issue that you are drawing HALF OF THE BORDER LINE
is totally usable in autolayout
completely re-works itself when the size of the view changes or animates
is totally IBDesignable - you can see it in realtime in your storyboard
for 2019 ...
@IBDesignable class RoundedCornersAndTrueBorder: UIView { @IBInspectable var cornerRadius: CGFloat = 10 { didSet { setup() } } @IBInspectable var borderColor: UIColor = UIColor.black { didSet { setup() } } @IBInspectable var trueBorderWidth: CGFloat = 2.0 { didSet { setup() } } override func layoutSubviews() { setup() } var border:CAShapeLayer? = nil func setup() { // make a path with round corners let path = UIBezierPath( roundedRect: self.bounds, cornerRadius:cornerRadius) // note that it is >exactly< the size of the whole view // mask the whole view to that shape // note that you will ALSO be masking the border we'll draw below let mask = CAShapeLayer() mask.path = path.cgPath self.layer.mask = mask // add another layer, which will be the border as such if (border == nil) { border = CAShapeLayer() self.layer.addSublayer(border!) } // IN SOME APPROACHES YOU would INSET THE FRAME // of the border-drawing layer by the width of the border // border.frame = bounds.insetBy(dx: borderWidth, dy: borderWidth) // so that when you draw the line, ALL of the WIDTH of the line // DOES fall within the actual mask. // here, we will draw the border-line LITERALLY ON THE EDGE // of the path. that means >HALF< THE LINE will be INSIDE // the path and HALF THE LINE WILL BE OUTSIDE the path border!.frame = bounds let pathUsingCorrectInsetIfAny = UIBezierPath(roundedRect: border!.bounds, cornerRadius:cornerRadius) border!.path = pathUsingCorrectInsetIfAny.cgPath border!.fillColor = UIColor.clear.cgColor // the following is not what you want: // it results in "half-missing corners" // (note however, sometimes you do use this approach): //border.borderColor = borderColor.cgColor //border.borderWidth = borderWidth // this approach will indeed be "inside" the path: border!.strokeColor = borderColor.cgColor border!.lineWidth = trueBorderWidth * 2.0 // HALF THE LINE will be INSIDE the path and HALF THE LINE // WILL BE OUTSIDE the path. so MAKE IT >>TWICE AS THICK<< // as requested by the consumer class. } }
So that's it.
Make a "new Swift file" called "Fattie.swift". (Note, funnily enough it actually makes no difference what you call it. If you are at the stage of "don't know how to make a new file" seek basic Xcode tutorials.)
Put all of the above code in the file
You've just added a class "RoundedCornersAndTrueBorder" to your project.
On your story board. Add an ordinary UIView to your scene. In fact, make it actually any size/shape whatsoever, anything you prefer.
Look at the Identity Inspector. (If you do not know what that is, seek basic tutorials.) Simply change the class to "RoundedCornersAndTrueBorder". (Once you start typing "Roun...", it will guess which class you mean.
You're done - run the project.
Note that you have to, of course, add complete and correct constraints to the UIView, just as with absolutely anything you do in Xcode. Enjoy!
Similar solutions:
https://stackoverflow.com/a/57465440/294884 - image + rounded + shadows
https://stackoverflow.com/a/41553784/294884 - two-corner problem
https://stackoverflow.com/a/59092828/294884 - "shadows + hole" or "glowbox" problem
https://stackoverflow.com/a/57400842/294884 - the "border AND gap" problem
https://stackoverflow.com/a/57514286/294884 - basic "adding" beziers
And please also see the alternate answer below! :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With