Set a button as an image in SpriteKit - ios

I currently have a UIButton as a restart button for a game within SpriteKit and I'd like to make it an image. I have declared the button as:
var RateBtn: UIButton!
Here's current code for button:
RestartBtn = UIButton(frame: CGRect(x: 0, y: 0, width: view!.frame.size.width / 3, height: view!.frame.size.height / 3))
RestartBtn.center = CGPointMake(view!.frame.size.width / 2, view!.frame.size.height)
RestartBtn.setTitle("RESTART", forState: UIControlState.Normal)
RestartBtn.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
RestartBtn.addTarget(self, action: Selector("restart"), forControlEvents: UIControlEvents.TouchUpInside)
self.view?.addSubview(RestartBtn)
UIView.animateWithDuration(1.0, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: nil, animations: ({
self.RestartBtn.center.y = self.view!.frame.size.height / 1.4
}), completion: nil)
So I have it set out with a spring effect but I now want it as an image instead of font. How do I do this?

Make the button a SKSpriteNode.
Detect touch on it by:
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if button.containsPoint(location) {
//enter restart function here!
}
}

Related

How to use completion block for UIView.animate()?

I'm working on a project to learn animations and am having trouble using the completion block for the UIView.animate(withDuration:) func. MY animation is a shoebox that falls from the top of the screen, lands on a pedestal, then opens. Once the box opens I want a UIImageView to come out of the box, grow to full size of the screen and then segue to the next page, but the completion handler that the code for segueing is called before my animation completes and the UIImageView doesn't appear at all.
Here's my code:
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 1.5, delay: 0.0, options: .curveEaseInOut,
animations: {
//Opening the box
self.shoeBoxImage.shoeBox.animationImages = self.boxOpeningAnimation
self.shoeBoxImage.shoeBox.animationDuration = 1.5
self.shoeBoxImage.shoeBox.animationRepeatCount = 1
self.shoeBoxImage.shoeBox.contentMode = .scaleAspectFill
self.shoeBoxImage.shoeBox.startAnimating()
//set to the final image
self.shoeBoxImage.shoeBox.image = UIImage(named: "frame13")
},completion: {_ in
let nextPage = UIImageView()
nextPage.frame = CGRect(origin: self.shoeBoxImage.center, size: CGSize(width: 0.0, height: 0.0))
nextPage.image = UIImage(named: "FirstLoadBackgroundImg.jpeg")
nextPage.autoresizesSubviews = true
self.view.addSubview(nextPage)
self.view.bringSubviewToFront(nextPage)
UIView.animate(withDuration: 5.0, animations: {
nextPage.transform = CGAffineTransform(scaleX: 428, y: 926)
})
self.performSegue(withIdentifier: "FinishedLoading", sender: self)
})
}
This is my first time working with animations and programatically creating views so if someone could explain how to make the completion block wait for the animation to complete. In order to make the UIImageView appear and animate then once it's full screen, segue to the next page it would be very much appreciated.
The size is 0, 0. Transforming zero by any scale is still zero. I would advise you to not use transform at all, but rather just set the final frame to be what you want.
E.g.,
let startFrame = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
let endFrame = view.bounds
let imageView = UIImageView(image: ...)
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
imageView.frame = startFrame
UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut) {
imageView.frame = endFrame
} completion: { _ in
// do something here
}
That yields:
By the way, the performSegue probably should be inside a completion closure of the inner animate call.

UIButton with shadow rotating without shadow rotating

Currently I have a button that draws its properties from a UIButton extension.
func mapControllerButtons(image: String, selector: Selector) -> UIButton {
let button = UIButton()
button.setImage(UIImage(named: image)!.withRenderingMode(.alwaysOriginal), for: .normal)
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
button.layer.shadowRadius = 5
button.layer.shadowOpacity = 0.5
button.layer.masksToBounds = false
button.addTarget(self, action: selector, for: .touchUpInside)
return button
}
}
This creates a shadow underneath the button, however I have another function that rotates the button around, to signal to the user that data is being loaded from an API.
Currently what I am doing is creating the button with a shadow and overlaying it with a button that does not have a shadow. The shadowless button is the one that will be tapped and rotate.
I have a gut feeling that there's gotta be a better way of doing this than creating two buttons having one eat memory just so it can be a placeholder (for the shadow effect).
How can I go about creating a button that can rotate with animateWith func while the shadow stays in its place?
Here is my animation code block.
func animateRefreshButton() {
UIView.animate(withDuration: 1.0, delay: 0.0, options: .repeat, animations: {
self.refreshButton.transform = CGAffineTransform(rotationAngle: .pi)
})
}
You could try rotating the button's imageView instead of the whole button:
func animateRefreshButton() {
UIView.animate(withDuration: 1.0, delay: 0.0, options: .repeat, animations: {
self.refreshButton.imageView?.transform = CGAffineTransform(rotationAngle: .pi)
})
}

UIButton not clickable while returning to one view from second view

I have an app. I assume two of the views have a problem. The first view has three buttons arranged one below the other. Clicking any of the three will take us to the second view with 3 tableview cells.
The problem is, if we go to the second view and come back to the first, the buttons in the first view are not clickable. I am attaching the screens of two views.
View 1: There are 3 buttons.
View 2: These are table view cells.
Note: The table view cells are clickable. We go back to the first view by pressing a close button. in the top left corner of the second view.
**Problem in a nutshell: If we go to view 2, click the 'X' close button, the three buttons in view 1, that is fb, G+ and Email are not clickable. **
Here is the code for the three buttons in the first view.
#IBAction func firstBtnAction(_ sender: Any) {
let bounds = self.firstBtn.bounds
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [], animations: {
self.firstBtn.bounds = CGRect(x: bounds.origin.x - 20, y: bounds.origin.y, width: bounds.size.width + 20, height: bounds.size.height)
self.firstBtn.isEnabled = false
}, completion: nil)
goaltype = "Conversion"
UserGoal()
}
#IBAction func secondBtnAction(_ sender: Any) {
let boundss = self.secondBtn.bounds
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [], animations: {
self.secondBtn.bounds = CGRect(x: boundss.origin.x - 20, y: boundss.origin.y, width: boundss.size.width + 20, height: boundss.size.height)
self.secondBtn.isEnabled = false
}, completion: nil)
goaltype = "Engagement"
UserGoal()
}
#IBAction func thirdBtnAction(_ sender: Any) {
let bounds3 = self.thirdBtn.bounds
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [], animations: {
self.thirdBtn.bounds = CGRect(x: bounds3.origin.x - 20, y: bounds3.origin.y, width: bounds3.size.width + 20, height: bounds3.size.height)
self.thirdBtn.isEnabled = false
}, completion: nil)
goaltype = "Storytelling"
UserGoal()
}
There is a small animation for the second view table view cell.
Edit 1: When I tried adding isEnabled = true, the button was clickable. However, due to animation, it keeps expanding on each click. See the pic:
How do you make the buttons clickable?
you are setting isEnabled = false for all the buttons. Enable it when coming back to the first view
self.firstBtn.isEnabled = true
self.secondBtn.isEnabled = true
self.thirdBtn.isEnabled = true
For Disabling buttons only at the time of animation, enable it in the completion block
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [], animations: {
self.thirdBtn.bounds = CGRect(x: bounds3.origin.x - 20, y: bounds3.origin.y, width: bounds3.size.width + 20, height: bounds3.size.height)
self.thirdBtn.isEnabled = false
}) { (_ isCompleted) in
self.thirdBtn.isEnabled = true
}
Similarly do it for both firstBtn & secondBtn animations

Animating a UIView's alpha in sequence with UIViewPropertyAnimator

I have a UIView that I want to reveal after 0.5 seconds, and hide again after 0.5 seconds, creating a simple animation. My code is as follows:
let animation = UIViewPropertyAnimator.init(duration: 0.5, curve: .linear) {
self.timerBackground.alpha = 1
let transition = UIViewPropertyAnimator.init(duration: 0.5, curve: .linear) {
self.timerBackground.alpha = 0
}
transition.startAnimation(afterDelay: 0.5)
}
animation.startAnimation()
When I test it out, nothing happens. I assume it's because they're both running at the same time, which would mean they cancel each other out, but isn't that what the "afterDelay" part should prevent?
If I run them separately, i.e. either fading from hidden to visible, or visible to hidden, it works, but when I try to run them in a sequence, it doesn't work.
My UIView is not opaque or hidden.
You can use Timer, and add appearing / hiding animations blocks on every timer tick to your UIViewPropertyAnimatorobject.
Here's a codebase:
#IBOutlet weak var timerBackground: UIImageView!
private var timer: Timer?
private var isShown = false
private var viewAnimator = UIViewPropertyAnimator.init(duration: 0.5, curve: .linear)
override func viewDidLoad() {
super.viewDidLoad()
viewAnimator.addAnimations {
self.timerBackground.alpha = 1
}
viewAnimator.startAnimation()
isShown = true
self.timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(self.startReversedAction), userInfo: nil, repeats: true)
}
func startReversedAction() {
// stop the previous animations block if it did not have time to finish its movement
viewAnimator.stopAnimation(true)
viewAnimator.addAnimations ({
self.timerBackground.alpha = self.isShown ? 0 : 1
})
viewAnimator.startAnimation()
isShown = !isShown
}
I've implemented the very similar behavior for dots jumping of iOS 10 Animations demo project.
Please, feel free to look at it to get more details.
Use UIView.animateKeyframes you'll structure your code nicely if you have complicated animations. If you'll use UIView animations nested within the completion blocks of others, it will probably result in ridiculous indentation levels and zero readability.
Here's an example:
/* Target frames to move our object to (and animate)
or it could be alpha property in your case... */
let newFrameOne = CGRect(x: 200, y: 50, width: button.bounds.size.width, height: button.bounds.size.height)
let newFrameTwo = CGRect(x: 300, y: 200, width: button.bounds.size.width, height: button.bounds.size.height)
UIView.animateKeyframes(withDuration: 2.0,
delay: 0.0,
options: .repeat,
animations: { _ in
/* First animation */
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.5, animations: { [weak self] in
self?.button.frame = newFrameOne
})
/* Second animation */
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations: { [weak self] in
self?.button.frame = newFrameTwo
})
/* . . . */
}, completion: nil)
What worked for me, was using sequence of UIViewPropertyAnimators. Here is example of my code:
let animator1 = UIViewPropertyAnimator(duration:1, curve: .easeIn)
animator1.addAnimations {
smallCoin.transform = CGAffineTransform(scaleX: 4, y: 4)
smallCoin.center = center
}
let animator2 = UIViewPropertyAnimator(duration:1, curve: .easeIn)
animator2.addAnimations {
center.y -= 20
smallCoin.center = center
}
let animator3 = UIViewPropertyAnimator(duration:10, curve: .easeIn)
animator3.addAnimations {
smallCoin.alpha = 0
}
animator1.addCompletion { _ in
animator2.startAnimation()
}
animator2.addCompletion { _ in
animator3.startAnimation()
}
animator3.addCompletion ({ _ in
print("finished")
})
animator1.startAnimation()
You can even add afterdelay attribute to manage speed of animations.
animator3.startAnimation(afterDelay: 10)

Animate the 'backgroundColor' for a UIButton in iOS using Swift3

I would like to animate the backgroundColor property of my UIButtons but I'm not sure where to begin. I've never used Core Animation, I'm not sure if I even need it for something so basic?
I've styled the buttons using a class and a method that looks something like this:
redTheme.swift
func makeRedButton() {
self.layer.backgroundColor = myCustomRedcolor
}
You can do it with UIView Animation as the following :
Note: Simply don't forget to set the background to clear before you perform the animation otherwise it won't work. That's because animation need a starting and ending point, starting point is clear color to Red. Or alpha 0 to alpha 1 or the other way around.
let button = UIButton(frame: CGRect(x: 0, y: 100, width: 50, height: 50))
button.layer.backgroundColor = UIColor.clear.cgColor
self.view.addSubview(button)
func makeRedButton() {
UIView.animate(withDuration: 1.0) {
button.layer.backgroundColor = UIColor.red.cgColor
}
}
Swift 4 - A simple way to animate any change on your UIButton or any other UIView:
UIView.transition(with: button, duration: 0.3, options: .curveEaseInOut, animations: {
self.button.backgroundColor = .red
self.button.setTitle("ERROR", for: .normal)
self.button.setTitleColor(.white, for: .normal)
})
For some reason animating button.layer.backgroundColor
had zero effect for my custom uibutton hence I animated alpha like so:
func provideVisualFeedback(_ sender:UIButton!)
{
sender.backgroundColor = UIColor.whatever
sender.alpha = 0
UIView .animate(withDuration: 0.1, animations: {
sender.alpha = 1
}, completion: { completed in
if completed {
sender.backgroundColor = UIColor.white
}
})
}
and then
#IBAction func buttonAction(_ sender:NumberPadButton!)
{
provideVisualFeedback(sender)
do your thing once animation was setup with the helper above

Resources