changing opacity of button when clicked xcode / swift - ios

I have an UIButton set up with an image and by default when the user presses the button, the image is reduced to around 30% opacity.
I am wondering how to prevent this from happening and how to set the opacity to whatever I need it to be.

To add on the viewController, if you want to change opacity and time delay programmatically.
#IBAction func keyPressed(_ sender: UIButton) {
playSound(soundName: sender.currentTitle!)
//Reduces the sender's (the button that got pressed) opacity to half.
sender.alpha = 0.5
//Code should execute after 0.2 second delay.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
//Bring's sender's opacity back up to fully opaque.
sender.alpha = 1.0
}
}
func playSound(soundName: String) {
let url = Bundle.main.url(forResource: soundName, withExtension: "wav")
player = try! AVAudioPlayer(contentsOf: url!)
player.play()
}

Swift 5
You can do this by using DispatchQueue. What's more, to make the transition "smooth" use UIView.animate.
Just change the alpha parameter.
#IBAction func keyPressed(_ sender: UIButton) {
sender.alpha = 0.5
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3 ) {
sender.alpha = 1.0
}
}
Smooth change of the parameter.
#IBAction func keyPressed(_ sender: UIButton) {
UIView.animate(withDuration: 0.3) {
sender.alpha = 0.5
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3 ) {
UIView.animate(withDuration: 0.3) {
sender.alpha = 1.0
}
}
}

If you want to change the opacity you should use this code below. The alpha is basically the opacity. You can actually change the bottom part time, like how long you want it to be dimmed, you can also change sender.alpha value, like how dim you want it to be.
#IBAction func keyPressed(_ sender: UIButton)
{
// Reduces the sender's (the button that got pressed) opacity to half.
sender.alpha = 0.5
// Code should execute after 0.2 second delay.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
// Bring's sender's opacity back up to fully opaque.
sender.alpha = 1.0
}
}

I don't like a lot of these answers because they shortcut making the user think the button changes color when they tap it but only mimic a functionality apple has already provided for us. In my mind the optimal functionality would allow the button to change its opacity only when pressed and then revert when not selected. Try this out in Swift 5:
1) Create a new swift file called SomeCustomBtn
2) Insert this code:
import UIKit
class SomeCustomBtn: UIButton {
override open var isHighlighted: Bool {
didSet {
alpha = isHighlighted ? 0.5 : 1.0
}
}
}
3) Add your custom class to your buttons and iOS will automatically change your alpha based on the attribute isHighlighted!

To add on to AtWork, if you want to change the opacity programmatically at any time.
button.alpha = 0.30 // Make sure to use CGFloat literals
button.alpha = 1
button.alpha = 0

Easiest way:
#IBAction func keyPressed(_ sender: UIButton) {
sender.alpha = 0.5
}

//Reduces the opacity of the Button to half (the selected Button)
sender.alpha = 0.5
//this line of code will help you to delay the opacity to the selected seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
//This code brings sender's opacity back to fully opaque.
sender.alpha = 1.0
}

or one easy way is without using any DispatchQueue is this:
#IBAction func KeyDownPressed(_ sender: UIButton) {
sender.alpha = 0.5
}
#IBAction func keyPressed(_ sender: UIButton) {
sender.alpha = 1
playSound(col : sender.currentTitle!)
}
Please Note: Set Event of action of func KeyDownPressed to Touch Down

You can just simply set adjustImageWhenHighlighted to No.
button.adjustsImageWhenHighlighted = NO;
Let me know if it didn't work for you.

import UIKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var player: AVAudioPlayer!
#IBAction func keyPressed(_ sender: UIButton) {
playAudio(sound: sender.title(for: .normal)!)
sender.alpha = 0.5
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {sender.alpha = 1}
}
func playAudio(sound: String) {
let url = Bundle.main.url(forResource: sound, withExtension: "wav")
player = try! AVAudioPlayer(contentsOf: url!)
player.play()
}
}

Related

swift flash or blink 2 random colors when the button is pressed

hello this app currently starts out with a blank black screen and when u press the button in the middle of the screen it changes the background to a random color. I would like to start out with a message in the black screen saying " warning may cause seizure" and have the message dissapear once the button is pressed for the first time and the background will flash or blink 2 random colors forever until the button is pressed again. when the button is pressed again it will blink or flash 2 new random colors and so on. I hope this is not confusing, its really easy I just can't figure it out :( please help
import UIKit
class ViewController: UIViewController {
let colors: [UIColor] = [
.systemYellow,
.systemGreen,
.systemPurple,
.systemPink,
.systemRed,
.systemBlue,
.systemOrange,
.black,.gray
]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
}
#IBAction func didTapButton() {
UIView.animate(withDuration: 1/12, delay:0, options:[UIView.AnimationOptions.autoreverse], animations: {
self.view.backgroundColor = self.colors.randomElement()
self.view.backgroundColor = self.colors.randomElement() }, completion: nil)
}
}
First setup a timer, since you'll be running the animation forever until the button is clicked. In the ViewDidLoad() this will start the timer.
var timer: customTimer = Timer()
var isAnimating = false
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
startAnimation()
}
We start the timer with startAnimation() and it executes the code every 1 second. This is double so you can make it fractions of seconds, eg 0.1 or 0.001 mind you the lower the number the more resource intensive.
func startAnimation() {
customTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
UIView.animate(withDuration: 1/12, delay:0, options:[UIView.AnimationOptions.autoreverse], animations: {
self.view.backgroundColor = self.colors.randomElement()
self.view.backgroundColor = self.colors.randomElement() }, completion: nil)
}
}
Timers can be invalidated, ergo "Stopped" whenever you need to stop them. Just make sure that you don't have a nil timer or it will crash. You could create a flag that has var animationRunning = false then switch it to make the button turn animations on/off.
#IBAction func buttonTapped() {
isAnimating = !isAnimating
if isAnimating { customTimer.start() }
else { customTimer.invalidate() }
}

Controlling text alpha with tap, and screen brightness with pan gesture

I'm trying to design an interface where a tap fades text in, then fades it out, but dragging two fingers up on the screen makes it brighter, and dragging two fingers down make the screen dimmer. I've gotten the text to fade in and out, but I can't seem to get the brightness functionality to work. The app is building, but the two finger gesture does nothing at all.
I have tried inserting code found here.
I have tried several other methods found on Stack Overflow, such as those found here as well, but with no luck.
UPDATE: I made some changes based off of feedback in the comments from #rmaddy and #leo-dabus. There's still nothing at all happening when I pan in the simulator or on my iPhone. I'm not sure if I should be using "recognizer.state" instead of "sender.state". I'm sure I'm making lots of beginner mistakes. Do I even have the code for the pan gesture in the right place? Here's what I have now:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Hide text on launch
self.text.alpha = 0
}
// Introducing: The text!
#IBOutlet weak var text: UITextView!
// Show text upon tap of gesture recognizer
#IBAction func tap(_ sender: UITapGestureRecognizer) {
// fade in
UIView.animate(withDuration: 0.5, animations: {
self.text.alpha = 1.0
}) { (finished) in
// fade out
UIView.animate(withDuration: 4.0, animations: {
self.text.alpha = 0.0
})
}
}
#IBAction func twoFingerPan(_ sender: UIPanGestureRecognizer) {
if sender.state == UIPanGestureRecognizer.State.changed || sender.state == UIPanGestureRecognizer.State.ended {
let velocity:CGPoint = sender.velocity(in: self.view)
if velocity.y > 0 {
UIScreen.main.brightness -= 0.03
}
else {
UIScreen.main.brightness += 0.03
}
}
}
}
I suggest creating a couple of gesture recognizers. Something like this:
fileprivate func setGestureRecognizers() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tap(_:)))
tapGesture.delegate = self as? UIGestureRecognizerDelegate
tapGesture.numberOfTapsRequired = 1
view.addGestureRecognizer(tapGesture)
let panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(twoFingerPan(_:)))
panGesture.delegate = self as? UIGestureRecognizerDelegate
panGesture.minimumNumberOfTouches = 1
view.addGestureRecognizer(panGesture)
}
On viewDidLoad call this method:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Hide text on launch
self.text.alpha = 0
setGestureRecognizers()
}
Then I change a little your methods because I set the gesture recognizers using code and not storyboards, so I got rid of #IBAction and added #objc. Also I was testing this on my phone and comparing velocity.y > 0 made the gesture too sensitive to finger movement, so I changed it to 0.4 instead.
// Show text upon tap of gesture recognizer
#objc func tap(_ sender: UITapGestureRecognizer) {
// fade in
UIView.animate(withDuration: 0.5, animations: {
self.text.alpha = 1.0
}) { (finished) in
// fade out
UIView.animate(withDuration: 4.0, animations: {
self.text.alpha = 0.0
})
}
}
#objc func twoFingerPan(_ sender: UIPanGestureRecognizer) {
if sender.state == UIPanGestureRecognizer.State.changed || sender.state == UIPanGestureRecognizer.State.ended {
let velocity:CGPoint = sender.velocity(in: self.view)
if velocity.y > 0.4 {
UIScreen.main.brightness -= CGFloat(0.03)
}
else {
UIScreen.main.brightness += CGFloat(0.03)
}
}
}

Swift: How to access one object (ie. a button) within a #IBAction collection to return a sender to a new function?

I am creating a simple calculator and I have put a couple of number buttons in an #IBAction. I access a pressed button's sender.title to update the input label like this:
#IBAction func numberButtonTapped(_ sender: UIButton) {
guard let numberButton = sender.title(for: .normal) else {return}
updateInput(num: numberButton)
}
Now I would like to animate each pressed button with a simple CGAFFineTransform (I know how this is done) and I can put the animation into the #IBAction and obviously this works:
#IBAction func numberButtonTapped(_ sender: UIButton) {
guard let numberButton = sender.title(for: .normal) else {return}
sender.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) // completion handler to reverse the animation
updateInput(num: numberButton)
}
But I would like to put the animation into another function so that I can access it from different #IBActions and would not have to re-write the code again and again, but I have no idea how I can transfer the "sender" to the new function.
So how would I tell the new function (let's call it animateButton() ) which sender was pressed so that the sender.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)... would know which button to animate?
Thanks
Create a function that receives an UIButton as parameter with your animation code inside:
func animateButton(anySender: UIButton) {
// Your animation code here
sender.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) // completion handler to reverse the animation
}
Call your animation in any function that requires the animation, passing your sender as parameter:
#IBAction func numberButtonTapped(_ sender: UIButton) {
guard let numberButton = sender.title(for: .normal) else {return}
animateButton(anySender: sender)
updateInput(num: numberButton)
}
EXTRA: if you need to call this function from different controllers, consider making it an extension:
extension UIButton {
func customAnimate() {
// Your animation code here
self.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) // completion handler to reverse the animation
}
}
and call it from any button:
#IBAction func numberButtonTapped(_ sender: UIButton) {
guard let numberButton = sender.title(for: .normal) else {return}
sender.customAnimate()
updateInput(num: numberButton)
}

Rotating button animation doesn't work

I have a button, when it's tapped, it should rotate itself, here's my code:
#IBAction func calculateButtonTapped(_ sender: UIButton) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(M_PI)
rotateAnimation.speed = 3.0
rotateAnimation.repeatCount = 6000
calculateButton.layer.add(rotateAnimation, forKey: nil)
DispatchQueue.main.async {
self.openCircle(withCenter: sender.center, dataSource: self.calculator!.iterateWPItems())
self.calculateButton.layer.removeAllAnimations()
}
}
However, sometimes when I tap the button, it immediately goes back to normal state then rotates, sometimes the button changes to dark selected state, and doesn't animate at all, tasks after the animates will get finished. If I don't stop the animation, it starts after openCircle is finished.
What could be the cause?
You're not setting duration of your animation.
Replace this
rotateAnimation.speed = 3.0
with this
rotateAnimation.duration = 3.0
#alexburtnik and it's ok to block the main thread
No, it's not ok. You should add a completion parameter in openCircle method and call it whenever it's animation (or whatever) is finished. If you block main thread, you will have a frozen UI, which is strongly discouraged.
If you're unsure that calculateButtonTapped is called on main thread, you should dispatch first part of your method as well. Everything related to UI must be done on the main thread.
It should look similar to this:
#IBAction func calculateButtonTapped(_ sender: UIButton) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(M_PI)
rotateAnimation.duration = 3.0
rotateAnimation.repeatCount = .infinity //endless animation
calculateButton.layer.add(rotateAnimation, forKey: nil)
self.openCircle(
withCenter: sender.center,
dataSource: self.calculator!.iterateWPItems(),
completion: {
self.calculateButton.layer.removeAllAnimations()
})
}
func openCircle(withCenter: CGPoint, dataSource: DataSourceProtocol, completion: (()->Void)?) {
//do your staff and call completion when you're finished
//don't block main thread!
}
Try this out in order to rotate a button that is clicked by connecting the button to the action on a storyboard. You can of course call this function by passing any UIButton as the sender!
#IBAction func calculateButtonTapped(_ sender: UIButton) {
guard (sender.layer.animation(forKey: "rotate") == nil) else { return }
let rotationDuration: Float = 3.0
let animation = CABasicAnimation(keyPath: "transform.rotation")
animation.toValue = Float.pi * rotationDuration
animation.duration = CFTimeInterval(rotationDuration)
animation.repeatCount = .infinity
sender.layer.add(animation, forKey: "rotate")
}
Change the rotationDuration to whatever time length you want for a full rotation. You could also adjust the function further to take that as an argument.
Edit: Added a guard statement so that the rotations don't keep adding up every time that the button is tapped.
Thanks to everybody for answering, I found the solution myself after a crash course on multithreading, the problem is I blocked the main thread with openCircle method.
Here's the updated code:
#IBAction func calculateButtonTapped(_ sender: UIButton) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(M_PI)
rotateAnimation.speed = 3.0
rotateAnimation.repeatCount = .infinity
DispatchQueue.global(qos: .userInitiated).async {
self.openCircle(withCenter: sender.center, dataSource: self.calculator!.iterateWPItems()){}
DispatchQueue.main.sync {
self.calculateButton.layer.removeAllAnimations()
}
}
self.calculateButton.layer.add(rotateAnimation, forKey: nil)
}

I cannot use viewDidAppear and viewDidDisappear at the same project

radarMap is a UIWebView object and exitMapButton is its close button. To access map I used hidden actions. Now I want to add fade out and fade in animations while hiding. I did fade in but not fade out. How can I add fade out animation while hiding?
func openRadarMap(){
radarMap.hidden = false
exitMapButton.hidden = false
self.radarMap.alpha = 0
self.exitMapButton.alpha = 0
}
override func viewDidAppear(animated: Bool) {
if radarMap.hidden == false {
super.viewDidAppear(animated)
UIView.animateWithDuration(0.5, animations: {
self.radarMap.alpha = 1.0
self.exitMapButton.alpha = 1.0
}) }
}
func exitFromMap() {
exitMapButton.hidden = true
radarMap.hidden = true
self.exitMapButton.alpha = 0.0
self.radarMap.alpha = 0.0
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
UIView.animateWithDuration(0.5, animations: {
self.radarMap.alpha = 0.0
self.exitMapButton.alpha = 0.0
})
}
#IBAction func exitMapButtonAction(sender: AnyObject) {
exitFromMap()
}
#IBAction func webView(sender: UIButton) {
getAd()
openRadarMap()
let URL = "somewebpage.com/map"
let requestURL = NSURL(string:URL)
let request = NSURLRequest(URL: requestURL!)
radarMap.loadRequest(request)
//performSegueWithIdentifier("mapView", sender: nil)
}
The method viewDidAppear will be called after the view was removed from the view hierarchy. the description of the method says ,
Notifies the view controller that its view was removed from a view hierarchy.
So the view will not be actually visible at that time, I suggest you to write the fade out code in viewWillDisappear
You need to call super in all cases. If you don't call super for viewDidAppear: you can't have viewDidDisappear: ever called.

Resources