I want to create image view like this.
With Bezier path, the code i tried is
let path = UIBezierPath()
path.move(to: CGPoint(x: myImage.frame.size.width - 10 , y: myImage.frame.size.height))
path.addLine(to: CGPoint(x: myImage.frame.size.width - 20 , y: 20))
path.addLine(to: CGPoint(x: myImage.frame.size.width, y: 20))
path.addArc(withCenter: CGPoint(x: myImage.frame.size.width/2, y: myImage.frame.size.height/2), radius: myImage.frame.size.width/2, startAngle:-CGFloat(M_PI_2), endAngle: CGFloat(M_PI_2), clockwise: false)
//
path.move(to: CGPoint(x: myImage.frame.size.width/2, y: myImage.frame.size.height))
path.close()
UIColor.red.setFill()
path.stroke()
path.reversing()
code
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 153.5, y: 137.5))
bezierPath.addCurve(to: CGPoint(x: 63.5, y: 196.5), controlPoint1: CGPoint(x: 130.5, y: 137.5), controlPoint2: CGPoint(x: 63.5, y: 120.37))
bezierPath.addCurve(to: CGPoint(x: 63.5, y: 310.5), controlPoint1: CGPoint(x: 63.5, y: 260.99), controlPoint2: CGPoint(x: 63.5, y: 218.5))
bezierPath.addCurve(to: CGPoint(x: 88.5, y: 352.5), controlPoint1: CGPoint(x: 63.5, y: 333.5), controlPoint2: CGPoint(x: 73.5, y: 347.5))
bezierPath.addCurve(to: CGPoint(x: 218.5, y: 360.5), controlPoint1: CGPoint(x: 137.23, y: 368.74), controlPoint2: CGPoint(x: 218.5, y: 360.5))
bezierPath.addCurve(to: CGPoint(x: 240.5, y: 309.5), controlPoint1: CGPoint(x: 218.5, y: 360.5), controlPoint2: CGPoint(x: 240.5, y: 337))
bezierPath.addCurve(to: CGPoint(x: 222.5, y: 249.5), controlPoint1: CGPoint(x: 240.5, y: 282), controlPoint2: CGPoint(x: 222.5, y: 249.5))
bezierPath.addCurve(to: CGPoint(x: 240.5, y: 196.5), controlPoint1: CGPoint(x: 222.5, y: 249.5), controlPoint2: CGPoint(x: 240.5, y: 224.5))
bezierPath.addCurve(to: CGPoint(x: 218.5, y: 137.5), controlPoint1: CGPoint(x: 240.5, y: 168.5), controlPoint2: CGPoint(x: 218.5, y: 137.5))
bezierPath.addCurve(to: CGPoint(x: 153.5, y: 137.5), controlPoint1: CGPoint(x: 218.5, y: 137.5), controlPoint2: CGPoint(x: 201.5, y: 137.5))
bezierPath.close()
UIColor.gray.setFill()
bezierPath.fill()
UIColor.black.setStroke()
bezierPath.lineWidth = 1
bezierPath.stroke()
output
Related
I need to create this custom UIView for my project.I need to achieve this
I was doing trial and error by creating, My own custom UIView and then drawing on it.
I'm working on this
My Question is, if I'm able to draw my custom shape, How will I clip-off the remaining white space of the UIView
func drawCustomShape() {
let viewSize = self.frame.size
let buttonSlotRadius = CGFloat(30)
let effectiveViewHeight = viewSize.height - buttonSlotRadius
let path = UIBezierPath()
path.move(to: CGPoint(x: 30, y: 0))
path.addLine(to: CGPoint(x: viewSize.width - 30, y: 0))
path.addArc(withCenter: CGPoint(x: viewSize.width - 30,
y: 30),
radius: 30,
startAngle: .pi * 3 / 2,
endAngle: 0,
clockwise: true)
path.addLine(to: CGPoint(x: viewSize.width, y: effectiveViewHeight))
path.addArc(withCenter: CGPoint(x: viewSize.width - 30,
y: effectiveViewHeight - 30),
radius: 30,
startAngle: 0,
endAngle: .pi / 2,
clockwise: true)
path.addLine(to: CGPoint(x: viewSize.width / 4, y: effectiveViewHeight))
// close path join to origin
path.close()
UIColor.secondarySystemFill.setFill()
path.fill()
path.lineWidth = 6.0
path.stroke()
}
You can use UIView with clear background color, and use CAShapeLayer with desired path like this:
extension UIView {
func drawCustomShape() {
let cornerRadius: CGFloat = 18.0
let shapeOffset = self.frame.size.height * 0.2
//create shape layer
let shapeLayer = CAShapeLayer()
shapeLayer.frame = self.bounds
shapeLayer.lineWidth = 1.0
shapeLayer.fillColor = UIColor.white.cgColor
self.layer.addSublayer(shapeLayer)
//create path
let path = UIBezierPath()
//top left point
path.move(to: CGPoint(x: 0, y: cornerRadius))
//top left corner
path.addQuadCurve(to: CGPoint(x: cornerRadius, y: 0),
controlPoint: CGPoint(x: 0, y: 0))
//top right point
path.addLine(to: CGPoint(x: self.frame.size.width - cornerRadius, y: 0))
//top right corner
path.addQuadCurve(to: CGPoint(x: self.frame.size.width, y: cornerRadius),
controlPoint: CGPoint(x: self.frame.size.width, y: 0))
//bottom right point
path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height - shapeOffset - cornerRadius))
//bottom right corner
path.addQuadCurve(to: CGPoint(x: self.frame.size.width - cornerRadius, y: self.frame.size.height - shapeOffset),
controlPoint: CGPoint(x: self.frame.size.width, y: self.frame.size.height - shapeOffset))
//bottom left
path.addLine(to: CGPoint(x: cornerRadius, y: self.frame.size.height))
//bottom left corner
path.addQuadCurve(to: CGPoint(x: 0, y: self.frame.size.height - cornerRadius),
controlPoint: CGPoint(x: 0, y: self.frame.size.height))
path.close()
shapeLayer.path = path.cgPath
}
Usage:
myView.drawCustomShape()
Result:
I have been working on creating this UI.But not able to achieve that upper slant, I tried tweaking around my Code,
By Doing that, UI messes up.
Any Help would be appreciated.
let buttonSlotRadius = CGFloat(50)
let path = UIBezierPath()
path.move(to: CGPoint(x: self.layer.frame.width - buttonSlotRadius , y: 0))
path.addArc(withCenter: CGPoint(x: self.layer.frame.width - buttonSlotRadius, y: buttonSlotRadius), radius: buttonSlotRadius, startAngle: 3 * .pi / 2, endAngle: 2 * .pi, clockwise: true)
path.addLine(to: CGPoint(x: buttonSlotRadius, y: 2 * buttonSlotRadius))
path.addArc(withCenter: CGPoint(x: buttonSlotRadius, y: 2 * buttonSlotRadius), radius: buttonSlotRadius, startAngle: .pi, endAngle: 3 * .pi / 2, clockwise: true)
path.addLine(to: CGPoint(x: self.layer.frame.width, y: 50))
path.addLine(to: CGPoint(x: self.layer.frame.width, y: self.layer.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.layer.frame.height))
path.addLine(to: CGPoint(x: 0, y: 2 * buttonSlotRadius))
path.addLine(to: CGPoint(x: self.layer.frame.width - buttonSlotRadius, y: 0))
path.close()
UIColor.white.setFill()
path.fill()
I figured It Out.
I added one more layer to Fix that
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 100))
path.addLine(to: CGPoint(x: 0.0, y: self.bounds.height))
path.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.height))
path.addLine(to: CGPoint(x: self.bounds.width, y: 50))
path.close()
UIColor.white.setFill()
path.fill()
And Now its Working.Working UI
Im attempting to animate a UIView along an oval using a bezier curve. The code works except that there is a delay between each loop of the animation. I'm at a loss as to what is causing this.
let myView = UIView()
myView.backgroundColor = UIColor.blue
myView.frame = CGRect(x: 0, y:0, width: 30, height: 30)
let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 200, height: 200))
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = path.cgPath
animation.repeatCount = 1000
animation.duration = 1
myView.layer.add(animation, forKey: "animate along path")
Found a solution. The pause between animation loops only takes place when making a path with the UIBezierPath(ovalIn:) constructor. There's no delay when manually making your own path. So I made my own oval.
Replaced this
let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 200, height: 200))
with this
let rect = CGRect(x: 0, y: 0, width: 200, height: 200)
let path = UIBezierPath()
path.move(to: CGPoint(x: rect.maxX, y: rect.midY))
path.addQuadCurve(to: CGPoint(x: rect.midX, y: rect.maxY), controlPoint: CGPoint(x: rect.maxX, y: rect.maxY))
path.addQuadCurve(to: CGPoint(x: rect.minX, y: rect.midY), controlPoint: CGPoint(x: rect.minX, y: rect.maxY))
path.addQuadCurve(to: CGPoint(x: rect.midX, y: rect.minY), controlPoint: CGPoint(x: rect.minX, y: rect.minY))
path.addQuadCurve(to: CGPoint(x: rect.maxX, y: rect.midY), controlPoint: CGPoint(x: rect.maxX, y: rect.minY))
I am try draw shape using UIBezietPath but each time I am have trouble with "Curve".
For solving my problem I did use Paint Code, code was be generated well, but it is a static shape , and I try translate this code for dynamic screen size, but each time I have trouble with half of shape.
Can anyone help me translate this code correctly?
Generated code by Paint Code:
//// Color Declarations
let fillColor = NSColor(red: 0.118, green: 0.118, blue: 0.149, alpha: 1)
//// Bezier Drawing
let bezierPath = NSBezierPath()
bezierPath.move(to: NSPoint(x: 8.83, y: 89))
bezierPath.line(to: NSPoint(x: 154.29, y: 89))
bezierPath.curve(to: NSPoint(x: 163.01, y: 81.04), controlPoint1: NSPoint(x: 158.71, y: 89), controlPoint2: NSPoint(x: 162.21, y: 85.39))
bezierPath.curve(to: NSPoint(x: 206.45, y: 45.05), controlPoint1: NSPoint(x: 166.77, y: 60.57), controlPoint2: NSPoint(x: 184.79, y: 45.05))
bezierPath.curve(to: NSPoint(x: 249.89, y: 81.04), controlPoint1: NSPoint(x: 228.11, y: 45.05), controlPoint2: NSPoint(x: 246.12, y: 60.57))
bezierPath.curve(to: NSPoint(x: 258.61, y: 89), controlPoint1: NSPoint(x: 250.68, y: 85.39), controlPoint2: NSPoint(x: 254.19, y: 89))
bezierPath.line(to: NSPoint(x: 405.17, y: 89))
bezierPath.curve(to: NSPoint(x: 414, y: 80.21), controlPoint1: NSPoint(x: 410.05, y: 89), controlPoint2: NSPoint(x: 414, y: 85.06))
bezierPath.line(to: NSPoint(x: 414, y: 8.79))
bezierPath.curve(to: NSPoint(x: 405.17, y: 0), controlPoint1: NSPoint(x: 414, y: 3.94), controlPoint2: NSPoint(x: 410.05, y: 0))
bezierPath.line(to: NSPoint(x: 8.83, y: 0))
bezierPath.curve(to: NSPoint(x: 0, y: 8.79), controlPoint1: NSPoint(x: 3.95, y: 0), controlPoint2: NSPoint(x: 0, y: 3.94))
bezierPath.line(to: NSPoint(x: 0, y: 80.21))
bezierPath.curve(to: NSPoint(x: 8.83, y: 89), controlPoint1: NSPoint(x: 0, y: 85.06), controlPoint2: NSPoint(x: 3.95, y: 89))
bezierPath.close()
fillColor.setFill()
bezierPath.fill()
Shape:
First extend CGPoint and create a scaled(by:) method:
extension CGPoint {
func scaled(by value: CGFloat) -> CGPoint { applying(.init(scaleX: value, y: value)) }
}
Then get the actual superview's bounds width and divide by the width of your path 414.
let value: CGFloat = superview!.bounds.size.width / 414
Now you just need to apply the scale value to every point of your path:
bezierPath.move(to: NSPoint(x: 8.83, y: 89).scaled(by: value))
I want to cast a shadow to the lower left AND the lower right of my Image. But I'm struggling to get the second shadow cast (layer2)...
I've created a subclass of UIImage view and use that in IB:
import UIKit
class ShadowImageView: UIImageView {
var layer2 = CALayer()
override func layoutSubviews() {
super.layoutSubviews()
self.layer2 = CALayer(layer: self.layer)
self.clipsToBounds = false
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowOffset = CGSize(width: -16, height: 16)
self.layer.shadowOpacity = 0.33
self.layer.shadowRadius = 3
self.layer.masksToBounds = false
self.layer2.shadowColor = UIColor.gray.cgColor
self.layer2.shadowOffset = CGSize(width: 16, height: 16)
self.layer2.shadowOpacity = 0.33
self.layer2.shadowRadius = 3
self.layer2.masksToBounds = false
self.layer.insertSublayer(self.layer2, at: 0)
print("\(self.layer)\n")
if let sublayers = self.layer.sublayers {
for layer in sublayers {
print("\(layer)\n")
}
}
}
}
Am I going in the right direction to copy the layer and apply a new shadow to it or should I look in a different direction?
Fixed the shadow problem. I was able to generate double shadows by adding 3 layers on my UIView. The furthest behind (the first sublayer of the view's layer) is the darker shadow, the one on top of that is the lighter shadow and on top of that I have one with the normal color.
I'm 'drawing' the images on the layers by applying a shadow path & color.
The image I'm drawing is coming from a method like listed below, but it's not being generated from a SFSymbols. I would like to generate it during runtime so I end up with a simple method of using symbols to place Neumorphic elements in our apps. The manual generation of these BezierPaths isn't really pleasant.
static var shieldFill: UIBezierPath = {
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 58.94, y: 115.53))
bezierPath.addCurve(to: CGPoint(x: 62.84, y: 114.4), controlPoint1: CGPoint(x: 60.06, y: 115.53), controlPoint2: CGPoint(x: 61.57, y: 115.04))
bezierPath.addCurve(to: CGPoint(x: 101.76, y: 74.46), controlPoint1: CGPoint(x: 91.5, y: 100.49), controlPoint2: CGPoint(x: 101.76, y: 91.89))
bezierPath.addLine(to: CGPoint(x: 101.76, y: 39.94))
bezierPath.addCurve(to: CGPoint(x: 92.04, y: 25.05), controlPoint1: CGPoint(x: 101.76, y: 31.84), controlPoint2: CGPoint(x: 99.41, y: 28.12))
bezierPath.addCurve(to: CGPoint(x: 67.63, y: 16.85), controlPoint1: CGPoint(x: 87.89, y: 23.29), controlPoint2: CGPoint(x: 71.29, y: 17.97))
bezierPath.addCurve(to: CGPoint(x: 58.94, y: 15.53), controlPoint1: CGPoint(x: 64.94, y: 16.11), controlPoint2: CGPoint(x: 61.52, y: 15.53))
bezierPath.addCurve(to: CGPoint(x: 50.24, y: 16.85), controlPoint1: CGPoint(x: 56.35, y: 15.53), controlPoint2: CGPoint(x: 52.93, y: 16.11))
bezierPath.addCurve(to: CGPoint(x: 25.83, y: 25.05), controlPoint1: CGPoint(x: 46.53, y: 17.97), controlPoint2: CGPoint(x: 29.93, y: 23.29))
bezierPath.addCurve(to: CGPoint(x: 16.11, y: 39.94), controlPoint1: CGPoint(x: 18.46, y: 28.12), controlPoint2: CGPoint(x: 16.11, y: 31.84))
bezierPath.addLine(to: CGPoint(x: 16.11, y: 74.46))
bezierPath.addCurve(to: CGPoint(x: 55.03, y: 114.4), controlPoint1: CGPoint(x: 16.11, y: 91.89), controlPoint2: CGPoint(x: 26.46, y: 100.29))
bezierPath.addCurve(to: CGPoint(x: 58.94, y: 115.53), controlPoint1: CGPoint(x: 56.3, y: 115.04), controlPoint2: CGPoint(x: 57.76, y: 115.53))
bezierPath.close()
return bezierPath
}()
I was able to get this very easily:
That seems to be the sort of thing you're after.
The double shadow stems from the fact that in a graphics context, shadow drawing is a state applied to any future drawing. So if you set the context's shadow to be down-and-to-the-left and draw the symbol image, and then set the context's shadow to be down-and-to-the-right and draw the symbol image, you get the double shadow.
It is true that the symbol image itself is drawn twice but that does not necessarily change the way it appears; if I hadn't told you I'd drawn it twice, I don't think you'd have known.