Swift 3 (SpriteKit): Change the properties of each line in CGPath - swift

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.

Related

Single shadow for multiple objects in Swift?

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?

ARKit Drawing 3D paper plane with rounded corner

I'm trying to draw 3D paper plane with rounded corner in ARKit, but I can't do that.
I did that with bazier path:
// create bezeir path
let path = UIBezierPath()
// A bezier path
path.move(to: CGPoint(x: 0, y: 0.025))
path.addLine(to: CGPoint(x: 0.02, y: -0.005))
path.addLine(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: -0.02, y: -0.005))
path.close()
// create arrow shape
let arrowShape = SCNShape(path: path, extrusionDepth: 0.001)
arrowShape.chamferRadius = 50
// create new node
arrownode = SCNNode(geometry: arrowShape);
// set arrow color
arrownode!.geometry?.firstMaterial?.diffuse.contents = UIColor.yellow
The result :
I need like this exactly but with rounded corner.
An instance property chamferRadius creates chamfer for extruded zDepth, not for XY path.
let arrowShape = SCNShape(path: path, extrusionDepth: 0.005)
arrowShape.chamferRadius = 20
If you want to create a rounded corners use the following method for path:
path.addQuadCurve(to: CGPoint, controlPoint: CGPoint)
or
path.addCurve(to: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint)

How to move SKSpriteNode in a curve without rotating the sprite in Swift

I'm trying to move a sprite with a curve.
I got this code:
let path = UIBezierPath()
path.move(to: CGPoint.zero)
path.addQuadCurve(to: CGPoint(x: ball.position.x+200, y: ball.position.y+50), controlPoint: CGPoint(x: ball.position.x+100, y: ball.position.y+200))
ball.run(SKAction.follow(path.cgPath, speed: 1.0))
I have few questions:
1 - why my sprite is rotating while moving, and if I can control this rotation?
2 - any idea why the ball is moving only small part of the way and very slow and not a smooth moving (10-20 seconds)?
Does anyone have any idea how this code works?
All the answers I found were related to older Swift version that had different method.
At last I've found the solution :)
func beizerSprite()
{
// create a bezier path that defines our curve
let path = UIBezierPath()
path.move(to: CGPoint(x: 16,y: 239))
path.addCurve(to:CGPoint(x: 301, y: 239),
controlPoint1: CGPoint(x: 136, y: 373),
controlPoint2: CGPoint(x: 178, y: 110))
// use the beizer path in an action
_playButton.run(SKAction.follow(path.cgPath,
asOffset: false,
orientToPath: true,
speed: 50.0))
}
This move the SKSprite in a curve along the screen.

Cut a circle out of a UIView using mask [duplicate]

This question already has answers here:
How can I 'cut' a transparent hole in a UIImage?
(4 answers)
Closed 6 years ago.
In my app I have a square UIView and I want to cut a hole/notch out of the top of. All the tutorials online are all the same and seemed quite straightforward but every single one of them always delivered the exact opposite of what I wanted.
For example this is the code for the custom UIView:
class BottomOverlayView: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
drawCircle()
}
fileprivate func drawCircle(){
let circleRadius: CGFloat = 80
let topMidRectangle = CGRect(x: 0, y: 0, width: circleRadius*2, height: circleRadius*2)
let circle: CAShapeLayer = CAShapeLayer()
circle.position = CGPoint(x: (frame.width/2)-circleRadius, y: 0-circleRadius)
circle.fillColor = UIColor.black.cgColor
circle.path = UIBezierPath(ovalIn: topMidRectangle).cgPath
circle.fillRule = kCAFillRuleEvenOdd
self.layer.mask = circle
self.clipsToBounds = true
}
}
Here is what I hope to achieve (the light blue is the UIView, the dark blue is the background):
But here is what I get instead. (Every single time no matter what I try)
I'm not sure how I would achieve this, aside from making a mask that is already the exact shape that I need. But if I was able to do that then I wouldn't be having this issue in the first place. Does anyone have any tips on how to achieve this?
EDIT: The question that this is supposedly a duplicate of I had already attempted and was not able to get working. Perhaps I was doing it wrong or using it in the wrong context. I wasn't familiar with any of the given methods and also the use of pointers made it seem a bit outdated. The accepted answer does a much better job of explaining how this can be implemented using much more widely used UIBezierPaths and also within the context of a custom UIView.
I'd suggest drawing a path for your mask, e.g. in Swift 3
// BottomOverlayView.swift
import UIKit
#IBDesignable
class BottomOverlayView: UIView {
#IBInspectable
var radius: CGFloat = 100 { didSet { updateMask() } }
override func layoutSubviews() {
super.layoutSubviews()
updateMask()
}
private func updateMask() {
let path = UIBezierPath()
path.move(to: bounds.origin)
let center = CGPoint(x: bounds.midX, y: bounds.minY)
path.addArc(withCenter: center, radius: radius, startAngle: .pi, endAngle: 0, clockwise: false)
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
path.close()
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
Note, I tweaked this to set the mask in two places:
From layoutSubviews: That way if the frame changes, for example as a result of auto layout (or by manually changing the frame or whatever), it will update accordingly; and
If you update radius: That way, if you're using this in a storyboard or if you change the radius programmatically, it will reflect that change.
So, you can overlay a half height, light blue BottomOverlayView on top of a dark blue UIView, like so:
That yields:
If you wanted to use the "cut a hole" technique suggested in the duplicative answer, the updateMask method would be:
private func updateMask() {
let center = CGPoint(x: bounds.midX, y: bounds.minY)
let path = UIBezierPath(rect: bounds)
path.addArc(withCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
let mask = CAShapeLayer()
mask.fillRule = .evenOdd
mask.path = path.cgPath
layer.mask = mask
}
I personally find the path within a path with even-odd rule to be a bit counter-intuitive. Where I can (such as this case), I just prefer to just draw the path of the mask. But if you need a mask that has a cut-out, this even-odd fill rule approach can be useful.

Scaleable UIBezier in Swift 3

I'm trying to design a scaleable UIBezier.
This is my Swift Code:
#IBDesignable
class AnimatedRingView: UIView {
override func draw(_ rect: CGRect) {
//// Color Declarations
let strokeColor = UIColor(red: 1.000, green: 0.706, blue: 0.004, alpha: 1.000)
//// bezier Drawing
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 48.93, y: 266.07))
bezierPath.addCurve(to: CGPoint(x: 48.93, y: 53.93), controlPoint1: CGPoint(x: -9.64, y: 207.49), controlPoint2: CGPoint(x: -9.64, y: 112.51))
bezierPath.addCurve(to: CGPoint(x: 261.07, y: 53.93), controlPoint1: CGPoint(x: 107.51, y: -4.64), controlPoint2: CGPoint(x: 202.49, y: -4.64))
bezierPath.addCurve(to: CGPoint(x: 261.07, y: 266.07), controlPoint1: CGPoint(x: 319.64, y: 112.51), controlPoint2: CGPoint(x: 319.64, y: 207.49))
bezierPath.addLine(to: CGPoint(x: 261.07, y: 266.07))
bezierPath.lineCapStyle = .round;
bezierPath.lineJoinStyle = .round;
strokeColor.setStroke()
bezierPath.lineWidth = 10
bezierPath.stroke()
}
}
This is the result:
My problem:
To save time, I created the UIBezierPath by PaintCode:
And as you can see above, we have a width and height for the UIBezierPath, however, the code is displayed points of UIBezierPath ...
How can I create a size variable to assign a value via code (or inspector) that automatically identify the points of UIBezierPath?
Is this possible? Need help!
Given you creating a Circular Arc, using Bezier Curves for that purpose seems like overkill.
With UIBezierPath, you can use this function:
func addArcWithCenter(_ center: CGPoint,
radius radius: CGFloat,
startAngle startAngle: CGFloat,
endAngle endAngle: CGFloat,
clockwise clockwise: Bool)
and set a radius, along with the requisite start and end angles.
If you really must use cubic Béziers for the purpose, you should use the magic number kappa which equals 0.5522847498, and that is the control points for a circle built out of cubic Béziers.
I have placed a small demo of what I'm describing on my web site at:
http://www.trilithon.com/download/AnimatedRingView.zip
Hope That Helps.
Bezier objects do not support attaching variables to their dimensions, but you can use Oval object with custom start/end angles.
— PaintCode Support