I can draw a single rect with :
let roundedRect = UIBezierPath(roundedRect: rect, cornerRadius: 50)
with this I can not add a new rect to the same path. (I need multiple rects to a single layer)
I can also just draw a rect by moving the point :
path.move(to: CGPoint(x:point1.x-rectWidth/2.0,y:point1.y) )
path.addLine(to: CGPoint(x: point1.x-rectWidth/2.0, y: point2.y))
path.addLine(to: CGPoint(x: point1.x+rectWidth/2.0, y: point2.y))
path.addLine(to: CGPoint(x: point1.x+rectWidth/2.0, y: point1.y))
path.addLine(to: CGPoint(x:point1.x-rectWidth/2.0,y:point1.y))
which will not be a rounded rect.
Which approach can I use to get a rounded rect ?
Building a rounded rect yourself is somewhat tricky. I suggest creating rounded rect paths using the UIBezierPath initializer init(roundedRect:cornerRadius:)
You can use append(_:) to append rounded rect paths to another path:
var path = UIBezierPath() //Create an empty path
//Add a rounded rect to the path
path.append(UIBezierPath(roundedRect: rect1, cornerRadius: radius1))
//Add another rounded rect to the path
path.append(UIBezierPath(roundedRect: rect2, cornerRadius: radius2))
//Lather, rinse, repeat.
path.append(UIBezierPath(roundedRect: rect3, cornerRadius: radius3))
Edit:
To animate all of your rectangles drawing from the bottom, like an ink-jet printer, create a CAShapeLayer that's the same size as your view, install a zero-height rectangle at the bottom, and make that layer the mask layer for your view's layer. Then create a CABasicAnimation of the rectangle growing to the full height of the view.
Related
I want to draw a dialog bubble so I need 2 objects:
arrow (can be drawn with UIBezierPath only)
rounded rect (can be drawn with UIBezierPath or UIView+corner radius)
Example of code generated by paintcode:
//// Rectangle Drawing
let rectanglePath = UIBezierPath(roundedRect: CGRect(x: 68, y: 38, width: 101, height: 38), cornerRadius: 5)
UIColor.gray.setFill()
rectanglePath.fill()
//// Polygon Drawing
let polygonPath = UIBezierPath()
polygonPath.move(to: CGPoint(x: 118, y: 25.5))
polygonPath.addLine(to: CGPoint(x: 125.36, y: 38.25))
polygonPath.addLine(to: CGPoint(x: 110.64, y: 38.25))
polygonPath.close()
UIColor.gray.setFill()
polygonPath.fill()
The problem is if I add shadow to this code then it is applied twice and may overlap the objects.
I could replace 2 objects with one but the complexity of code is increased significantly (drawing rect and 3 points will be replaced with ~50 points including "control points").
So is it possible to join rectanglePath and polygonPath to draw single shadow somehow without this complexity?
I need to add shadow for view with shape aka trapezoid.
here is my code, and it doesn't work.
[![enter image description here][1]][1]let trapezoidPath = UIBezierPath()
trapezoidPath.move(to: CGPoint(x: 0, y: 0))
trapezoidPath.addLine(to: CGPoint(x: Constants.padding, y: bounds.maxY - 10))
trapezoidPath.addLine(to: CGPoint(x: titleLabel.bounds.width + Constants.padding, y: bounds.maxY - 10))
trapezoidPath.addLine(to: CGPoint(x: titleLabel.bounds.width + 2 * Constants.padding, y: 0))
let trapezoidLayer = CAShapeLayer()
trapezoidLayer.path = trapezoidPath.cgPath
whiteView.layer.shadowPath = UIBezierPath(roundedRect: trapezoidPath.bounds, cornerRadius: whiteView.layer.cornerRadius).cgPath
whiteView.layer.shadowRadius = 15
whiteView.layer.shadowOffset = .zero
whiteView.layer.shadowOpacity = 0.4
whiteView.layer.masksToBounds = false
whiteView.layer.shadowColor = UIColor.red.cgColor
whiteView.layer.mask = trapezoidLayer
Nothing outside your mask, including the shadow, will be drawn.
To fix this you need to create two views. The outer view should be transparent and have the shadow properties set. The inner view gets added as a subview to the outer view and has the layer mask configured.
Basically the inner view draws your shape and content and the outer view around it is only responsible for drawing the shadow.
I have seen this question asked in varying ways on Stack Overflow, but I have not seen an implementation that leverages anything but UIKit to develop an approach. For my use case, I have a CIImage and UIBezierPath (rather, a set of points from a machine learning feature detection model) that I would like to use a "mask" for the CIImage.
For example, I may have an image of a person's face. Subsequently, I may have a UIBezierPath that creates a closed shape around one of their eyes. Ideally, I would like to "crop" my CIImage to encompass the UIBezierPath. I recognize that since the final image will need to be square/rectangle, I would like to have any area outside of the UIBezierPath either black or transparent so it can be used as a mask for a future composition.
At present, I have code set up as such;
func doCrop(image: CIImage) -> CIImage {
// The drawing context (here to show I need to use CIContext for another use later on in this function.
let ciContext = CIContext()
// The original source image
let source = image
// Setup the path
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 400, y: 0))
path.addLine(to: CGPoint(x: 600, y: 200))
path.addLine(to: CGPoint(x: 400, y: 400))
path.addLine(to: CGPoint(x: 200, y: 400))
path.addLine(to: CGPoint(x: 100, y: 200))
path.close()
// Test
let cropped = source.cropped(to: path.bounds)
}
To be honest, I'm really unsure where to go from here. From what I have ascertained online, I see solutions that either implement UIKit to create an image context and draw a path, or solutions that only mask a UIImageView (which is not relevant for my needs, as I will need to draw this into a CIContext and pass through a Metal shader at a later point).
I am trying to create a circular transparent hole into my UIview with the following code, and this code makes the correct hole based on given rect. In my situation I have few rects which are individual and few of them are overlapped,for all those rect which are getting overlapped and not getting transparent holes over intersection rect area because of fill rule kCAFillRuleEvenOdd.How can I modify the same code so it can work for individual rects and all other overlapped rect and create transparent holes into view?
let overlay = UIView(frame: CGRect(x: 0, y: 0, width: width,height: height))
overlay.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
// Create the path.
let path = UIBezierPath(rect: view.frame)
let maskLayer = CAShapeLayer()
maskLayer.frame = view.frame
for rectFrame in rects {
// Create the frame for the circle.
let rect = CGRect(x: rectFrame.origin.x, y: rectFrame.origin.y, width: 38, height: 38)
// Append the circle to the path so that it is subtracted.
path.append(UIBezierPath(ovalIn: rect))
}
maskLayer.fillRule = kCAFillRuleEvenOdd
maskLayer.path = path.cgPath
// Set the mask of the view.
view.layer.mask = maskLayer
self.overlayLayer.addSubview(overlay)
I was wondering if for each line that is added to a CGPath, you can change the properties of it like the lineWidth and strokeColor.
Currently I create the CGPath like this:
let path = CGMutablePath()
points = [CGPoint(x: -372, y: -250), CGPoint(x: 372, y: -250)]
path.move(to: CGPoint(x: points[0].x, y: points[0].y))
//Somewhere here change the properties of the line before adding it
path.addLine(to: CGPoint(x: points[1].x, y: points[1].y))
//Or after here
Line.path = path
Is this possible to individually change the properties of each line added to a CGPath, or does the whole CGPath have only one set color and lineWidth?
It's far worse than you think. The mutable part of CGPath is a lie.