The following code:
let size = CGSize(width: 200, height: 30)
let rect = CGRect(origin: .zero, size: size)
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: 10, y: 5))
path1.addLine(to: CGPoint(x: 180, y: 5))
path1.addArc(withCenter: CGPoint(x: 180, y: 20), radius: 15,
startAngle: (3.14159 / 2), endAngle: (3 * 3.14159 / 2), clockwise: false)
produces this:
Ok, am I missing something? I do not want to close this path. I never call path1.close(). I want to add another straight line from the end of the arc, not from the closed version of it. Basically, I don't want the half circle to be closed, how can I do that?
You need to start your arc at -90 degrees and end it at +90 degrees. You need also to change its direction. You need to do as follow:
path1.addArc(withCenter: CGPoint(x: 180, y: 20), radius: 15, startAngle: -.pi/2, endAngle: .pi/2, clockwise: true)
If you would like to complete the shape it would look like this:
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: 10, y: 5))
path1.addLine(to: CGPoint(x: 180, y: 5))
path1.addArc(withCenter: CGPoint(x: 180, y: 20), radius: 15, startAngle: -.pi/2, endAngle: .pi/2, clockwise: true)
path1.addLine(to: CGPoint(x: 10, y: 35))
path1.addArc(withCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: .pi/2, endAngle:-.pi/2 , clockwise: true)
Hope this will help you to achieve your result
let size = CGRect(origin: .zero, size: CGSize(width : 200 , height : 30))
let path = UIBezierPath()
path.move(to: CGPoint(x: 10, y: 100))
path.addLine(to: CGPoint(x: 180, y: 100))
path.addArc(withCenter: CGPoint(x:180 , y: 85), radius: 15, startAngle: (3.14159 / 2), endAngle: (3 * 3.14159 / 2), clockwise: false)
path.addLine(to: CGPoint(x: 10, y: 70)) //y = radius * 2
Above code will draw this in your canvas .
Related
how to draw the curve with blur background?
here I have attached my code but am not getting the exact output
#IBDesignable class CurvedHeaderView: UIView {
#IBInspectable var curveHeight:CGFloat = 50.0
var curvedLayer = CAShapeLayer()
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.move(to: CGPoint(x: 0, y: rect.height))
// path.addLine(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addArc(withCenter: CGPoint(x: CGFloat(rect.width) - curveHeight, y: 100), radius: curveHeight, startAngle: 0, endAngle: 1.5 * CGFloat.pi, clockwise: false)
// path.addArc(withCenter: CGPoint(x: CGFloat(rect.width) - curveHeight, y: rect.height), radius: curveHeight, startAngle: 0, endAngle: 1.5 * CGFloat.pi, clockwise: false)
// path.addLine(to: CGPoint(x: curveHeight, y: rect.height - curveHeight))
// path.addLine(to: CGPoint(x: curveHeight, y: 50))
path.addLine(to: CGPoint(x: curveHeight, y: curveHeight))
path.addArc(withCenter: CGPoint(x: curveHeight, y: 0), radius: curveHeight, startAngle: 0, endAngle: CGFloat.pi, clockwise: true)
path.addLine(to: CGPoint(x: 0, y: 0))
// path.addArc(withCenter: CGPoint(x: curveHeight, y: rect.height - (curveHeight * 2.0)), radius: curveHeight, startAngle: 0, endAngle: CGFloat.pi, clockwise: true)
path.close()
curvedLayer.path = path.cgPath
curvedLayer.fillColor = UIColor.white.cgColor
curvedLayer.frame = rect
self.layer.insertSublayer(curvedLayer, at: 0)
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowRadius = 10.0
self.layer.shadowOpacity = 0.7
}
}
Please share me reference if anything wrong
Thanks in advance
I have tried but it showing code working but not exactly been getting
#IBOutlet weak var blurEffectView: UIVisualEffectView!
override func viewDidLayoutSubviews() {
let maskView = CurvedHeaderView(frame: blurEffectView.bounds)
maskView.clipsToBounds = true;
maskView.backgroundColor = UIColor.clear
blurEffectView.mask = maskView
}
I want to generate a curve in the middle of a line programatically, however the edge of the curve is not rounded using my current solution.
func createPath() -> CGPath {
let height: CGFloat = 86.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: (centerWidth - (height/2)), y: 0))
path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: 80 / 2, startAngle: 180 * CGFloat(PI)/180, endAngle: 0 * CGFloat(PI)/180, clockwise: false)
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.frame.height))
path.lineCapStyle = .round
path.stroke()
path.close()
self.path = path
return path.cgPath
}
This is the Arc I have created but I want the curves to be rounded:
But I want it to be like this:
This is the code that you need
func createPath() -> CGPath {
let padding: CGFloat = 5.0
let centerButtonHeight: CGFloat = 53.0
let f = CGFloat(centerButtonHeight / 2.0) + padding
let h = frame.height
let w = frame.width
let halfW = frame.width/2.0
let r = CGFloat(18)
let path = UIBezierPath()
path.move(to: .zero)
path.addLine(to: CGPoint(x: halfW-f-(r/2.0), y: 0))
path.addQuadCurve(to: CGPoint(x: halfW-f, y: (r/2.0)), controlPoint: CGPoint(x: halfW-f, y: 0))
path.addArc(withCenter: CGPoint(x: halfW, y: (r/2.0)), radius: f, startAngle: .pi, endAngle: 0, clockwise: false)
path.addQuadCurve(to: CGPoint(x: halfW+f+(r/2.0), y: 0), controlPoint: CGPoint(x: halfW+f, y: 0))
path.addLine(to: CGPoint(x: w, y: 0))
path.addLine(to: CGPoint(x: w, y: h))
path.addLine(to: CGPoint(x: 0.0, y: h))
path.close()
return path.cgPath
}
Result:
You can customize the size of the button...
For a great explanation of how it is work, I recommend you to read this explanation, and you can draw any shape which do you want: https://ayusinghi96.medium.com/draw-custom-shapes-and-views-with-uiberzierpath-ios-1737f5cb975
You can just draw more arc like you already does. Or may be you can use path.addCurve, if you dont want oval
func createPath() -> CGPath {
let bigRadius: CGFloat = 40.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
path.move(to: CGPoint(x: 0, y: 0))
let radius: CGFloat = 4 //change it if you want
let leftArcOriginX = centerWidth - bigRadius - radius
let leftArcOriginY: CGFloat = 0
path.addLine(to: CGPoint(x: leftArcOriginX, y: leftArcOriginY))
// add left little arc, change angle if you want, if you dont want oval, may be you can use path.addCurve(to: , controlPoint1: , controlPoint2: )
path.addArc(withCenter: CGPoint(x: leftArcOriginX, y: leftArcOriginY + radius), radius: radius, startAngle: CGFloat(270.0 * Double.pi/180.0), endAngle: 0, clockwise: true)
// add big arc
path.addArc(withCenter: CGPoint(x: centerWidth, y: radius), radius: bigRadius, startAngle: CGFloat(180.0 * Double.pi/180.0), endAngle: CGFloat(0 * Double.pi/180.0), clockwise: false)
// add right litte arc
path.addArc(withCenter: CGPoint(x: centerWidth + bigRadius + radius, y: radius), radius: radius, startAngle: CGFloat(180.0 * Double.pi/180.0), endAngle: CGFloat(270.0 * Double.pi/180.0), clockwise: true)
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.frame.height))
path.lineCapStyle = .round
path.stroke()
path.close()
return path.cgPath
}
I'm trying to convert my UIView to this shape with UIBezierPath, currently I'm only able to do the left bottom corner, seeking for help for adding other corners.
Code for left bottom only.
let mask = CAShapeLayer()
mask.frame = self.innerLayout.layer.bounds
let path = UIBezierPath()
let radius: CGFloat = 50
let rect = mask.bounds
path.move(to: rect.origin)
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.minX + radius, y: rect.maxY))
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: 0, endAngle: CGFloat(M_PI_2 * 3), clockwise: false)
mask.path = path.cgPath
self.innerLayout.layer.mask = mask
I did couple of trials for adding other corners but my UIView got funny shapes. I simply added this by copying and pasting (and changing origin), I believe we will use this part of the code 4 times to add 4 corners
path.move(to: CGPoint(x: rect.maxX, y: rect.maxY)
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.minX + radius, y: rect.maxY))
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: 0, endAngle: CGFloat(M_PI_2 * 3), clockwise: false)
Here's a complete example to be run in Playground. Such geometric shapes are always tricky. It's very easy to get coordinates wrong or have an arc go into the wrong direction.
import UIKit
import PlaygroundSupport
class ConcaveConrnerViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let frame = CGRect(x: 100, y: 200, width: 200, height: 200)
let subView = UIView(frame: frame)
subView.backgroundColor = .red
let mask = CAShapeLayer()
mask.frame = subView.layer.bounds
let path = UIBezierPath()
let radius: CGFloat = 50
let rect = mask.bounds
path.move(to: CGPoint(x: rect.minX + radius, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX - radius, y: rect.minY))
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: CGFloat(Double.pi / 2 * 2), endAngle: CGFloat(Double.pi / 2 * 3), clockwise: false)
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - radius))
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius, startAngle: CGFloat(Double.pi / 2 * 1), endAngle: CGFloat(Double.pi / 2 * 2), clockwise: false)
path.addLine(to: CGPoint(x: rect.minX + radius, y: rect.maxY))
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: CGFloat(Double.pi / 2 * 0), endAngle: CGFloat(Double.pi / 2 * 1), clockwise: false)
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + radius))
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: CGFloat(Double.pi / 2 * 3), endAngle: CGFloat(Double.pi / 2 * 0), clockwise: false)
path.close()
mask.path = path.cgPath
subView.layer.mask = mask
view.addSubview(subView)
self.view = view
}
}
PlaygroundPage.current.liveView = ConcaveConrnerViewController()
I hope all are doing well.
Here's the following, I need to make the cornerRadius like the one in the example, but in the icon, he doesn't change at all and in a Carousel, he only makes the bottom corners, not the top ones.
What can I do accomplish this? My corners don't have the same radius on all sides.
Icon Corners Example
I have a function for the cornerRadius:
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
}
In my icon I make the call this way:
bigIconContentView.roundCorners([.topRight, .bottomLeft], radius: 30)
bigIconContentView.roundCorners([.topLeft], radius: 10)
And in my Carousel I'm calling this way:
bStoreImage.roundCorners([.topLeft, .topRight], radius: 20)
beaconsContentView.roundCorners([.topLeft, .topRight], radius: 20)
beaconsContentView.roundCorners([.bottomLeft, .bottomRight], radius: 7)
What I'm doing wrong? What can I do to make them work and make these sides with different Radius?
I can make two roundCorners call to the same element but with different attributes? Like I did in my examples?
Thanks for your help!
Let's focus on roundCorners(_ corners: radius:)
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
let someNewMask = somethingNew(corners, radius: radius)
self.layer.mask = someNewMask
}
You are doing:
bigIconContentView.roundCorners([.topRight, .bottomLeft], radius: 30)
bigIconContentView.roundCorners([.topLeft], radius: 10)
Which in the ends, is doing this:
let someNewMask1 = somethingNew([.topRight, .bottomLeft], radius: 30)
bigIconContentView.mask = someNewMask1
let someNewMask2 = somethingNew([. topLeft], radius: 10)
bigIconContentView.mask = someNewMask2
So you are doing:
bigIconContentView.mask = someNewMask1
bigIconContentView.mask = someNewMask2
You see the issue? You are overriding bigIconContentView mask with a new one each time (the last one called).
You have to apply the different corners all together (there is nothing complicated in corner radius)
extension UIView {
func roundCornersRadii(topLeft: CGFloat = 0, topRight: CGFloat = 0, bottomLeft: CGFloat = 0, bottomRight: CGFloat = 0) {
let path = UIBezierPath()
path.move(to: CGPoint(x: topLeft, y: 0))
path.addLine(to: CGPoint(x: bounds.width - topRight, y: 0))
path.addArc(withCenter: CGPoint(x: bounds.width - topRight, y: topRight),
radius: topRight,
startAngle: -CGFloat.pi/2.0,
endAngle: 0,
clockwise: true)
path.addLine(to: CGPoint(x: bounds.width, y: bounds.width - bottomRight))
path.addArc(withCenter: CGPoint(x: bounds.width - bottomRight, y: bounds.height - bottomRight),
radius: bottomRight,
startAngle: 0,
endAngle: CGFloat.pi/2.0,
clockwise: true)
path.addLine(to: CGPoint(x: bottomRight, y: bounds.height))
path.addArc(withCenter: CGPoint(x: bottomLeft, y: bounds.height - bottomLeft),
radius: bottomLeft,
startAngle: CGFloat.pi/2.0,
endAngle: CGFloat.pi,
clockwise: true)
path.addLine(to: CGPoint(x: 0, y: topLeft))
path.addArc(withCenter: CGPoint(x: topLeft, y: topLeft),
radius: topLeft,
startAngle: CGFloat.pi,
endAngle: CGFloat.pi/2.0,
clockwise: true)
path.close()
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
}
}
Adding sample test:
let superView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
let subview = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
superView.backgroundColor = .red
subview.backgroundColor = .blue
subview.roundCornersRadii(topLeft: 20, topRight: 30, bottomLeft: 40, bottomRight: 50)
superView.addSubview(subview)
or in your case:
let superView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
let subview = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
superView.backgroundColor = .red
subview.backgroundColor = .blue
subview.roundCornersRadii(topLeft: 20, topRight: 30, bottomLeft: 40, bottomRight: 50)
subview.roundCornersRadii(topLeft: 10, topRight: 30, bottomLeft: 30)
superView.addSubview(subview)
I want to change the radius of two corners of a SKShapeNode (rect) but I didn't find a working solution.
I've tried to use a path, and it didn't work.
Swift 4.2, iOS 12.1.1, Xcode 10.1
let shape = SKShapeNode()
shape.path = UIBezierPath(roundedRect: CGRect(x: -128, y: -128, width: 256, height: 256), cornerRadius: 64).cgPath
shape.position = CGPoint(x: frame.midX, y: frame.midY)
shape.fillColor = UIColor.red
shape.strokeColor = UIColor.blue
shape.lineWidth = 10
addChild(shape)
I made a function that will allow you to customize each corner radius to whatever size you'd like. You can have 1,2,3 or 4 corners with a radius. If you always just want two corners then I would suggest making a wrapper function so you don't have so many parameters to fill in each time you call it.
func CustomRoundRectPath(_ rect:CGRect, _ TLR:CGFloat,_ TRR:CGFloat,_ BLR:CGFloat,_ BRR:CGFloat) -> CGPath {
let w = rect.width
let h = rect.height
//TLP:(TLP)
let TLP = CGPoint(x: TLR, y: h - TLR)
let TRP = CGPoint(x: w - TRR, y: h - TRR)
let BLP = CGPoint(x: BLR, y: BLR)
let BRP = CGPoint(x: w - BRR, y: BRR)
//Create path and addComponents
let path = CGMutablePath()
path.addArc(center: TLP, radius: TLR, startAngle: CGFloat.pi, endAngle: CGFloat.pi/2, clockwise: true)
path.addLine(to: CGPoint(x: TRP.x, y: h))
path.addArc(center: TRP, radius: TRR, startAngle: CGFloat.pi/2, endAngle: 0, clockwise: true)
path.addLine(to: CGPoint(x: w, y: BRP.y))
path.addArc(center: BRP, radius: BRR, startAngle: 0, endAngle: -CGFloat.pi/2, clockwise: true)
path.addLine(to: CGPoint(x: BLP.x, y: 0))
path.addArc(center: BLP, radius: BLR, startAngle: -CGFloat.pi/2, endAngle: -CGFloat.pi, clockwise: true)
path.addLine(to: CGPoint(x: 0, y: TLP.y))
return path
}