How to add multiple bezier paths into same view swift 3 - ios

I have added a triangle in this way
// draw the line graph
UIColor.white.setFill()
UIColor.white.setStroke()
///--------SETUP FIRST TRIANGLE-----------------------------------------------
//set up the points line
let graphPath = UIBezierPath()
//go to start of line
graphPath.move(to: CGPoint.init(x: rect.minX, y: rect.minY))
graphPath.addLine(to: CGPoint.init(x: (rect.maxX/2.0), y: (rect.maxY/2.0)))
graphPath.addLine(to: CGPoint.init(x: rect.minX, y: rect.maxY))
graphPath.stroke()
//2 - make a copy of the path
let clippingPath = graphPath.copy() as! UIBezierPath
//3 - add lines to the copied path to complete the clip area
clippingPath.addLine(to: CGPoint.init(x: rect.minX, y: rect.minY))
clippingPath.close()
//4 - add the clipping path to the context
clippingPath.addClip()
let startPoint = CGPoint(x:(rect.maxX/2.0), y: (rect.maxY/2.0))
let endPoint = CGPoint(x:rect.minX, y:(rect.maxY/2.0))
let gradient=self.getGradient(tiangleNo: 1)
let context = UIGraphicsGetCurrentContext()
context!.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: 0))
This is drawing a triangle that I wanted. Now I want to draw another triangle. So I did like this.
///------------SECOND TRIANGLE -----------------------------------------------
let graphPath2 = UIBezierPath()
graphPath2.move(to: CGPoint.init(x: rect.minX, y: rect.minY))
graphPath2.addLine(to: CGPoint.init(x: rect.maxX, y: rect.minY))
graphPath2.addLine(to: CGPoint.init(x: (rect.maxX/2.0), y: (rect.minY/2.0)))
graphPath2.stroke()
let oldPath1=clippingPath.copy() as! UIBezierPath
oldPath1.append(graphPath2)
let startPoint2 = CGPoint(x:(rect.maxX/2.0), y: (rect.maxY/2.0))
let endPoint2 = CGPoint(x:(rect.maxX/2.0), y:rect.minY)
let gradient2=self.getGradient(tiangleNo: 2)
let context2 = UIGraphicsGetCurrentContext()
context2!.drawLinearGradient(gradient2, start: startPoint2, end: endPoint2, options: CGGradientDrawingOptions(rawValue: 0))
But this just adding a triangle inside the previous one.rectis the my UIScreen rectangle

Simple Solution
You can write a function like this
func drawTriangle(point1:CGPoint, point2:CGPoint, point3:CGPoint) {
let path = UIBezierPath()
path.move(to: point1)
path.addLine(to: point2)
path.addLine(to: point3)
path.close()
path.lineWidth = 2.0
UIColor.red.setFill()
path.fill()
}
Draw triagles by calling the function
//Triagle 1
let pointA = CGPoint(x: 0, y: 0)
let pointB = CGPoint(x: 100, y: 0)
let pointC = CGPoint(x: 0, y: 100)
drawTriangle(point1: pointA, point2: pointB, point3: pointC)
//Triagle 2
let pointD = CGPoint(x: 120, y: 0)
let pointE = CGPoint(x: 220, y: 0)
let pointF = CGPoint(x: 220, y: 100)
drawTriangle(point1: pointD, point2: pointE, point3: pointF)
You will get something like this,

Related

Curved bottom bar

I want to make curved bottom bar it is not looking perfect round by below code
struct customShape: Shape {
var xAxis : CGFloat
var animatableData: CGFloat{
get { return xAxis}
set { xAxis = newValue}
}
func path(in rect: CGRect) -> Path {
return Path { path in
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: 0, y: rect.height))
let center = xAxis
path.move(to: CGPoint(x: center - 43, y: 0))
let to1 = CGPoint(x: center, y: 50)
let center1 = CGPoint(x: center - 30, y: 0)
let center2 = CGPoint(x: center - 46, y: 42)
let to2 = CGPoint(x: center + 52, y: 0)
let center3 = CGPoint(x: center + 57, y: 47)
let center4 = CGPoint(x: center + 35, y: 0)
path.addCurve(to: to1, control1: center1, control2: center2)
path.addCurve(to: to2, control1: center3, control2: center4)
}
}
}
You are trying to construct a circle with Bezier handles. And it seems you are not calculating the values but guessing and trying. This will be at least very hard if not impossible to solve. Try to add an arc instead of a curve.
Possible implementation:
struct customShape: Shape {
var xAxis : CGFloat
var radius: CGFloat = 40
var animatableData: CGFloat{
get { return xAxis}
set { xAxis = newValue}
}
func path(in rect: CGRect) -> Path {
return Path { path in
path.move(to: CGPoint(x: 0, y: 0))
// add a line to the starting point of the circle
path.addLine(to: CGPoint(x: xAxis - radius, y: 0))
// add the arc with the centerpoint of (xAxis,0) and 180°
path.addArc(center: .init(x: xAxis, y: 0), radius: radius, startAngle: .init(degrees: 180), endAngle: .init(degrees: 0), clockwise: true)
// complete the rectangle
path.addLine(to: .init(x: rect.size.width, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: 0, y: rect.height))
path.closeSubpath()
}
}
}

How to draw a line at the corner of a square with CAShapeLayer in Swift

I am developing a face recognition application of Vision library, and I am having trouble drawing lines with CAShapeLayer
here is the code after getting camera output:
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
return
}
let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: imageBuffer, orientation: .leftMirrored, options: [:])
let faceDetectionRequest = VNDetectFaceLandmarksRequest(completionHandler: { (request: VNRequest, error: Error?) in
DispatchQueue.main.async {
self.faceLayers.forEach({ drawing in drawing.removeFromSuperlayer() })
if let observations = request.results as? [VNFaceObservation] {
for observation in observations {
let faceRectConverted = self.videoPreviewLayer.layerRectConverted(fromMetadataOutputRect: observation.boundingBox)
let faceRectanglePath = CGPath(rect: faceRectConverted, transform: nil)
let faceLayer = CAShapeLayer()
faceLayer.path = faceRectanglePath
faceLayer.fillColor = UIColor.clear.cgColor
faceLayer.strokeColor = UIColor.systemPink.cgColor
self.faceLayers.append(faceLayer)
self.cameraView.layer.addSublayer(faceLayer)
}
}
}
})
do {
try imageRequestHandler.perform([faceDetectionRequest])
} catch {
print(error.localizedDescription)
}
}
result
the problem I am facing when I want to draw a short line at the corner of the square
Thanks for all the support!
You can use this:
let thinLayer = CAShapeLayer()
thinLayer.path = CGPath(rect: rect, transform: nil)
thinLayer.strokeColor = UIColor.purple.cgColor
thinLayer.fillColor = UIColor.clear.cgColor
thinLayer.lineWidth = 1.0
view.layer.addSublayer(thinLayer)
let cornerWidth: CGFloat = 20.0
let topLeftBezierPath = UIBezierPath()
topLeftBezierPath.move(to: CGPoint(x: rect.origin.x, y: rect.origin.y + cornerWidth))
topLeftBezierPath.addLine(to: CGPoint(x: rect.origin.x, y: rect.origin.y))
topLeftBezierPath.addLine(to: CGPoint(x: rect.origin.x + cornerWidth, y: rect.origin.y))
let topRightBezierPath = UIBezierPath()
topRightBezierPath.move(to: CGPoint(x: rect.maxX, y: rect.origin.y + cornerWidth))
topRightBezierPath.addLine(to: CGPoint(x: rect.maxX, y: rect.origin.y))
topRightBezierPath.addLine(to: CGPoint(x: rect.maxX - cornerWidth, y: rect.origin.y))
let bottomRightBezierPath = UIBezierPath()
bottomRightBezierPath.move(to: CGPoint(x: rect.maxX, y: rect.maxY - cornerWidth))
bottomRightBezierPath.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
bottomRightBezierPath.addLine(to: CGPoint(x: rect.maxX - cornerWidth, y: rect.maxY))
let bottomLeftBezierPath = UIBezierPath()
bottomLeftBezierPath.move(to: CGPoint(x: rect.origin.x, y: rect.maxY - cornerWidth))
bottomLeftBezierPath.addLine(to: CGPoint(x: rect.origin.x, y: rect.maxY))
bottomLeftBezierPath.addLine(to: CGPoint(x: rect.origin.x + cornerWidth, y: rect.maxY))
func cornerLayer(with bezierPath: UIBezierPath) -> CAShapeLayer {
let shape = CAShapeLayer()
shape.path = bezierPath.cgPath
shape.lineWidth = 4.0
shape.strokeColor = UIColor.purple.cgColor
shape.fillColor = UIColor.clear.cgColor
shape.lineCap = .round
return shape
}
let topLeftShape = cornerLayer(with: topLeftBezierPath)
view.layer.addSublayer(topLeftShape)
let topRightShape = cornerLayer(with: topRightBezierPath)
view.layer.addSublayer(topRightShape)
let bottomRightShape = cornerLayer(with: bottomRightBezierPath)
view.layer.addSublayer(bottomRightShape)
let bottomLeftShape = cornerLayer(with: bottomLeftBezierPath)
view.layer.addSublayer(bottomLeftShape)
Where:
view is the UIView on which to add the layer, in your case it's cameraView.
rect is the full rect of the face, in your case it's faceRectConverted
customize to fulfill your needs (lineWidh, strokeColor, cornerWidth which might me proportional to the size of the rect?)
Sample in Playground:
func drawing() -> UIView {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = .orange
let rect = CGRect(x: 50, y: 50, width: 200, height: 200)
let thinLayer = CAShapeLayer()
thinLayer.path = CGPath(rect: rect, transform: nil)
thinLayer.strokeColor = UIColor.purple.cgColor
thinLayer.fillColor = UIColor.clear.cgColor
thinLayer.lineWidth = 1.0
view.layer.addSublayer(thinLayer)
let cornerWidth: CGFloat = 20.0
let topLeftBezierPath = UIBezierPath()
topLeftBezierPath.move(to: CGPoint(x: rect.origin.x, y: rect.origin.y + cornerWidth))
topLeftBezierPath.addLine(to: CGPoint(x: rect.origin.x, y: rect.origin.y))
topLeftBezierPath.addLine(to: CGPoint(x: rect.origin.x + cornerWidth, y: rect.origin.y))
let topRightBezierPath = UIBezierPath()
topRightBezierPath.move(to: CGPoint(x: rect.maxX, y: rect.origin.y + cornerWidth))
topRightBezierPath.addLine(to: CGPoint(x: rect.maxX, y: rect.origin.y))
topRightBezierPath.addLine(to: CGPoint(x: rect.maxX - cornerWidth, y: rect.origin.y))
let bottomRightBezierPath = UIBezierPath()
bottomRightBezierPath.move(to: CGPoint(x: rect.maxX, y: rect.maxY - cornerWidth))
bottomRightBezierPath.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
bottomRightBezierPath.addLine(to: CGPoint(x: rect.maxX - cornerWidth, y: rect.maxY))
let bottomLeftBezierPath = UIBezierPath()
bottomLeftBezierPath.move(to: CGPoint(x: rect.origin.x, y: rect.maxY - cornerWidth))
bottomLeftBezierPath.addLine(to: CGPoint(x: rect.origin.x, y: rect.maxY))
bottomLeftBezierPath.addLine(to: CGPoint(x: rect.origin.x + cornerWidth, y: rect.maxY))
func cornerLayer(with bezierPath: UIBezierPath) -> CAShapeLayer {
let shape = CAShapeLayer()
shape.path = bezierPath.cgPath
shape.lineWidth = 4.0
shape.strokeColor = UIColor.purple.cgColor
shape.fillColor = UIColor.clear.cgColor
shape.lineCap = .round
return shape
}
let topLeftShape = cornerLayer(with: topLeftBezierPath)
view.layer.addSublayer(topLeftShape)
let topRightShape = cornerLayer(with: topRightBezierPath)
view.layer.addSublayer(topRightShape)
let bottomRightShape = cornerLayer(with: bottomRightBezierPath)
view.layer.addSublayer(bottomRightShape)
let bottomLeftShape = cornerLayer(with: bottomLeftBezierPath)
view.layer.addSublayer(bottomLeftShape)
return view
}
let drawn = drawing()
drawn
Output:

Add curve on arc swift

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
}

Add two paths together using UIBezierPath in Swift

I have 2 lines drawn using UIBezierPath, how can i combine point to form a shape inform of letter X. I want to join both lines together
Path 1
let path1 = UIBezierPath()
path1.move(to: .zero)
path1.addLine(to: CGPoint(x: 100, y: 100))
path1.close()
path1.lineWidth = 1.0
UIColor.blue.set()
path1.stroke()
path1.fill()
Path 2
let path2 = UIBezierPath()
path2.move(to: .zero)
path2.addLine(to: CGPoint(x: 50, y: 50))
path2.close()
path2.lineWidth = 1.0
UIColor.red.set()
path2.stroke()
path2.fill()
You should use proper CGPoint values
If your view bounds value is (0,0,100,100)
Move to (0,0)
Addline to (100,100)
Mode to (0,100)
Addline to (100,0)
Try this
let path = UIBezierPath()
path.move(to: .zero)
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
path.move(to: CGPoint(x: bounds.minX, y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
path.close()
path.lineWidth = 1.0
UIColor.blue.set()
path.stroke()
path.fill()

UIBezierPath Creating Triangle,Square,Circle Swift

I am attempting to create three shapes: a Circle, Square & Triangle. I have created the circle & square, but am unable to create the triangle. My biggest issue is to keep all three shapes in the center of the screen. The circle and square are fine, but when I attempt to make the triangle it does not work. I am also trying to make the triangle look like a "play button" so that the "tip" of the triangle is facing to the right. Here is the code.
func trainglePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
let path = UIBezierPath()
let startX = center.x - side / 2
let startY = center.y - side / 2
path.move(to: CGPoint(x: startX, y: startY))
path.addLine(to: CGPoint(x: startX, y: startY - side))
path.addLine(to: CGPoint(x: startX + side, y: startY + side/2))
path.close()
return path
}
func circlePathWithCenter(center: CGPoint, radius: CGFloat) -> UIBezierPath {
let circlePath = UIBezierPath()
circlePath.addArc(withCenter: center, radius: radius, startAngle: -CGFloat(M_PI), endAngle: -CGFloat(M_PI/2), clockwise: true)
circlePath.addArc(withCenter: center, radius: radius, startAngle: -CGFloat(M_PI/2), endAngle: 0, clockwise: true)
circlePath.addArc(withCenter: center, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI/2), clockwise: true)
circlePath.addArc(withCenter: center, radius: radius, startAngle: CGFloat(M_PI/2), endAngle: CGFloat(M_PI), clockwise: true)
circlePath.close()
return circlePath
}
func squarePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
let squarePath = UIBezierPath()
let startX = center.x - side / 2
let startY = center.y - side / 2
squarePath.move(to: CGPoint(x: startX, y: startY))
squarePath.addLine(to: squarePath.currentPoint)
squarePath.addLine(to: CGPoint(x: startX + side, y: startY))
squarePath.addLine(to: squarePath.currentPoint)
squarePath.addLine(to: CGPoint(x: startX + side, y: startY + side))
squarePath.addLine(to: squarePath.currentPoint)
squarePath.addLine(to: CGPoint(x: startX, y: startY + side))
squarePath.addLine(to: squarePath.currentPoint)
squarePath.close()
return squarePath
}
Where am I going wrong with the geometry for the triangle?
You subtracted when you need to add. Remember, +Y is down.
Change:
path.addLine(to: CGPoint(x: startX, y: startY - side))
To:
path.addLine(to: CGPoint(x: startX, y: startY + side))
Here it is running in a Playground:
Here's the full code for the Playground demo:
class Custom: UIView {
override func draw(_ rect: CGRect) {
let path = trainglePathWithCenter(center: self.center, side: self.bounds.width / 2)
path.stroke()
}
func trainglePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
let path = UIBezierPath()
let startX = center.x - side / 2
let startY = center.y - side / 2
path.move(to: CGPoint(x: startX, y: startY))
path.addLine(to: CGPoint(x: startX, y: startY + side))
path.addLine(to: CGPoint(x: startX + side, y: startY + side/2))
path.close()
return path
}
}
let custom = Custom(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
custom.backgroundColor = .white
Result triangles
Code in swift5
//TriangleView
extension UIView {
func setRightTriangle(targetView:UIView?){
let heightWidth = targetView!.frame.size.width //you can use triangleView.frame.size.height
let path = CGMutablePath()
path.move(to: CGPoint(x: heightWidth/2, y: 0))
path.addLine(to: CGPoint(x:heightWidth, y: heightWidth/2))
path.addLine(to: CGPoint(x:heightWidth/2, y:heightWidth))
path.addLine(to: CGPoint(x:heightWidth/2, y:0))
let shape = CAShapeLayer()
shape.path = path
shape.fillColor = UIColor.blue.cgColor
targetView!.layer.insertSublayer(shape, at: 0)
}
func setLeftTriangle(targetView:UIView?){
let heightWidth = targetView!.frame.size.width
let path = CGMutablePath()
path.move(to: CGPoint(x: heightWidth/2, y: 0))
path.addLine(to: CGPoint(x:0, y: heightWidth/2))
path.addLine(to: CGPoint(x:heightWidth/2, y:heightWidth))
path.addLine(to: CGPoint(x:heightWidth/2, y:0))
let shape = CAShapeLayer()
shape.path = path
shape.fillColor = UIColor.blue.cgColor
targetView!.layer.insertSublayer(shape, at: 0)
}
func setUpTriangle(targetView:UIView?){
let heightWidth = targetView!.frame.size.width
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: heightWidth))
path.addLine(to: CGPoint(x:heightWidth/2, y: heightWidth/2))
path.addLine(to: CGPoint(x:heightWidth, y:heightWidth))
path.addLine(to: CGPoint(x:0, y:heightWidth))
let shape = CAShapeLayer()
shape.path = path
shape.fillColor = UIColor.blue.cgColor
targetView!.layer.insertSublayer(shape, at: 0)
}
func setDownTriangle(targetView:UIView?){
let heightWidth = targetView!.frame.size.width
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x:heightWidth/2, y: heightWidth/2))
path.addLine(to: CGPoint(x:heightWidth, y:0))
path.addLine(to: CGPoint(x:0, y:0))
let shape = CAShapeLayer()
shape.path = path
shape.fillColor = UIColor.blue.cgColor
targetView!.layer.insertSublayer(shape, at: 0)
}
}

Resources