Xcode button (IBAction) only working once - ios

I'm new to iOS development. I'm trying to make a very simple application that rotates an image on a button press. The code works fine, but only once - i.e. the image rotates one time and then the button can no longer be pressed.
I've searched and found many similar issues, but nothing seems to fix my problem. Below is my ViewController.swift code.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var rotateImage: UIButton!
#IBAction func rotateImage(_ sender: Any) {
UIView.animate(withDuration: 2.0, animations: {
self.imageName.transform = CGAffineTransform(rotationAngle: (90.0 * .pi) / 90.0)
})
}
#IBOutlet weak var imageName: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Ideally, the code should allow for multiple button presses, enabling the user to rotate the image as much as they would like.

It gets called multiple times, but it does the same rotation each time so nothing visually changes.
You can look into using a variable for your rotationAngle based on the image orientation so that it keeps rotating in your desired direction with each click.
An easy example takes your code and then uses a random value for the angle.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageName: UIImageView!
#IBAction func rotateImage(_ sender: Any) {
UIView.animate(withDuration: 2.0, animations: {
self.imageName.transform = CGAffineTransform(rotationAngle: (CGFloat.random(in: 0 ..< 360) * .pi) / 90.0)
})
}
}

Related

Switching between two strings after button is pressed

So I am working on a BlackJack game for iOS using Swift. I have two buttons, HIT and STAY. Here is where my problem lies: When I press the originally, it is player 1's turn. But after clicking the HIT button, I want the turn label to oscillate between player 2 and player 1. So after player 1 clicks HIT button, I want the turn label to read player 2 and then player 1. Here is what I got so far:
class ViewController: UIViewController {
var deck = PlayingCardDeck()
#IBOutlet weak var cardLabel: UILabel!
#IBOutlet weak var playerTurn: UILabel!
#IBOutlet weak var scorePlayerOne: UILabel!
#IBOutlet weak var scorePlayerTwo: UILabel!
#IBAction func btnHit(_ sender: Any) {
cardLabel.text = String(describing: deck.drawCard()!)
playerTurn.text = String(describing: "Player 2")
}
#IBAction func btnStay(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
deck.shuffle()
}
}
With the method I have, it mimics what I want to happen (switches to player 2)
You need a variable to track which player is currently playing and then change the label based on those variable value.
var isFirstPlayerTurn: Bool = true
#IBAction func btnHit(_ sender: Any) {
cardLabel.text = String(describing: deck.drawCard()!)
isFirstPlayerTurn = !isFirstPlayerTurn
playerTurn.text = isFirstPlayerTurn ? "Player 1" : "Player 2"
}
Create two labels, one for each player. Then hide player 2's label and flip it while hidden in viewDidLoad(). Once the turn changes, flip both labels while simultaneously showing and hiding the labels based who's turn it is.
To flip the label, use CGAffineTransform. Use the labels' alpha property to show and hide the labels.
func changeTurn(_ player: Int) {
UIView.animate(withDuration: 0.5) {
playerTurn1.transform = CGAffineTransform(scaleX: -playerTurn1.transform.a, y: playerTurn1.transform.d)
playerTurn2.transform = CGAffineTransform(scaleX: -playerTurn2.transform.a, y: playerTurn2.transform.d)
playerTurn1.alpha = player == 1 ? 1 : 0
playerTurn2.alpha = player == 2 ? 1 : 0
}
}
Usage:
#IBOutlet weak var playerTurn1: UILabel!
#IBOutlet weak var playerTurn2: UILabel!
private var turn = 1
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
deck.shuffle()
playerTurn2.alpha = 0
playerTurn2.transform = CGAffineTransform(scaleX: -playerTurn2.transform.a, y: playerTurn2.transform.d)
}
#IBAction func btnHit(_ sender: Any) {
cardLabel.text = String(describing: deck.drawCard()!)
turn = turn == 1 ? 2 : 1
changeTurn(turn)
}
Make sure both labels are the same frames.

How can I click a button multiple times and repeat the function?

I am new to app development and am starting by writing a 'Wheel of fortune' app in Xcode. I have a button and a wheel, and when I first launch the app I can click the button and the wheel will spin, however if I click the button again nothing happens and I have to restart the app, to click the button and make it spin. I have enclosed the code that I've used. I would appreciate if anyone take a look over my code and help me :)
The image of my code.
Here's my code in case you can't open the image.
import UIKit
import AudioToolbox
import AVFoundation
var count = 1
class ViewController: UIViewController {
#IBOutlet weak var Wheel: UIImageView!
#IBOutlet weak var Label: UILabel!
#IBAction func rotateButton(_ sender: Any) {
playSound (sound: "spinSound", type: "mp3")
var usedNumbers = [Int]()
let rotateButton = Int.random(in: 1...20)
Label.text = String(rotateButton)
if usedNumbers.contains (rotateButton) {
} else {
usedNumbers.append(rotateButton)
}
UIView.animate(withDuration: Double(rotateButton)) { () -> Void in
self.Wheel.transform = CGAffineTransform(rotationAngle: CGFloat(rotateButton))
}
UIView.animate(withDuration: Double(rotateButton), delay: Double(rotateButton)/2, options: UIView.AnimationOptions.curveEaseIn, animations: { () -> Void in
self.Wheel.transform = CGAffineTransform(rotationAngle: CGFloat(rotateButton))
}, completion: nil)
}
}

Repeat animation from left to right smoothly

I'm trying to animate 3 squares from the left to the right. The squares should reappear from the left while they are disappearing from the right. The idea is that the squares represent clouds, so the idea is clear.
This is what I currently got:
class ViewController: UIViewController {
// contains the squares
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startAnimating()
}
func startAnimating() {
UIView.animateKeyframes(withDuration: 10, delay: 0, options: .repeat, animations: {
self.containerView.center.x += self.view.bounds.width
}, completion: nil)
}
}
This is the result:
The squares move from the left to the right. This is good. The problem is that once the animation is done the squares just reappear from where they started. It isn't a smooth continuous movement where the clouds move from the left to the right and slowly reappear from the left again. How do you achieve this? Should there be a second containerView? Or remove the containerView and animate individual clouds?
The answer Milan Nosáľ solved the problem. A note though:
I placed containerView2 above containerView1 with the exact same
constraints (they overlap each other). Just make sure IB doesn't
include containerView2 as a subview of containerView1.
I guess the simplest approach for you know is to use two exactly the same containers. You can use exactly the same code, and put the containerView2 above containerView to make sure that cover one another.
And then simply use:
class ViewController: UIViewController {
// contains the squares
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var containerView2: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// this will transform the containerView2 to the left to appear as if it was exactly to the left of the containerView
containerView2.transform = CGAffineTransform.identity.translatedBy(x: -self.view.bounds.width, y: 0)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startAnimating()
}
func startAnimating() {
UIView.animateKeyframes(withDuration: 10, delay: 0, options: .repeat, animations: {
self.containerView.center.x += self.view.bounds.width
self.containerView2.center.x += self.view.bounds.width
}, completion: nil)
}
}
The containerView2 will represent the clouds coming back from the left.
You could try something like putting containerView.center.x -= 200 right before starting the animation. This way it first appears from the left side.

swift photo reveal timer set up

I am trying to add a photo reveal quiz piece to a larger app that I'm creating. The picture of the set up looks like this so far(rough draft so to speak)
The 16 labeled items are there just to give a sense of the end product(they will be white tiles in the app). How can I program it so that they begin disappearing when they user taps reveal picture? NSTimer? Should they be labels or UIViews or ?? Thanks for any hints.
I can get this far, but would want to add code to make the tiles disappear randomly and roughly each second
import UIKit
class ViewController: UIViewController {
var gameTimer : NSTimer!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var tile15: UILabel!
#IBOutlet weak var tile16: UIView!
#IBOutlet var photoTilesHandler: [UIView]!
//#IBOutlet weak var viewTile: UIView!
//#IBOutlet var pictureTiles: [UILabel]!
#IBOutlet var buttonHandler: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func revealButton(sender: AnyObject) {
Hide()
print("button pressed")
}
func Hide() {
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.tile15.hidden = true
})
}
override func viewDidDisappear(animated: Bool) {
gameTimer.invalidate()
}
}
Put all of your UILabels/UIViews/etc in an IBOutletCollection. Then inside of dispatch_after's callback do the following steps:
0.- You might want to keep track of which tiles are already facedown. So this way you can know at every time which index you have already used and pick always new ones.
One way to achieve this is storing the full list of indexes available to select and instead of taking into account your IBOutletCollection for the following steps take as reference your unused indexes list and, after you randomly pick one, remove it from the list.
1.- Determine a/(a set of) random integers between 0...[your IBOutletCollection].length - 1]
2.- Use this numbers as an index to set the hidden property of [your IBOutletCollection] corresponding element to true
As your other question, my recommendation would be to use UIViews with your UILabel inside or UIButtons. The tap zone of labels is a bit hard to use.

How to get reference to UIView in scene dock?

I have UIViewController with 2 UIViews (scenes) in dock:
How can i display it in whatever place I need in my UIViewController? In other words, how can I get reference to this views from within my controller?
Swift 3, Xcode 8
1. Create a Reference
You can drag the UIViews to your ViewController and create outlets for them.
2. To Show UIView
You want to add it to the view as a subview. I hooked up those two buttons you see in my scene:
class ViewController: UIViewController {
#IBOutlet var view1: UIView!
#IBOutlet var view2: UIView!
// SHOW THE VIEWS
#IBAction func showView1(_ sender: UIButton) {
view.addSubview(view1)
view1.center = view.center
}
#IBAction func showView2(_ sender: UIButton) {
view.addSubview(view2)
view2.center = CGPoint(x: view.center.x, y: 100)
}
}
3. To Hide the UIViews
You will have to remove them from your view. I created actions for the Close buttons on the UIViews:
class ViewController: UIViewController {
#IBOutlet var view1: UIView!
#IBOutlet var view2: UIView!
// SHOW THE VIEWS
#IBAction func showView1(_ sender: UIButton) {
view.addSubview(view1)
view1.center = view.center
}
#IBAction func showView2(_ sender: UIButton) {
view.addSubview(view2)
view2.center = CGPoint(x: view.center.x, y: 100)
}
// HIDE THE VIEWS
#IBAction func hideView1(_ sender: UIButton) {
view1.removeFromSuperview()
}
#IBAction func hideView2(_ sender: UIButton) {
view2.removeFromSuperview()
}
}
Hope this helps!
Within my UIViewController's code keep references using #IBOutlet:
#IBOutlet weak var dockView: UIView!

Resources