StrokeEnd not drawing full circle - ios

I'm trying to build a progress view for a Music Player. I am totaly new to programming in general and now really reached a dead end here.
My progress works fine. I am animatig "strokeEnd" with the value (value/duration) of the Mediafile, but strokeEnd only ever reaches have of the circle. However, the song is then finished as well, this is why I think, somewhere it calculats wrong.
It is a very simple Shaplayer I am using.
var value: CGFloat = 0.0{
didSet {
redrawStrokeEnd() }
}
private func setupShapeLayer(shapeLayer: CAShapeLayer) {
let startAngle = CGFloat(M_PI_2)
let endAngle = CGFloat(M_PI_2 * 2 + M_PI_2)
progressLayer.path = UIBezierPath(arcCenter:centerPoint, radius: CGRectGetWidth(frame)/2 + 5, startAngle:startAngle, endAngle: endAngle, clockwise: true).CGPath
progressLayer.strokeStart = 0.0
progressLayer.strokeEnd = 0.0 }
func redrawStrokeEnd() {
progressLayer.strokeEnd = CGFloat(value / CGFloat(duration))}
value = currentTime and duration = Playbackduration.
I can't figure out, why stroke End does not move the full circle.

Perhaps you're using the wrong constant? As some of the other commenters have pointed out, a circle in radians traverses 2 * pi. However the constant M_PI_2 is equal to (pi / 2) So right now you're only traversing from (pi/2) -> (3pi/2)... or pi. If you really want to start at pi/2 try:
let startAngle = CGFloat(M_PI_2)
let endAngle = CGFloat(M_PI_2 + 2.0 * M_PI)

It's not strokeEnd the reason you see half circle. You just have wrong start and end angles for your path. You can set them as below to have a full circle :
let startAngle = CGFloat(M_PI_2)
let endAngle = CGFloat(5*M_PI_2)
Or better if you don't want to set angles by radians set them in degrees like this :
let startAngle = CGFloat(0 * M_PI / 180)
let endAngle = CGFloat(360 * M_PI / 180.0)

Just changing the start angle and end angle in your code will work for you.
let startAngle = -90.0
let endAngle = -90.01

Related

Smooth open UIBezierPath

I'm trying to get smooth edges of Arcs and Curves.
The SKShapeNodes are open UIBezierPaths with a thick line-width, however, jagged edges are noticeable around Arcs and Curves. Changing the flatness doesn't seem to have any effect. Is there a way to smoothen these, or am I going to have to make a closed UIBezierPath with no line-width?
You need to create closed UIBezierPath with no linewidth
also
Make it shouldRasterize = true and set proper scale rasterizationScale = 2 * UIScreen.main.scale
I have a solution to my problem. Instead of using path.addArc(), I now use a function to get as many points as I'd like along an arc and then use path.addLine() to every one of those points. If you want more points for a smoother path, just lower the value of n.
func getCirclePoints(centerPoint: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) -> [CGPoint] {
let n : CGFloat = clockwise ? -1 : 1
let points: [CGPoint] = stride(from: startAngle - n, through: endAngle + n, by: n).map {
let degreesToRadians = CGFloat($0) * .pi / 180
let x = centerPoint.x + radius * cos(degreesToRadians)
let y = centerPoint.y + radius * sin(degreesToRadians)
return CGPoint(x: x, y: y)
}
return points
}

How Can I determine which section shows by indicator after two objects completed actions?

I have a wheel sprite node like below and it can rotate different speeds, directions, angles. Also I have an indicator it can rotate different features (like I cited above for wheel) After tapping a button firstly I remove UIBezierPath follow action for my indicator, at the same time remove all actions from my wheel.
Here's my question. After all things stopped. How Can I determine/handle that which color pointing by that indicator?
I couldn't create a relation indicator's zRotation and wheel's zRotation. Can I get an rotation angle from first state. But If I continue first state will change.
If someone explain this case, It would be great.
Update for indicator returning:
func moveClockWise() {
let dx = person.position.x - self.size.width / 2
let dy = person.position.y - self.size.height / 2
let rad = atan2(dy, dx)
path = UIBezierPath(arcCenter: CGPoint(x: self.size.width / 2, y: self.size.height / 2), radius: 294, startAngle: rad, endAngle: rad + CGFloat(M_PI * 4), clockwise: true)
let follow = SKAction.follow(path.cgPath, asOffset: false, orientToPath: true, speed: 500)
person.run(SKAction.repeatForever(follow).reversed(), withKey: "personFollow")
}
Update indicatorAngle return these values (independent from direction)
let dx = person.position.x - self.size.width / 2
let dy = person.position.y - self.size.height / 2
let rad = atan2(dy, dx)
var indicatorAngle = (rad * 180 / .pi).truncatingRemainder(dividingBy: 360)
wheelAngle returns gradually increase in positive or negative (depends on wheel's rotate direction)
var wheelAngle = (wheel.zRotation * 180 / .pi).truncatingRemainder(dividingBy: 360)
As described by #Whirlwind, you just need to map every angle to a color.
func colorName(byRotationOf wheel: SKSpriteNode) -> String {
let degree = wheel.zRotation * 180 / .pi
switch degree {
case 0..<45: return "Dark Green"
case 45..<90: return "Dark Blue"
case 90..<135: return "Purple"
case 135..<180: return "Bordeaux"
case 180..<225: return "Red"
case 225..<270: return "Orange"
case 270..<315: return "Green"
case 315..<360: return "Blue"
default: fatalError()
}
}
Update
If the indicator moves as well, you'll need to get the delta of the wheel angle and the indicator angle.
This code should do the job
func colorName(by wheel: SKSpriteNode, indicatorRotation: CGFloat) -> String {
let wheelAngle = (wheel.zRotation * 180 / .pi).truncatingRemainder(dividingBy: 360)
let indicatorAngle = (indicatorRotation * 180 / .pi).truncatingRemainder(dividingBy: 360)
let deltaAngle = indicatorAngle - wheelAngle
let slice = Int(deltaAngle / 45)
let colorNames = ["Dark Green", "Dark Blue", "Purple", "Bordeaux", "Red", "Orange", "Green", "Blue"]
return colorNames[slice]
}

Using UIBeizerPath on Circle

I want to make a circle which can be cut by percent, for example, 0.25 is 1/4 of the circle.
This is my code so far:
let circlePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 300), radius: CGFloat(20), startAngle: CGFloat(M_PI + M_PI_2), endAngle:CGFloat(90 * M_PI / 180, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.CGPath
//change the fill color
shapeLayer.fillColor = UIColor.clearColor().CGColor
//you can change the stroke color
shapeLayer.strokeColor = UIColor.redColor().CGColor
//you can change the line width
shapeLayer.lineWidth = 3.0
view.layer.addSublayer(shapeLayer)
In startAngle I wrote CGFloat(M_PI + M_PI_2) because the start of the circle doesnt start in 0 but as in this picture:
In the endAngle line I wrote CGFloat(90 * M_PI / 180) because the formula from degrees to radians is: degrees*pi/180 .
BUT! In the view I get half of the circle and not quarter of the circle:
Does my endAngle formula is wrong?
I think what you want is for your start angle to be 0 and your end angle to be 90 * PI/180
What you have now is a curve that starts at the bottom 270, aka M_PI + M_PI/2 and goes to 90 (90 * M_PI / 180)
//Possibly what you want is for your endAngle = (90 * M_PI/180) + startAngle , aka a quarter more than where you started, i'm not sure which quarter of the circle you want
Because in iOS the top of the circle starts at 270 degrees, the startAngle is:
startAngle: CGFloat(M_PI + M_PI_2)
And the endAngle is:
endAngle:CGFloat((360 * 0.25 - 90)
Instead of 0.25, you can change it to any number between 0 to 1 (for example: if you want half of the circle to be full, change it to 0.5)

SpriteKit : Draw line using angle and Length

I want to do some drawing based on angles and lengths instead of coordinance in SpriteKit (Swift)
I have the following function that draws from 2 points but I want to create one that draws from 1 point to another place based on angle and length of line
func drawLine( start: CGPoint, end: CGPoint)
{
CGPathMoveToPoint(ref, nil, start.x * 100, start.y * 100)
let line = SKShapeNode()
CGPathAddLineToPoint(ref, nil, end.x * 100, end.y * 100)
line.path = ref
line.lineWidth = 4
line.fillColor = UIColor.redColor()
line.strokeColor = UIColor.redColor()
self.addChild(line)
}
sin and cos are your friends here and this answer isn't specific to Swift.
Given angle and radius, if you define your angle in radians rather than degrees, the end point of your line should be:
let endPoint = CGPoint(x: start.x + sin(angle) * radius,
y: start.y + cos(angle) * radius)
Simon

using skphysicsBody and circle of radius to trap ball in circle

roundbg.physicsBody = SKPhysicsBody(circleOfRadius: round.frame.width/2)
roundbg.physicsBody?.dynamic = false
Trying to trap the ball inside the circle(roundbg). The problem is that when I use circleOfRadius for my round texture it takes the whole circle and not just the border. I get why circleOfRadius is doing that but can't figure out how to grab just the border. Any ideas? So ideally when ball falls it gets trapped inside the circle(roundbg).
First iOS app/game and very new to this. Thanks for you help!
SKPhysicsBody(circleOfRadius:) creates a volume based body. In your case you need an Edge based body for the larger circle. Try the following code.
let largeCircleRadius : CGFloat = 100
var largeCirclePath = CGPathCreateMutable();
CGPathAddArc(largeCirclePath, nil, 0.0, 0.0, largeCircleRadius, 0.0, CGFloat(2.0 * M_PI), true)
let largeCircle = SKShapeNode(circleOfRadius: 100)
largeCircle.position = CGPointMake(CGRectGetWidth(frame) / 2.0, CGRectGetHeight(frame) / 2.0)
largeCircle.physicsBody = SKPhysicsBody(edgeLoopFromPath: largeCirclePath)
addChild(largeCircle)
let smallCircle = SKShapeNode(circleOfRadius: 10)
smallCircle.position = CGPointMake(CGRectGetWidth(frame) / 2.0, CGRectGetHeight(frame) / 2.0)
smallCircle.physicsBody = SKPhysicsBody(circleOfRadius: 10)
addChild(smallCircle)
Read more about Edge based and Volume based body here : https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Physics/Physics.html#//apple_ref/doc/uid/TP40013043-CH6-SW3
To create a fraction of a circle, you can change the startAngle and endAngle of the circlePath accordingly. Also use edgeChainFromPath instead of edgeLoopFromPath to not close the loop.
let largeCircleRadius : CGFloat = 100
var largeCirclePath = CGPathCreateMutable();
let startAngle : CGFloat = 3.14
let endAngle : CGFloat = 2 * 3.14
CGPathAddArc(largeCirclePath, nil, 0.0, 0.0, largeCircleRadius, startAngle, endAngle , false)
let largeCircle = SKShapeNode(path: largeCirclePath)
largeCircle.position = CGPointMake(CGRectGetWidth(frame) / 2.0, CGRectGetHeight(frame) / 2.0)
largeCircle.physicsBody = SKPhysicsBody(edgeChainFromPath: largeCirclePath)
addChild(largeCircle)
let smallCircle = SKShapeNode(circleOfRadius: 10)
smallCircle.position = CGPointMake(CGRectGetWidth(frame) / 2.0, CGRectGetHeight(frame) / 2.0)
smallCircle.physicsBody = SKPhysicsBody(circleOfRadius: 10)
addChild(smallCircle)

Resources