Changing UIImage after click - swift

I have 13 images named 1 to 13 in my project. I would like the UIImageView to change from image 1 to 2, then 3, then 4 and so on, at the click of a UIButton.
My current code when the UIButton is clicked:
#IBAction func button(sender: Any) {
cardImage.image = UIImage(named: 1)
}
So I know I need to store the value in a variable and initially set the value to 1. Add 1 to the value every time I click the UIButton. That value needs to be somehow linked to cardImage.Image = UIImage. How can I achieve this?

So create an array of your filenames in your view controller, and an index into that array:
class ViewController: UIViewController {
lazy var filenames: [String] = {
return Array(1...13).map {String($0)}
}()
var filenameIndex = 0
Then write a function to load one of your images and increment the index:
func fetchImage() -> UIImage? {
let result = UIImage(named: filenames[filenameIndex])
filenameIndex = (filenameIndex + 1) % filenames.count
return result
}
(You could also use Yury's approach of managing an integer index and converting that to a filename, but my approach will let you manage cycling through any array of image filenames, whether they are sequential numbers or not.)

You need to store your current image number value in a variable and increase in when the button is pressed.
let imagesCount: Int = 13
var currentImageNumber: Int = 1
#IBAction func didPressButton(sender: Any) {
cardImage.image = UIImage(named: "\(currentImageNumber)")
currentImageNumber += 1
if currentImageNumber > imagesCount {
currentImageNumber = 1
}
}
Or you can use a shorter version without if condition.
let imagesCount: Int = 13
var currentImageNumber: Int = 0
#IBAction func didPressButton(sender: Any) {
cardImage.image = UIImage(named: "\(currentImageNumber + 1)")
currentImageNumber += 1
currentImageNumber %= imagesCount
}

Related

How do I animate a set of UIImages backward inside UIImageVIew - swift - programmatically

I have an array of UIImages and I'm trying to animate it in a particular way. Just like the instagram boomerang feature.
When the animation arrives to the last frame It goes backward and arrives to the first and the process continues.
What I've been doing till now has been this:
imageView.animationImages = [myImages]
imageView.animationDuration = 0.9
imageView.animationRepeatCount = 2
imageView.image = imageView.animationImages?.first
imageView.startAnimating()
How can I achieve that boomerang Loop if possible?
I suggest we override the startAnimating() method. This way we can provide our own logic of the animation and decide what the next frame should be.
We'll create a frameTimer that will replace the image of the UIImageView every equal fraction of the animationDuration. It'll iterate over the images array forwards and backwards until stopped with the stopAnimating() method.
class BoomerangImageView: UIImageView {
// MARK: Overrides
override func startAnimating() {
guard let frames = animationImages, frames.count > 1 else {
return
}
image = frames.first
let timePerFrame = animationDuration / Double(frames.count)
let timer = Timer(fire: Date(timeIntervalSinceNow: timePerFrame),
interval: timePerFrame,
repeats: true) { [weak self] _ in
self?.changeFrame()
}
RunLoop.current.add(timer, forMode: .common)
frameTimer = timer
}
override func stopAnimating() {
frameTimer?.invalidate()
frameTimer = nil
currentFrameIndex = 0
}
// MARK: Private
private var frameTimer: Timer?
private var isAnimatingForward = true
private var currentFrameIndex = 0 {
didSet {
if let frames = animationImages,
currentFrameIndex >= 0,
currentFrameIndex < frames.count {
image = frames[currentFrameIndex]
}
}
}
private func changeFrame() {
guard let frames = animationImages, frames.count > 1 else {
stopAnimating()
return
}
let canAnimateForward = currentFrameIndex < frames.count - 1
let canAnimateBackward = currentFrameIndex > 0
isAnimatingForward = (isAnimatingForward && canAnimateForward) || (!isAnimatingForward && !canAnimateBackward)
currentFrameIndex += isAnimatingForward ? 1 : -1
}
}
Then in your view controller, you run the animation just like you planned:
class ViewController: UIViewController {
#IBOutlet weak var imageView: BoomerangImageView!
let frames: [UIImage] = [
// your images here
]
override func viewDidLoad() {
super.viewDidLoad()
imageView.animationImages = frames
imageView.animationDuration = 2
imageView.startAnimating()
}
}
It should look something like this:

Setting random element to a variable in Swift

I'm trying to make a game where you press the back of a card, it displays a random card and then removes that card from the card deck array. When I simulate the program it runs for a random amount of times and then gives me the Index out of range error.
Does anyone see where I have done something wrong?
var cardDeck = [card1, card2, card3 etc....]
var randomCard: Int = 0
#IBAction func row1card5tap(_ sender: UITapGestureRecognizer) {
randomCard = Int.random(in: 0...51)
if cardDeck.contains(cardDeck[randomCard]) {
row1card5.image = cardDeck[randomCard+1]
cardDeck.remove(at: randomCard)
print("removed:" + "\(cardDeck[randomCard])")
} else {
print("card already removed")
}
}
You should make sure the array count is bigger than the requestedIndex + 1 before subscripting
var cardDeck = [card1, card2, card3 etc....]
var randomCard: Int = 0
#IBAction func row1card5tap(_ sender: UITapGestureRecognizer) {
randomCard = Int.random(in: 0...51)
if cardDeck.count > randomCard + 1 {
row1card5.image = cardDeck[randomCard+1]
cardDeck.remove(at: randomCard)
print("removed:" + "\(cardDeck[randomCard])")
} else {
print("card already removed")
}
}
If you want
it displays a random card and then removes that card from the card deck array
then the upper bound of the random array should be always the current number of cards in the deck
var cardDeck = [card1, card2, card3 etc....]
#IBAction func row1card5tap(_ sender: UITapGestureRecognizer) {
if cardDeck.isEmpty { return }
let randomIndex = Int.random(in: 0..<cardDeck.count)
row1card5.image = cardDeck[randomIndex]
cardDeck.remove(at: randomIndex)
}

Why is Label Text not Updating to Specified Variable(swift)

mega-noob question:
So right now I'm just trying to test my variable system in my gpa project. When popup button is altered it alters the value of a courses point total for example
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
func UPDTmathG(_ sender: Any) {
if let title = (sender as AnyObject).titleOfSelectedItem, title == "A+" {
mathPoints = 4.3;
}
if let title = (sender as AnyObject).titleOfSelectedItem, title == "A" {
mathPoints = 4;
}
Now to test that these variables are actually changing an learn how to change string values.
testLabel.stringValue = String(mathPoints);
Ideally, when this added to the func UPDTmathG(_ sender: Any) {.
It would update the string value of the label to be the value of the variable, but the string isn't updating. Any ideas?
Thanks:)

WatchOS2 - How to make all row groups or each row group a different color when selected?

I'm trying to make it so a row (a group in a row) changes to purple when clicked and when a new row is clicked it turns purple and the previous one reverts back to the default OS color.
When I select a row it turns purple correctly but when I tap another row the previous row doesn't revert to the normal black row.
I'd also like to take it a step further and make the 5 rows each turn a different color. With each row group reverting back to the default watchOS black color when a new row is selected.
It's interesting because the default color doesn't seem to be dark grey or black. Black is essentially the same as clear since the main view bg is black. Dark grey is lighter than the default.
class TableInterfaceController: WKInterfaceController {
#IBOutlet var tableOutlet: WKInterfaceTable!
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
let arrayOfObjects = ["ORANGE","PURPLE","BLUE","GRAY","YELLOW"]
tableOutlet.setNumberOfRows(arrayOfObjects.count, withRowType: "row")
for (var i = 0; i < arrayOfObjects.count; i++) {
let row = tableOutlet.rowControllerAtIndex(i) as? RowController
let labelValue = arrayOfObjects[i]
row?.labelOutlet.setText(labelValue)
}
}
override func table(table: WKInterfaceTable, didSelectRowAtIndex rowIndex: Int) {
let row = tableOutlet.rowControllerAtIndex(rowIndex) as? RowController
row?.groupOutlet.setBackgroundColor(UIColor.purpleColor())
if (row!.isSelected) {
row!.isSelected = false
row?.groupOutlet.setBackgroundColor(UIColor.blackColor())
}
else {
row?.isSelected = true
row?.groupOutlet.setBackgroundColor(UIColor.purpleColor())
}
}
class RowController: NSObject {
#IBOutlet var labelOutlet: WKInterfaceLabel!
#IBOutlet var groupOutlet: WKInterfaceGroup!
var isSelected : Bool = false
}
I want it like this
You need to implement the following changes.
let arrayOfObjects = [["text":"ORANGE", "color":UIColor.orangeColor(),"status":false] as NSMutableDictionary ,["text":"PURPLE", "color":UIColor.purpleColor(),"status":false] as NSMutableDictionary,["text":"BLUE", "color":UIColor.blueColor(),"status":false] as NSMutableDictionary,["text":"GRAY", "color":UIColor.grayColor(),"status":false] as NSMutableDictionary,["text":"YELLOW", "color":UIColor.yellowColor(),"status":false] as NSMutableDictionary];
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
tableOutlet.setNumberOfRows(arrayOfObjects.count, withRowType: "row")
for (var i = 0; i < arrayOfObjects.count; i++) {
let row = tableOutlet.rowControllerAtIndex(i) as? RowController
row?.updateData(arrayOfObjects[i] as NSMutableDictionary);
}
}
override func table(table: WKInterfaceTable, didSelectRowAtIndex rowIndex: Int) {
let selectedRow = tableOutlet.rowControllerAtIndex(rowIndex) as? RowController
for (var i = 0; i < arrayOfObjects.count; i++) {
let row = tableOutlet.rowControllerAtIndex(i) as? RowController
//--
let data = arrayOfObjects[i] as NSMutableDictionary
data["status"] = (row == selectedRow);
row?.updateData(arrayOfObjects[i] as NSMutableDictionary);
}
}
class RowController: NSObject {
#IBOutlet var labelOutlet: WKInterfaceLabel!
#IBOutlet var groupOutlet: WKInterfaceGroup!
func updateData(data:NSMutableDictionary){
let labelValue = data["text"] as! String
let color = data["color"] as! UIColor
let status = data["status"] as! Bool
//--
self.labelOutlet.setText(labelValue)
self.groupOutlet.setBackgroundColor(status ? color : UIColor.blackColor());
}//F.E
}

How to rewrite code for a (too) big IF statement?

I have a project with some UIbuttons with different UIimages displayed in it. Through user interaction, there could be any of the UIimages in the UIButtons. There are like around 1000 images in the project. I have initialised a variable named 'i'. And a IBAction named buttonTapped on all the buttons. Now I want to update variable 'i' and use the value of 'i' for every different possible `UIImage'. I can do this with an IF statement as shown here:
#IBAction func buttonTapped(sender: UIButton) {
if sender.currentImage == UIImage(named: "image1") {
i = 1
print(i)
// use the value of i
} else if sender.currentImage == UIImage(named: "image2") {
i = 2
print(i)
// use the value of i
} else if sender.currentImage == UIImage(named: "image3") {
i = 3
print(i)
// use the value of i
} else if // and so on
But I would like a better solution then an IF statement with around 1000 else if(s). I have tried, but I am not able to rewrite the code in a concise matter. What could I use instead of the IF statement? Some kind of loop?
A crude solution (assuming the indices are all sequential) would be
for i in 1 ... 1000 { // or whatever the total is
if sender.currentImage == UIImage(named: "image\(i)") {
print(i)
// use i
}
}
A better solution, especially if the names are not in the format you give, is to have an array of structs (or just an array of images, if the numbers are all sequential)...
struct ImageStruct {
var image: UIImage
var index: Int
}
var imageStructs:[ImageStruct]... // Some code to fill these
...
#IBAction func buttonTapped(sender: UIButton) {
let matches = self.imageStructs.filter( { $0.image == sender.currentImage } )
if let match = matches.first {
// use match.index
}
}