I have an subview that fades into the view. this goes perfectly with this code:
UIView.animateWithDuration(0.9, animations: {
self.popUpView.backgroundColor = UIColor.blackColor()
})
But how can i make a sort of bounce? That the popupview fades in very hard and bounces a little bit back.
From UIView, you could use:
class func animateWithDuration(_ duration: NSTimeInterval,
delay delay: NSTimeInterval,
usingSpringWithDamping dampingRatio: CGFloat,
initialSpringVelocity velocity: CGFloat,
options options: UIViewAnimationOptions,
animations animations: () -> Void,
completion completion: ((Bool) -> Void)?)
To change the bounciness you'll want to change the dampingRatio; zero being the most bouncy and one being the least bouncy. From the UIView documentation:
Damping Ratio: The damping ratio for the spring animation as it approaches its quiescent state.
To smoothly decelerate the animation without oscillation, use a value
of 1. Employ a damping ratio closer to zero to increase oscillation.
Related
On Android, I've previously used CCScrollView (part of Cocos2D-X) to add nodes to a scrollable container, with all the extras like the elastic bounce, scrollbars, swipe gestures, etc.
But I can't work out how to do this in Xcode. I know there's UIScrollView, but I'm writing for macOS. And there's SKCameraNode, but this seems to target the whole scene, rather than just part of the scene, I think.
I'd like to create a horizontal scrollable container for interactive sprite nodes. I know I could simply make the node move in relation to the mouse's x value, but that's not as pleasing as a simple scrollview.
I guess I could place a second scene within the scrollview, but that seems overkill and a performance hit. There's also third party solutions like SwiftySKScrollView but I'd like to avoid relying on such dependencies.
override func mouseDown(with event: NSEvent) {
scroller.removeAllActions()
scrollerPosX = scroller.position.x - event.location(in: self).x
}
override func mouseDragged(with event: NSEvent) {
mouseMomentum = event.deltaX
for node in nodes(at: event.location(in: self)) {
if node.isMember(of: Scroller.self) {
scroller.position.x = event.location(in: self).x + scrollerPosX
}
}
}
// This needs work to improve the ease out momentum, and a boundary bounce needs adding
override func mouseUp(with event: NSEvent) {
let moveby = SKAction.moveBy(x: mouseMomentum*10, y: 0, duration: 0.3)
moveby.timingMode = .easeOut
scroller.run(moveby)
}
I'm trying to animate a constraint but it seems to be effecting the entire view. Here is the code that I am using to animate the constraint.
#IBOutlet weak var personHeight: NSLayoutConstraint!
func animateBackgroundHeight() {
print("animate")
UIView.animate(withDuration: 5.0, animations: {
self.personHeight.constant = 19 // personHeight is the IBOutlet to the constraint
self.view.layoutIfNeeded()
})
}
Everything in the view moves when the animation is going on but I'm not sure why. What I want to animate is the height of the view, but not effect the TextField or the button. Think of the GreyView as a background element of the TextField.
Here is a image to show you which constraint I am trying to animate (green one)
Pink box represents the parent view.
Grey Box is the view that I am trying to animate the height of.
The TextField and button are above the Grey Box object.
You may animate the wrong view after all
so to be clear about your question
if you have x view that nested in another view
and want to animate only x view
then you should
make
self.x.layoutIfNeeded()
instead of
self.view.layoutIfNeeded()
The middle lock animates upwards faster than the top pane in my animation. How do I animate the middle lock so that it moves upwards alongside the top pane? To be more specific, I want the middle locks center to always align with the top panes bottom as it moves up and off the screen.
This is the code I currently have, lockBorder and lockKeyhole is what is referred to as "middle lock" which is moving upwards too fast compared to the topLock:
#IBOutlet var topLock: UIImageView!
#IBOutlet var bottomLock: UIImageView!
#IBOutlet var lockBorder: UIImageView!
#IBOutlet var lockKeyhole: UIImageView!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
openLock()
}
func openLock() {
UIView.animateWithDuration(0.5, delay: 1.0, options: [], animations: {
self.lockKeyhole.transform = CGAffineTransformMakeRotation(CGFloat(3.14))
}, completion: { _ in
UIView.animateWithDuration(0.6, delay: 0.2, options: [], animations: {
var topFrame = self.topLock.frame
topFrame.origin.y -= topFrame.size.height
var bottomFrame = self.bottomLock.frame
bottomFrame.origin.y += bottomFrame.size.height
var lockBorderFrame = self.lockBorder.frame
lockBorderFrame.origin.y -= self.view.frame.height
var lockKeyholeFrame = self.lockKeyhole.frame
lockKeyholeFrame.origin.y -= self.view.frame.height
self.topLock.frame = topFrame
self.bottomLock.frame = bottomFrame
self.lockKeyhole.frame = lockKeyholeFrame
self.lockBorder.frame = lockBorderFrame
}, completion: { finished in
})
})
}
You are adjusting topFrame.origin.y by topFrame.size.height, but you are adjusting lockBorderFrame.origin.y and lockKeyholeFrame.origin.y by self.view.frame.height. We can deduce that these adjustments are different amounts. I assume topFrame.size.height is one half of self.view.size.height. Since you're moving the lock views by twice the distance you're moving topLock, and both adjustments happen over the same 0.6 second interval, the lock views move twice as fast.
I assume you want to animate the lock views all the way off the screen, so it would be insufficient to move them by by only topFrame.size.height. That would leave the bottom half of the lock visible. You need to move the lock views so that their bottom edges are at the top edge of self.view.
This means that you need to set lockBorder.frame.origin.y to the negative of lockBorder.frame.size.height. It will move a total distance of lockBorder.frame.size.height + lockBorder.frame.origin.y, which happens to be equal to lockBorder.frame.maxY.
I assume that lockBorder is at least as large as lockKeyhole, so if we move all the views by lockBorder.frame.maxY, they will all be off the screen. And since we move them all the same distance, all the views will move together.
Note also that if you use constraints in your storyboard, then these views will snap back to their original positions as soon as you do almost anything else to the view hierarchy. So you should probably remove them from their superviews as soon as the animation finishes, unless you're hiding them in some other way (like by removing their superview from the view hierarchy).
Incidentally, the standard library defines M_PI for you.
func openLock() {
UIView.animateWithDuration(0.5, delay: 1.0, options: [], animations: {
self.lockKeyhole.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
}, completion: { _ in
UIView.animateWithDuration(0.6, delay: 0.2, options: [], animations: {
let yDelta = self.lockBorder.frame.maxY
self.topLock.center.y -= yDelta
self.lockKeyhole.center.y -= yDelta
self.lockBorder.center.y -= yDelta
self.bottomLock.center.y += yDelta
}, completion: { _ in
self.topLock.removeFromSuperview()
self.lockKeyhole.removeFromSuperview()
self.lockBorder.removeFromSuperview()
self.bottomLock.removeFromSuperview()
})
})
}
I'm very new to iOS/xcode development, I'm trying to do something very basic as part of a tutorial exercise which is to fade a UILabel in once a user has completed a game.
So I'm hiding the label when the view loads:
override func viewDidLayoutSubviews() {
winMessageLabel.alpha = 0
}
And then on completion of the game I'm trying to fade it back in:
UIView.animateWithDuration(1, animations: { () -> Void in
self.winMessageLabel.alpha = 1
self.view.layoutIfNeeded()
})
But rather than fade in it appears with alpha 1 and then fades out. I get the same behaviour if I try to animate the x/y coordinates whereby it jumps to where I want it to go and then animates back to its original position
I've just worked it out - the layout is invalid so if I call layoutIfNeeded() prior to my animation it sorts everything out and then the label animates as expected:
self.view.layoutIfNeeded()
UIView.animateWithDuration(1, animations: { () -> Void in
self.winMessageLabel.alpha = 1
self.view.layoutIfNeeded()
})
I have a UIView that I would like to animate off the screen and then bring another view into the screen. I'm using auto layout and I have a IBOutlet set up for the left and right NSLayoutConstraint. Its seem that with out a width set for the UIView that the view actually doesn't animate off the screen.
I'm trying to use this code to pus the UIView off:
self.gameViewConstraintLeft.constant = -400
UIView.animateWithDuration(1, animations: { () in
self.view.layoutIfNeeded()
})
self.gameViewConstriantRight.constant = -400
UIView.animateWithDuration(1, animations: { () in
self.view.layoutIfNeeded()
})
I have constraints set up on the top(0), left(0) and right(0). Maybe this is my issue that I'm pinning the view to the edge of the screen, but I want this to work on multiple screen sizes, so I'm just not sure the best method of achieving my goal.
You are going to have problems moving the views if they're pinned on multiple sides horizontally or vertically.
I would suggest the following:
Set up width, height, centerX, and centerY constraints for your two views. The width and height can be set to "Equal Widths" and "Equal Heights" with the superview to allow it to work with multiple screen sizes.
Add an IBOutlets to the centerX constraint. Animate by modifying the constant property of this constraint:
self.gameView1ConstraintCenterX.constant = -400
UIView.animateWithDuration(1) {
self.view.layoutIfNeeded()
}
self.gameView2ConstriantCenterX.constant = 0
UIView.animateWithDuration(1) {
self.view.layoutIfNeeded()
}