Troubles using CGPathContainsPoint SWIFT
As of Swift 3, many Core Graphics functions are now methods
on the corresponding type. In your example:
if my_sprite_path.contains(my_point) {
}
For more information see
SE-044 Import as member.
CGPathContainsPoint broken in iOS 12? Is a workaround possible?
I see the same behavior you describe. Interestingly, if you manually close the path, it seems to work. E.g., this is the same as your path except adding the line with the comment:
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 318.43, 183.47);
CGPathAddCurveToPoint(path, nil, 322.98, 189.32, 327, 194.93, 329.81, 201.62);
CGPathAddLineToPoint(path, nil, 339.36, 182.58);
CGPathAddLineToPoint(path, nil, 318.43, 183.47); // manually add line back to starting point
CGPathCloseSubpath(path);
CGPathMoveToPoint(path, nil, 327.8, 193.04);
CGPathAddCurveToPoint(path, nil, 323.06, 193.04, 323.06, 185.5, 327.8, 185.5);
CGPathAddCurveToPoint(path, nil, 332.54, 185.5, 332.54, 193.04, 327.8, 193.04);
CGPathCloseSubpath(path);
I might suggest trying keeping track of the starting point yourself and adding a line back to it wherever you close the subpath and see if that fixes it.
Even more inexplicably, doing CGPathMoveToPoint
twice (lol) also seems to solve it.
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 318.43, 183.47);
CGPathMoveToPoint(path, nil, 318.43, 183.47); // move the the starting point twice?!?
CGPathAddCurveToPoint(path, nil, 322.98, 189.32, 327, 194.93, 329.81, 201.62);
CGPathAddLineToPoint(path, nil, 339.36, 182.58);
CGPathCloseSubpath(path);
CGPathMoveToPoint(path, nil, 327.8, 193.04);
CGPathAddCurveToPoint(path, nil, 323.06, 193.04, 323.06, 185.5, 327.8, 185.5);
CGPathAddCurveToPoint(path, nil, 332.54, 185.5, 332.54, 193.04, 327.8, 193.04);
CGPathCloseSubpath(path);
Detect if CGPoint within polygon
You can create a CG(Mutable)PathRef
(or a UIBezierPath
that wraps a CGPathRef
) from your points and use the CGPathContainsPoint
function to check if a point is inside that path. If you use UIBezierPath
, you could also use the containsPoint:
method.
Swift 3 CGAffineTransform on CGRect
I modified the function found here and produced the following function:
func createPathRotatedAroundBoundingBoxCenter(path: CGPath, radians: CGFloat) -> CGPath {
let bounds = path.boundingBox; // might want to use CGPathGetPathBoundingBox
let center = CGPoint(x: bounds.midX, y: bounds.midY)
var transform = CGAffineTransform(translationX: 0, y: 0)
transform = transform.translatedBy(x: center.x, y: center.y)
transform = transform.rotated(by: radians)
transform = transform.translatedBy(x: -center.x, y: -center.y)
return path.copy(using: &transform)!
}
I then modified my code for the desired result.
let rect = CGRect(origin: CGPoint(x: Logo.position.x - (Logo.frame.width/3.6), y: Logo.position.y - (Logo.frame.height/2) - kViewSize.height/32), size: CGSize(width: Logo.frame.width/2, height: Logo.frame.height + kViewSize.height/16))
let ellipse = CGPath(ellipseIn: rect, transform: nil)
let ellipse2a = CGPath(ellipseIn: rect, transform: nil)
let ellipse3a = CGPath(ellipseIn: rect, transform: nil)
let ellipse2 = createPathRotatedAroundBoundingBoxCenter(path: ellipse2b, radians: CGFloat(M_PI/4.0))
let ellipse3 = createPathRotatedAroundBoundingBoxCenter(path: ellipse3b, radians: CGFloat(-M_PI/4.0))
YellowMagic.run(SKAction.repeatForever(SKAction.follow(ellipse, asOffset: false, orientToPath: false, duration: 1)))
RedMagic.run(SKAction.repeatForever(SKAction.follow(ellipse2, asOffset: false, orientToPath: false, duration: 1)))
GreenMagic.run(SKAction.repeatForever(SKAction.follow(ellipse3, asOffset: false, orientToPath: false, duration: 1)))
Swift - how to fill a path (opaque path)
Your code is correct but you should probably look at the origins of your scene. Are your sure you can see your shape?
By default, a scene’s origin is placed in the lower-left corner of the view. So, a default scene is initialized with a height of 1024 and a width of 768, has the origin (0,0) in the lower-left corner, and the (1024,768) coordinate in the upper-right corner. The frame property holds (0,0)-(1024,768).
Suppose you have this example using your question code:
let tileSize = CGSizeMake(150.0,150.0)
// Your code:
let isometricPath = CGPathCreateMutable()
CGPathMoveToPoint(isometricPath, nil, 0, -(tileSize.height / 2))
CGPathAddLineToPoint(isometricPath, nil, (tileSize.width / 2), 0)
CGPathAddLineToPoint(isometricPath, nil, 0, (tileSize.height / 2))
CGPathAddLineToPoint(isometricPath, nil, -(tileSize.width / 2), 0)
CGPathCloseSubpath(isometricPath)
This path represent a rhombus shape.
Now if I want to draw this shape I can do:
let shape = SKShapeNode.init(path: isometricPath)
shape.strokeColor = SKColor.yellowColor()
shape.fillColor = SKColor.blueColor()
self.addChild(shape)
But nothing is showed because my scene origin start from 0.0 and your shape have negative values.
To show my rhombus I can simply change the scene anchorPoint to:
self.anchorPoint = CGPointMake(0.5,0.5)
This centers the scene’s origin in the middle of the view (Apple docs) and you can see your tile:
Now suppose you want to know if a point is inside your shape:
let locationInNode = CGPointMake(tileSize.width/15,tileSize.height/15)
// this point is definitely inside the rhombus
I want to show this point simply for debug:
let circle = SKShapeNode.init(circleOfRadius: 5)
circle.strokeColor = SKColor.whiteColor()
circle.fillColor = SKColor.whiteColor()
self.addChild(circle)
circle.position = locationInNode
Now we can check if this point is inside the rhombus:
let isometricPathRef = isometricPath as CGPathRef
print(CGPathContainsPoint(isometricPathRef, nil, locationInNode, true))
Output:
Detecting a tap on a CAShapeLayer in Swift?
Heres imo the best way to do what you want to achieve:
// First add the shapelayer
let layer = CAShapeLayer()
layer.anchorPoint = CGPointZero
layer.path = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: 100, height: 200)).CGPath
layer.bounds = CGPathGetBoundingBox(layer.path) // IMPORTANT, without this hitTest wont work
layer.fillColor = UIColor.redColor().CGColor
self.view.layer.addSublayer(layer)
// Check for touches
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let point = touches.anyObject()!.locationInView(self.view) // Where you pressed
if let layer = self.view.layer.hitTest(point) as? CAShapeLayer { // If you hit a layer and if its a Shapelayer
if CGPathContainsPoint(layer.path, nil, point, false) { // Optional, if you are inside its content path
println("Hit shapeLayer") // Do something
}
}
}
Related Topics
How to Remove Node from Parent If Touched More Than Once
Iwatch: Wkinterfacelabel How to Stop Text from Being Cut Off with "..." at The End of a Label
Provide Simple Method to Get Current Speed (Implementing Speedometer)
Anchoring Multiple Scenes in Realitykit
How to Replace the Values of Labels in iOS-Charts
Appdelegate Segue Alternative Pass Data
Change Notification from Observable Object as a Nested Object in Swiftui
Function Builder Not Working When Only One Value
How to Access Content View's Elements Later in Swiftui
Swift Enumerate Functions. How Does It Work Behind
How to Read Id3 Tags/Other Metadata from an Hls Stream in Swift/Avkit
Changing The Color of a Button in Swiftui on Tvos
Swiftui Reorder List Dynamic Sections from Another View
Swift Firebase Using an Unspecified Index
Create a Loading Image/ Activity Indicator, Until the Image Is Shown in the Screen in Swift