I Have tried to Center CAShapeLayer in UIImageView but didn't succeed and I have find solution that I have added it(CAShapeLayer) to UIVIew and then Add UIVIew to ImageView and its worked !!!
Is this good practices ? any suggestion
func Draw(Fn:Bool) {
let theCenter = CGPoint(x: img.bounds.midX, y: img.bounds.midY)
// Create Layer To Add The CAShapeLayer On it So I can Center it
let DrawingLayer = UIView(frame: CGRect(x: img.bounds.midX, y: img.bounds.midY, width: 140, height: 80))
DrawingLayer.center = theCenter
img.addSubview(DrawingLayer)
// Create The Drawing
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 140, height: 80)).cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 3.0
// Add Drawing to UIView
DrawingLayer.layer.addSublayer(shapeLayer)
// Add UIView To ImageView
img.addSubview(DrawingLayer)
}
I was using this code :
func DrawArmor(Fn:Bool) {
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 140, height: 80)).cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 3.0
if (Fn) {
shapeLayer.name = "Armor Friendly"
shapeLayer.strokeColor = UIColor.blue.cgColor
}else{
shapeLayer.name = "ArmorEnemy"
shapeLayer.strokeColor = UIColor.red.cgColor
}
img.layer.addSublayer(shapeLayer)
}
but the Drawing showing on right bottom corner which mean the ancherpoint is not the center
Find a solution
shapeLayer.frame = CGRect(x: img.bounds.midX, y: img.bounds.midY, width: 140, height: 80)
shapeLayer.position = CGPoint(x: img.bounds.midX, y: img.bounds.midY)
I have set a size of the CAShapeLayer and then set the position for it and its work
Related
How can i draw custom oval shape like in below image in swift (not swiftUI).
Thank you in advance
I have tried to clone similar view using UIView and was able to create similar UI as you have stated on screenshot
And here is my code snippet
let width: CGFloat = view.frame.size.width
let height: CGFloat = view.frame.size.height
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height), cornerRadius: 0)
let rect = CGRect(x: width / 2 - 150, y: height / 2.5 - 100, width: 300, height: 400)
let circlePath = UIBezierPath(ovalIn: rect)
path.append(circlePath)
path.usesEvenOddFillRule = true
let fillLayer = CAShapeLayer()
fillLayer.path = path.cgPath
fillLayer.fillRule = .evenOdd
fillLayer.fillColor = UIColor.red.cgColor
fillLayer.opacity = 0.5
view.layer.addSublayer(fillLayer)
Hope this helps you. Good day.
You can draw oval path like this
class CustomOval: UView {
override func drawRect(rect: CGRect) {
var ovalPath = UIBezierPath(ovalIn: rect)
UIColor.gray.setFill()
ovalPath.fill()
}
}
I created a DrawView where it's possible to draw. I created an instance of UIBezierPath for drawing.
// This function is called when the user has finished drawing.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let shapeLayer = CAShapeLayer()
//The CAShapeLayer has the same path of the drawing (currentPath is the instance of UIBezierPath).
shapeLayer.path = currentPath?.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 10.0
//Here I set the shapeLayer on the drawingView (the blue one that you can see in the image)
drawingView.layer.addSublayer(shapeLayer)
shapeLayer.position = CGPoint(x: drawingView.layer.bounds.midX, y: drawingView.layer.bounds.midY)
shapeLayer.backgroundColor = UIColor.blue.cgColor
shapeLayer.frame = drawingView.layer.bounds
}
The problem is that the path (e.g number 3 in the image) is not centered on its shapeLayer.
shapeLayer.path?.boundingBoxOfPath = (289.5, 349.5, 525.0, 129.0)
shapeLayer.frame = (0.0, 0.0, 200.0, 200.0)
shapeLayer.position = (100.0, 100.0)
drawingView.frame = (0.0, 912.0, 200.0, 200.0)
Any hint? thank you
Forget the drawing view and just think about how to center a path in a shape layer of known size. Here's a deliberate failure:
let path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 50, height: 50))
let lay = CAShapeLayer()
lay.frame = CGRect(x: 40, y: 40, width: 200, height: 200)
lay.backgroundColor = UIColor.red.cgColor
lay.path = path.cgPath
self.view.layer.addSublayer(lay)
We get this:
The filled circle is not centered in the red shape layer. Okay, we know why, because we know the bezier path that created the filled circle. But let's say we don't know that. We can still center the shape in the shape layer, like this:
let path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 50, height: 50))
let lay = CAShapeLayer()
lay.frame = CGRect(x: 40, y: 40, width: 200, height: 200)
lay.backgroundColor = UIColor.red.cgColor
let cgpath = path.cgPath
let box = cgpath.boundingBoxOfPath
let xtarget = (lay.bounds.width - box.width)/2
let ytarget = (lay.bounds.height - box.height)/2
let xoffset = xtarget - box.minX
let yoffset = ytarget - box.minY
var transform = CGAffineTransform(translationX: xoffset, y: yoffset)
let cgpath2 = cgpath.copy(using: &transform)
lay.path = cgpath2
self.view.layer.addSublayer(lay)
Result:
So, given a CGPath, and given a shape layer whose final bounds are known, you can use that technique to center the path in the shape layer. And those are circumstances that apply perfectly to your use case.
I want make view this Bottom roundedenter image description here
I think this is really close to what you want:
The rectView is your initial view;
You can play with the + 50 of quad curve height to adjust the curve
let rectView = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 200))
rectView.backgroundColor = .red
view.addSubview(rectView)
let arcBezierPath = UIBezierPath()
arcBezierPath.move(to: CGPoint(x: 0, y: rectView.frame.height))
arcBezierPath.addQuadCurve(to: CGPoint(x: rectView.frame.width, y: rectView.frame.height), controlPoint: CGPoint(x: rectView.frame.width / 2 , y: rectView.frame.height + 50 ))
arcBezierPath.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = arcBezierPath.cgPath
shapeLayer.fillColor = UIColor.red.cgColor
rectView.layer.insertSublayer(shapeLayer, at: 0)
rectView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
rectView.layer.cornerRadius = 10
rectView.layer.shadowRadius = 3
rectView.layer.shadowColor = UIColor.black.cgColor
rectView.layer.shadowOffset = CGSize(width: 0, height: 0)
rectView.layer.shadowOpacity = 0.25
Maybe, even better:
You can create your view:
let rectView = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 200))
view.addSubview(rectView)
Extend UIView
extension UIView {
func addBottomArc(ofHeight height: CGFloat, topCornerRadius: CGFloat) {
let arcBezierPath = UIBezierPath()
arcBezierPath.move(to: CGPoint(x: 0, y: frame.height))
arcBezierPath.addQuadCurve(to: CGPoint(x: frame.width, y: frame.height), controlPoint: CGPoint(x: frame.width / 2 , y: frame.height + height ))
arcBezierPath.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = arcBezierPath.cgPath
shapeLayer.fillColor = UIColor.red.cgColor
layer.insertSublayer(shapeLayer, at: 0)
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
layer.cornerRadius = topCornerRadius
}
}
So you can set the height and the corner radius like this:
rectView.addBottomArc(ofHeight: 50, topCornerRadius: 15)
and than add the shadow you need to your view:
rectView.layer.shadowRadius = 3
rectView.layer.shadowColor = UIColor.black.cgColor
rectView.layer.shadowOffset = CGSize(width: 0, height: 0)
rectView.layer.shadowOpacity = 0.25
I'm trying to make an image round. I always used maskCircle but this time it does not work. I wrote the code in UICollectionViewCell's awakeFromNib and I can not use viewDidLayoutSubviews since I'm dequeueing the cell in cellForItemAt function. So this is the code:
#IBOutlet weak var storeLogo: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
storeLogo.maskCircle()
storeLogo.clipsToBounds = true
}
and the imageView looks like this:
imageView
edit:
it seemed to work fine, but i have the problem again.
this is the code in awakeFromNib now:
super.awakeFromNib()
storeLogo.clipsToBounds = true
storeLogo.layer.cornerRadius = storeLogo.frame.width / 2
let gradient = CAGradientLayer()
gradient.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 55, height: 55))
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
gradient.colors = [Color.storeName?.cgColor, Color.idTint?.cgColor]
let shape = CAShapeLayer()
shape.lineWidth = 2
let radius: CGFloat = 27
shape.path = UIBezierPath(roundedRect: CGRect(x: -2.5, y: -14, width: 2.0 * radius, height: 2.0 * radius), cornerRadius: radius).cgPath
shape.position = CGPoint(x: self.frame.midX - radius, y: self.frame.midY - radius)
shape.strokeColor = UIColor.black.cgColor
shape.fillColor = UIColor.clear.cgColor
gradient.mask = shape
self.storeLogo.layer.addSublayer(gradient)
and the image is not round as you can see in this image: imageView
i
i don't know why the image is not a circle now. any idea?!
If you want to do round clip on UIImageView set cornerRadius of UIImageView.
storeLogo.layer.cornerRadius = storeLogo.frame.width / 2
storeLogo.clipsToBounds = true
myLabel.layer.borderWidth = 0.5
myLabel.layer.borderColor = UIColor.green.cgColor
Thats a simple answer of drawing a border. However the issue is I need to draw the border only to the right and bottom. I also need another label to draw border on top and right. How can I draw border to specific sides, not all 4 sides?
Add a shape layer and draw the lines in that layer. Note that layers do not participate in auto layout so you need to put your code in viewDidLayoutSubviews or subclass uilabel and do this in layout subviews. Here is a playground example using the UILabel subclass:
import PlaygroundSupport
import UIKit
class L: UILabel {
var strokeColor = UIColor.blue
var strokeWidth = CGFloat(0.5)
private lazy var labelBorderLayer:CAShapeLayer = {
let shapeLayer = CAShapeLayer()
self.layer.addSublayer(shapeLayer)
return shapeLayer
}()
override func layoutSubviews() {
super.layoutSubviews()
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: bounds.size.height))
path.addLine(to: CGPoint(x: bounds.size.width, y: bounds.size.height))
path.addLine(to: CGPoint(x: bounds.size.width, y: 0))
labelBorderLayer.path = path
labelBorderLayer.strokeColor = strokeColor.cgColor
labelBorderLayer.lineWidth = strokeWidth
labelBorderLayer.fillColor = UIColor.clear.cgColor
}
}
let v = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
let l = L(frame: v.frame.insetBy(dx: 50, dy: 80))
v.addSubview(l)
l.textColor = .white
l.textAlignment = .center
l.text = "gjgkjgjgjgj"
v.backgroundColor = .red
PlaygroundPage.current.liveView = v
Im not sure if you can do that with the actual label border. But but you can do something similar to this
public enum UIButtonBorderSide {
case Top, Bottom, Left, Right
}
extension UIButton {
public func addBorder(side: UIButtonBorderSide, color: UIColor, width: CGFloat) {
let border = CALayer()
border.backgroundColor = color.CGColor
switch side {
case .Top:
border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: width)
case .Bottom:
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width)
case .Left:
border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
case .Right:
border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height)
}
self.layer.addSublayer(border)
}
}
Thats for UIButton, But you can probably adapt it easily.
Source: https://gist.github.com/Isuru-Nanayakkara/496d5713e61125bddcf5
Hope this helps