Slice an SKShapeNode into two - ios

Is there an easy method to slice an SKShapeNode into 2 seperate SKShapeNodes?
In this example I have created an SKShapeNode using a closed bezier path as well as a single line.
let squareShape = SKShapeNode()
squareShape.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 256, height: 256), cornerRadius: 10).cgPath
squareShape.position = CGPoint(x: frame.midX-128, y: frame.midY+200-128)
squareShape.fillColor = UIColor.green
squareShape.strokeColor = UIColor.blue
squareShape.lineWidth = 2
addChild(squareShape)
let lineShape1 = SKShapeNode()
let line1 = UIBezierPath()
line1.move(to: CGPoint(x: -200, y: 300))
line1.addLine(to: CGPoint(x: 200, y: 100))
lineShape1.path = line1.cgPath
lineShape1.strokeColor = UIColor.white
lineShape1.lineWidth = 2
addChild(lineShape1)
How can I transform the existing SKShapeNode into 2 seperate SKShapeNodes separated by the line?
I need to be able to do this with varying line positions and shapes types (or even with multiple shapes and a single line).
Thanks

Related

Swift. disable antialiasing when UIBezierPath append

I'd like to append UIBezierPath without antialiasing.
Here is what I tried. testView's frame is 10x10
let shadowLayer = CALayer()
var path: UIBezierPath!
shadowLayer.frame = testView.bounds
path = UIBezierPath(rect: CGRect(x: 1, y: 1, width: 3, height: 3))
shadowLayer.shadowPath = path.cgPath
shadowLayer.shadowColor = UIColor.red.cgColor
shadowLayer.shadowOffset = .init(width: 0, height: 0)
shadowLayer.shadowOpacity = 1
shadowLayer.shadowRadius = 0
testView.layer.addSublayer(shadowLayer)
The red rectangle inside testView is 3x3, no problem.
But when I append another UIBezierPath.
...
path = UIBezierPath(rect: CGRect(x: 1, y: 1, width: 3, height: 3))
path.append(UIBezierPath(rect: CGRect(x: 5, y: 5, width: 3, height: 3)))
...
The red rectangles are getting antialiasing.
How do I disable antialiasing when appending UIBezierPath? Thanks.
You can create a new graphics context, set the shouldAntialias attribute to false and add the cgPath from your UIBezierPath to it. Or you can move it to draw(_:), get the current context and do the same.
https://developer.apple.com/documentation/appkit/nsgraphicscontext/1529486-shouldantialias

Why the edges of UIView are not smooth for huge corner radius?

class AttributedView: UIView {
private let gradient = CAGradientLayer()
var cornerRadius: CGFloat = 3 {
didSet {
layer.cornerRadius = cornerRadius
}
}
}
I simply use it for both: button view (corner radius: 20) and background circle (corner radius: 600).
Why button is smooth, and background is not?
With iOS 13.0 you can simple do, in addition to setting corner radius
yourView.layer.cornerCurve = .continuous
You should use bezzier paths and draw circle. After that you will receive nice, smooth edges.
let context = UIGraphicsGetCurrentContext()!
let gradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.white.cgColor] as CFArray, locations: [0, 1])!
let ovalPath = UIBezierPath(ovalIn: CGRect(x: 64, y: 9, width: 111, height: 93))
context.saveGState()
ovalPath.addClip()
context.drawLinearGradient(gradient, start: CGPoint(x: 119.5, y: 9), end: CGPoint(x: 119.5, y: 102), options: [])
context.restoreGState()
UIBezierPath is a simple and efficient class for drawing shapes using Swift, which you can then put into CAShapeLayer, SKShapeNode, or other places. It comes with various shapes built in, so you can write code like this to create a rounded rectangle or a circle:
let rect = CGRect(x: 0, y: 0, width: 256, height: 256)
let roundedRect = UIBezierPath(roundedRect: rect, cornerRadius: 50)
let circle = UIBezierPath(ovalIn: rect)
You can also create custom shapes by moving a pen to a starting position then adding lines:
let freeform = UIBezierPath()
freeform.move(to: .zero)
freeform.addLine(to: CGPoint(x: 50, y: 50))
freeform.addLine(to: CGPoint(x: 50, y: 150))
freeform.addLine(to: CGPoint(x: 150, y: 50))
freeform.addLine(to: .zero)
If your end result needs a CGPath, you can get one by accessing the cgPath property of your UIBezierPath.
You probably should clip bounds of this view:
attributedView.clipsToBounds = true

How Can I Create a Dashed Line SKShapeNode from a UIBezierPath, Using Swift?

All of the explanations I've found seem to say the same thing. I can't figure out why this isn't working.
var linePath = UIBezierPath()
linePath.move(to: CGPoint(x: 50, y: 50))
linePath.addLine(to: CGPoint(x: 100, y: 100))
var pattern : [CGFloat] = [10.0, 10.0]
linePath.setLineDash(pattern, count: pattern.count, phase: 0)
linePath.lineWidth = 10
linePath.lineCapStyle = .round
let shape = SKShapeNode()
shape.path = linePath.cgPath
shape.strokeColor = UIColor.white
self.addChild(shape)
This code successfully draws a line but shape does not inherit the dashed properties of linePath, including even the width. Any ideas?
let linePath = UIBezierPath()
linePath.move(to: CGPoint(x: 50, y: 50))
linePath.addLine(to: CGPoint(x: 100, y: 100))
var pattern: [CGFloat] = [10.0, 10.0]
let dashed = CGPathCreateCopyByDashingPath (linePath.CGPath, nil, 0, pattern, 2)
var shape = SKShapeNode(path: dashed)
shape.strokeColor = UIColor.white
self.addChild(shape)
NOTE: In Swift 3 CGPathCreateCopyByDashingPath has been replaced by path.copy(dashingWithPhase:lengths:)
e.g.
let dashed = SKShapeNode(path: linePath.cgPath.copy(dashingWithPhase: 2, lengths: pattern))

Dots following the path

I am looking for some simple method that will animate dots moving by closed path.
I have some path created with UIBezierPath. Which is represented by CAShapeLayer.
I want to place on this shape dots that will move around this closed path. The problem is that I couldn't find the best method. I do not want to add every dot programmatically. Maybe there is some method where could be use particles.
Path will have different shapes. The most important thing is to please dots with the same distance between each other.
I will be glad for help
Here is an example you can run in playground.
It's based on an article by Matt Long (from a while back) http://www.cimgf.com/2009/10/20/marching-ants-with-core-animation/
import UIKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 600, height: 700))
container.backgroundColor = UIColor.green
let v = UIView(frame: CGRect(x: 100, y: 100, width: 300, height: 300))
v.backgroundColor = UIColor.yellow
let shp = CAShapeLayer()
shp.bounds = v.bounds
let pth = UIBezierPath()
pth.move(to: CGPoint(x: 20, y: 220))
pth.addLine(to: CGPoint(x: 20, y: 40))
pth.addLine(to: CGPoint(x: 40, y: 80))
pth.addLine(to: CGPoint(x: 120, y: 40))
pth.addLine(to: CGPoint(x: 140, y: 40))
pth.close()
shp.strokeColor = UIColor.orange.cgColor
shp.fillColor = UIColor.cyan.cgColor
shp.lineWidth = 3.0
shp.lineDashPattern = [5, 5]
shp.path = pth.cgPath
shp.position = CGPoint(x: 150, y: 150)
v.layer.addSublayer(shp)
container.addSubview(v)
let dashAnim = CABasicAnimation(keyPath: "lineDashPhase")
dashAnim.fromValue = 0
dashAnim.toValue = 15
dashAnim.duration = 0.75
dashAnim.repeatCount = 10000
shp.add(dashAnim, forKey: "lineDashPhase")
PlaygroundPage.current.liveView = container
PlaygroundPage.current.needsIndefiniteExecution = true

Playground code: AffineTransform and AnchorPoint don't work as I want

I want to shrink a triangle downward as below:
But the triangle shrink upward as below:
The transform code is below:
layer.setAffineTransform(CGAffineTransformMakeScale(1.0, 0.5))
I tried to use anchorPoint but I can't scceed.
//: Playground - noun: a place where people can play
import UIKit
let view = UIView(frame: CGRectMake(0, 0, 200, 150))
let layer = CAShapeLayer()
let triangle = UIBezierPath()
triangle.moveToPoint (CGPointMake( 50, 150))
triangle.addLineToPoint(CGPointMake(100, 50))
triangle.addLineToPoint(CGPointMake(150, 150))
triangle.closePath()
layer.path = triangle.CGPath
layer.strokeColor = UIColor.blueColor().CGColor
layer.lineWidth = 3.0
view.layer.addSublayer(layer)
view
layer.setAffineTransform(CGAffineTransformMakeScale(1.0, 0.5))
view
Anish gave me a advice but doesn't work well:
layer.setAffineTransform(CGAffineTransformMakeScale(2.0, 0.5))
Transforms operate around the layer's anchorPoint, which by default is at the center of the layer. Thus, you shrink by collapsing inwards, not by collapsing downwards.
If you want to shrink downward, you will need to scale down and change the view's position (or use a translate transform in addition to your scale transform), or change the layer's anchorPoint before performing the transform.
Another serious problem with your code is that your layer has no assigned size. This causes the results of your transform to be very misleading.
I ran this version of your code and got exactly the results you are asking for. I have put a star next to the key lines that I added. (Note that this is Swift 3; you really need to step up to the plate and update here.)
let view = UIView(frame:CGRect(x: 0, y: 0, width: 200, height: 150))
let layer = CAShapeLayer()
layer.frame = view.layer.bounds // *
let triangle = UIBezierPath()
triangle.move(to: CGPoint(x: 50, y: 150))
triangle.addLine(to: CGPoint(x: 100, y: 50))
triangle.addLine(to: CGPoint(x: 150, y: 150))
triangle.close()
layer.path = triangle.cgPath
layer.strokeColor = UIColor.blue.cgColor
layer.lineWidth = 3
view.layer.addSublayer(layer)
view
layer.anchorPoint = CGPoint(x: 0.5, y: 0) // *
layer.setAffineTransform(CGAffineTransform(scaleX: 1, y: 0.5))
view
Before:
After:

Resources