How can I add animation on Path - animation

I made custom button with Shape. When I click on custom Shape. I changed path CGPoint.
I wanna change this points with animation but I couldn't. How can I do this ?
ContentView:
struct ContentView: View {
#State var buttonStatus: ButtonStatus = .left
#State var foo: Bool = false
var body: some View {
BarButton(buttonStatus: buttonStatus)
.frame(width: 125, height: 45)
.animation(.easeInOut, value: foo)
.onTapGesture {
// withAnimation(.easeInOut(duration: 1)) {
buttonStatus = buttonStatus == .left ? .right : .left
// }
}
}
}
BarButton
struct BarButton: Shape {
private var buttonStatus: ButtonStatus
var animatableData: ButtonStatus {
get { return buttonStatus }
set { buttonStatus = newValue }
}
init(buttonStatus: ButtonStatus) {
self.buttonStatus = buttonStatus
}
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: buttonStatus == .left ? rect.minX + 12 : rect.minX + 18, y: .zero))
path.addLine(to: CGPoint(x: buttonStatus == .left ? rect.maxX - 15 : rect.maxX - 12, y: .zero))
path.addCurve(to: CGPoint(x: rect.maxX, y: buttonStatus == .left ? rect.minX + 13 : rect.minY + 14), control1: CGPoint(x: rect.maxX - 5, y: .zero), control2: CGPoint(x: rect.maxX, y: rect.minY + 7))
path.addCurve(to: CGPoint(x: buttonStatus == .left ? rect.maxX - 5 : rect.maxX, y: buttonStatus == .left ? rect.midY + 15 : rect.minY + 30), control1: CGPoint(x: rect.maxX, y: buttonStatus == .left ? rect.midY : rect.minY + 15), control2: CGPoint(x: buttonStatus == .left ? rect.maxX - 5 : rect.maxX, y: buttonStatus == .left ? rect.midY + 15 : rect.minY + 30))
path.addCurve(to: CGPoint(x: buttonStatus == .left ? rect.maxX - 20 : rect.maxX - 10, y: rect.maxY), control1: CGPoint(x: buttonStatus == .left ? rect.maxX - 8 : rect.maxX, y: buttonStatus == .left ? rect.maxY : rect.maxY - 5), control2: CGPoint(x: buttonStatus == .left ? rect.maxX - 10 : rect.maxX - 5, y: rect.maxY))
path.addLine(to: CGPoint(x: buttonStatus == .left ? rect.minX + 12 : rect.minX + 10, y: rect.maxY))
path.addCurve(to: CGPoint(x: .zero, y: buttonStatus == .left ? rect.midY + 10 : rect.maxY - 10), control1: CGPoint(x: buttonStatus == .left ? rect.minX + 5 : rect.minX + 4, y: rect.maxY), control2: CGPoint(x: .zero, y: rect.maxY - 5))
path.addLine(to: CGPoint(x: buttonStatus == .left ? .zero : rect.minX + 7, y: buttonStatus == .left ? rect.minY + 15 : rect.minY + 9))
path.addCurve(to: CGPoint(x: buttonStatus == .left ? rect.minX + 12 : rect.minX + 18, y: .zero), control1: CGPoint(x: buttonStatus == .left ? .zero : rect.minX + 9, y: buttonStatus == .left ? rect.minY + 7 : rect.minY + 4), control2: CGPoint(x: buttonStatus == .left ? rect.minX + 5 : rect.minX + 12, y: .zero))
path.closeSubpath()
return path
}
}

As #Asperi commented, animatableData works by interpolating its values, so it needs to be of a type that "can be interpolated", such as a CGFloat (e.g. Changing its value from 0.0 to 5.0, will interpolate all values in between).
In your case, you are defining animatableData as a ButtonStatus (I guess it is an enum). So it does have no clue on how to apply the interpolation when changing values.
However, you can define how interpolation should be handled for a given custom type by making it conform to VectorArithmetic.
Note that in your case, as the effect produced by changing the animatableData value varies from CGPoint to CGPoint in the path, you should probably extract some logic so these changes may be all defined by a single interpolation logic.
Here is an article in which VectorArithmetic is used to animate a path defined by multiple curves (Similar to what you want to achieve).
Note: Be aware you are applying an animation on foo change, which has nothing to do with the button's shape change, so make sure to also replace it with the value that actually defines its shape.

Related

How to Animate a UIImage out of the Screen in Swift

i have searched around the web but nothing could answer my question.
I am trying to animate an UIImageview out of the Screen, set the alpha of it to 0, reset its position and reset the alpha to 1 again.
I can't seem to get the right position where to animate the Image to.
I have set up a gesturerecognizer to drag the image around and when the center of the image is above a certain point, i want to animate it out of screen.
This is the Regocnizercode :
#objc func imageDragged(gestureRecognizer: UIPanGestureRecognizer){
//Current Point where the label is dragged
let draggedLabelPoint = gestureRecognizer.translation(in: view)
//Updating the center of the label : viewcenter +/- currentpoint
matchImageView.center = CGPoint(x: view.bounds.width/2 + draggedLabelPoint.x, y: view.bounds.height/2 + draggedLabelPoint.y)
let xFromCenter = view.bounds.width/2 - matchImageView.center.x
var rotation = CGAffineTransform(rotationAngle: xFromCenter / -500)
let scale = min(100/abs(xFromCenter),1)
var scaledAndRotated = rotation.scaledBy(x: scale, y: scale)
//Transforming the Item respective to the distance from center
matchImageView.transform = scaledAndRotated
if gestureRecognizer.state == .ended {
if matchImageView.center.x < (view.bounds.width/2 - 100) {
animateImageToSide(target: CGPoint(x: view.bounds.width - 200, y: matchImageView.center.y))
matchImageView.alpha = 0
}
if matchImageView.center.x > (view.bounds.width/2 + 100) {
animateImageToSide(target: CGPoint(x: view.bounds.width + 200, y: matchImageView.center.y))
matchImageView.alpha = 0
}
//Reset the scaling variables and recentering the Item after swipe
rotation = CGAffineTransform(rotationAngle: 0)
scaledAndRotated = rotation.scaledBy(x: 1, y: 1)
matchImageView.transform = scaledAndRotated
matchImageView.center = CGPoint(x: view.bounds.width/2, y: view.bounds.height/2)
}
}
I am sorry if the resetting part is irrelevant for you, but im not sure if this could cause this behavior.
And this is my current Animationmethod :
func animateImageToSide(target:CGPoint){
UIImageView.animate(withDuration: 0.5, delay: 0, options: .curveLinear, animations: {self.matchImageView.center = target}) { (success: Bool) in
self.updateImage()
self.fadeInImage()//This is where i set the alpha back to 1
}
}
I am not sure if a CGPoint is what i need to use.
The current behavoir of the image is very weird. Most of the time it snaps to a specific place, regardless of the targetposition. Maybe my attempt on this is entirely wrong.
Thanks for your help !

How to prepare rectangle type tab bar in swift?

I have prepared a tab bar which has a square shape rectangle but I can't able to prepare a rectangle type shape of a tab bar button or UIButton or View. How can I prepare a tab bar or button or view as following gif file?
You can try something like given below for each button:
let tabBar1Path = UIBezierPath()
let tabBar1Layer = CAShapeLayer()
tabBar1Path.move(to: CGPoint(x: 0, y: 0))
tabBar1Path.addLine(to: CGPoint(x: self.view!.frame.size.width * 0.25, y: 0))
tabBar1Path.addLine(to: CGPoint(x: self.view!.frame.size.width * 0.2, y: tabBarHeight))
tabBar1Path.addLine(to: CGPoint(x: 0, y: tabBarHeight))
tabBar1Path.addLine(to: CGPoint(x: 0, y: 0))
tabBar1Layer.path = trianglePath.cgPath
tabBar1Layer.fillColor = UIColor.black.cgColor
tabBarButton1.layer.addSublayer(triangleLayer)

Draw a vertical line

I want to put an image (I will create) in my status bar app and this image should made of vertical line but I don't now how to draw them.
Found
let graphWidth: CGFloat = 22.0;
let graphHeight: CGFloat = 22.0
let size = NSMakeSize( graphWidth, graphHeight )
let image = NSImage(size: size)
image.lockFocus()
let path = NSBezierPath()
path.move(to: NSPoint(x: 20.0, y: 0))
path.line(to: NSPoint(x: 20.0, y: 20.0))
path.stroke()
image.unlockFocus()
statusItem.image = image

Changing rotation of a UIBezierPath in SpriteKit without changing orientation

I'm making a game. I want 2 separate lines to rotate clockwise and when the player touches the screen the rotation should change to counterclockwise. Right now, when it changes to counterclockwise, the UIBezierPath flips the node. I don't want the node to be flipped, just rotate the other way.
func clockwise() {
let dx = leftside.position.x - self.frame.width / 2
let dy = leftside.position.y - self.frame.height / 2
let rad = atan2(dy, dx)
let path = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2, y: self.frame.height / 2), radius: 85, startAngle: rad, endAngle: rad + CGFloat(M_PI * 4), clockwise: true)
let follow = SKAction.followPath(path.CGPath, asOffset: false, orientToPath: true, speed: 250)
leftside.runAction(SKAction.repeatActionForever(follow).reversedAction())
let dx1 = rightside.position.x - self.frame.width / 2
let dy1 = rightside.position.y - self.frame.height / 2
let rad1 = atan2(dy1, dx1)
let path1 = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2, y: self.frame.height / 2), radius: 85, startAngle: rad1, endAngle: rad1 + CGFloat(M_PI * 4), clockwise: true)
let follow1 = SKAction.followPath(path1.CGPath, asOffset: false, orientToPath: true, speed: 250)
rightside.runAction(SKAction.repeatActionForever(follow1).reversedAction())
}
func counterclockwise() {
let dx = leftside.position.x - self.frame.width / 2
let dy = leftside.position.y - self.frame.height / 2
let rad = atan2(dy, dx)
let path = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2, y: self.frame.height / 2), radius: 85, startAngle: rad, endAngle: rad + CGFloat(M_PI * 4), clockwise: true)
let follow = SKAction.followPath(path.CGPath, asOffset: false, orientToPath: true, speed: 250)
leftside.runAction(SKAction.repeatActionForever(follow))
let dx1 = rightside.position.x - self.frame.width / 2
let dy1 = rightside.position.y - self.frame.height / 2
let rad1 = atan2(dy1, dx1)
let path1 = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2, y: self.frame.height / 2), radius: 85, startAngle: rad1, endAngle: rad1 + CGFloat(M_PI * 4), clockwise: true)
let follow1 = SKAction.followPath(path1.CGPath, asOffset: false, orientToPath: true, speed: 250)
rightside.runAction(SKAction.repeatActionForever(follow1))
}

I have 2 bodies in swift that when collide I want to bounceoff of each other, How can I make this possible so it won't sense the collision?

I have a roof Sprite Node that i would like for the Player Sprite Node to bounce off of, but when they touch it runs my collision function.
Roof and player physics category:
struct PhysicsCatagory {
static let Player : UInt32 = 0x1 << 1
static let Roof : UInt32 = 0x1 << 6
}
Player and Roof code:
Player = SKSpriteNode(imageNamed: "plane")
Player.size = CGSize(width: 60, height: 70)
Player.physicsBody = SKPhysicsBody(rectangleOfSize: Player.size)
Player.position = CGPoint(x: self.frame.width / 2 - Player.frame.width, y: self.frame.width / 2)
Player.physicsBody?.categoryBitMask = PhysicsCatagory.Player
Player.physicsBody?.collisionBitMask = PhysicsCatagory.Ground | PhysicsCatagory.Mine | PhysicsCatagory.Ep | PhysicsCatagory.Bullet
Player.physicsBody?.contactTestBitMask = PhysicsCatagory.Ground | PhysicsCatagory.Mine | PhysicsCatagory.Ep | PhysicsCatagory.Bullet
Player.physicsBody?.affectedByGravity = false
Player.physicsBody?.dynamic = false
Player.zPosition = 4
Roof.size = CGSize(width: self.frame.width, height: 40)
Roof.physicsBody = SKPhysicsBody(rectangleOfSize: Roof.size)
Roof.position = CGPoint(x: self.frame.width / 2, y: self.frame.height - 20)
Roof.physicsBody?.categoryBitMask = PhysicsCatagory.Roof
Roof.physicsBody?.affectedByGravity = false
Roof.physicsBody?.dynamic = false
Roof.zPosition = 4
And here is my code for when a collision occurs:
func didBeginContact(contact: SKPhysicsContact) {
game = false
end = true
soundy = false
self.removeAllChildren()
self.removeAllActions()
endScreen()
}
I have tried changing their identities to 0 but then the player just goes through the roof. If you know anything about this please leave any recommendations or suggestions below. Thank You
remove contactTestBitmask if you dont want didBeginContact to occur

Resources