I have a UIImageView that displays picture cards of the Alphabet, A-Z. I use a segment control to determine if the cards display in order A-Z or are randomly displayed.
Whenever I start the in order, if I switch over to random and then back to in order, the index starts at where I left off. So if I was at card C, switch to random, do a few and then switch back to in order, the next card is D, I want it to start over at A (index 1).
How would I reset the index counter to index 1?
I'm using the below variable:
var number = 1
I called that on my button click:
#IBAction func dealBTN(_ sender: Any) {
if segControl.selectedSegmentIndex == 0 {
imageView.image = UIImage(named: "card1")
imageView.image = UIImage(named: "card\(number)")
number = number % 26 + 1
}
else if segControl.selectedSegmentIndex == 1 {
let Number = Int.random(in: 1...26)
imageView.image = UIImage(named: "card\(Number)")
}
}
You are using a property called number to keep track of your next card to display when they are in order. Simply set it to 1 whenever you are picking a random card so that the index is back at 1 the next time you choose an ordered card:
if segControl.selectedSegmentIndex == 0 {
imageView.image = UIImage(named: "card\(number)")
number = number % 26 + 1
}
else if segControl.selectedSegmentIndex == 1 {
let random = Int.random(in: 1...26)
imageView.image = UIImage(named: "card\(random)")
number = 1
}
Related
I want to change colors of all adjacent buttons in my UIStackView when any button is pressed. In the end I will turn this into an animation - almost like a ripple effect.
I looked at the Combine Framework but that seems to have too many unrelated features for what I need. Because the user could press any button, I don't think creating an observer for every button is an economical solution. I was hoping there was a way to find the adjacent view of a button's properties and influence that in some way?
Is there a simple solution to this problem, or do I need to build a complex matrix of IF statements, which is what I'm prepared to do in any case.
All my buttons are in a UIStackView (8 horizontal stack-views in 1 x vertical stack-view)
Here follows my non-ideal solution:
if sender.tag == 1 then {
view1.layer.backgroundColor = blue
view2.layer.backgroundColor = blue
view3.layer.backgroundColor = blue
} else if sender.tag == 2 then {
view4.layer.backgroundColor = blue
view5.layer.backgroundColor = blue
view6.layer.backgroundColor = blue
}...
You can find "adjacent" buttons by searching a 2-D array.
For example... if you have an 8x8 "grid" of buttons, you'll have 8 "rows" of 8 "columns". When you tap any button, search through the rows to find the row and column of the tapped button.
Suppose you find it at Row 4 : Col 4, then to find the adjacent buttons subtract and add a row and column in each direction:
// arrays are Zero based
// if tapped button is at
array[3][3]
// the button above it is at
array[3 - 1][3]
// the button to the left it is at
array[3][3 - 1]
// the button to the right it is at
array[3][3 + 1]
// the button below it is at
array[3 + 1][3]
If by adjacent you also want to include diagonals, you just need another set of "+/-" for "above and to the left" / "above and to the right" / "below and to the left" / "below and to the right":
Here's a complete example (code only... no #IBOutlet or #IBAction connections):
class ViewController: UIViewController {
// 2-D array to track the buttons
// we could use the stack views and their arrangedSubviews, but
// this will avoid repeated unwrapping of optionals
var arrayOfButtons: [[UIButton]] = []
// add a reset button above the grid
let resetBtn = UIButton()
// add a segmented control to select fully adjacent or diagonal
let segCtrl = UISegmentedControl(items: ["Full Only", "Include Diagonal"])
override func viewDidLoad() {
super.viewDidLoad()
// create an 8x8 "grid" of buttons
let outerStack = UIStackView()
outerStack.axis = .vertical
outerStack.distribution = .fillEqually
// we'll use Row:Col for the button labels
for row in 0..<8 {
var thisRow: [UIButton] = []
let rowStack = UIStackView()
rowStack.distribution = .fillEqually
for col in 0..<8 {
let b = UIButton()
b.backgroundColor = .blue
b.setTitle("\(row):\(col)", for: [])
b.setTitleColor(.white, for: .normal)
b.setTitleColor(.gray, for: .highlighted)
// add a border so we can see the frames
b.layer.borderWidth = 1.0
b.layer.borderColor = UIColor.yellow.cgColor
// square buttons
b.heightAnchor.constraint(equalTo: b.widthAnchor).isActive = true
// add button to rowStack
rowStack.addArrangedSubview(b)
// add button to 2-D array
thisRow.append(b)
// add target for button
b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
}
// add rowStack to outerStack
outerStack.addArrangedSubview(rowStack)
// add this row of buttons to 2-D array
arrayOfButtons.append(thisRow)
}
// outerStack properties
outerStack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(outerStack)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain outerStack width
outerStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 16.0),
outerStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -16.0),
// center vertically
outerStack.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
resetBtn.backgroundColor = .systemTeal
resetBtn.setTitle("Reset", for: [])
resetBtn.setTitleColor(.white, for: .normal)
resetBtn.setTitleColor(.gray, for: .highlighted)
resetBtn.addTarget(self, action: #selector(resetTapped(_:)), for: .touchUpInside)
[resetBtn, segCtrl].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
NSLayoutConstraint.activate([
resetBtn.bottomAnchor.constraint(equalTo: outerStack.topAnchor, constant: -20.0),
resetBtn.leadingAnchor.constraint(equalTo: outerStack.leadingAnchor),
resetBtn.trailingAnchor.constraint(equalTo: outerStack.trailingAnchor),
segCtrl.topAnchor.constraint(equalTo: outerStack.bottomAnchor, constant: 20.0),
segCtrl.leadingAnchor.constraint(equalTo: outerStack.leadingAnchor),
segCtrl.trailingAnchor.constraint(equalTo: outerStack.trailingAnchor),
])
segCtrl.selectedSegmentIndex = 0
}
#objc func resetTapped(_ sender: Any?) -> Void {
arrayOfButtons.forEach { thisRow in
thisRow.forEach { btn in
btn.backgroundColor = .blue
}
}
}
#objc func btnTapped(_ sender: Any?) -> Void {
guard let tappedBtn = sender as? UIButton else {
return
}
// find the row and column for the tapped button
var row: Int = -1
var col: Int = -1
for r in 0..<arrayOfButtons.count {
let thisRow = arrayOfButtons[r]
if let c = thisRow.firstIndex(of: tappedBtn) {
// found the tapped button
row = r
col = c
break
}
}
if row == -1 || col == -1 {
// did not find the tapped button in the grid!!!
return
}
// we found the row:col of the tapped button
var adjacentButtons: [UIButton] = [
tappedBtn
]
if segCtrl.selectedSegmentIndex == 0 {
// adjacent means ONLY above, left, right, below
if row > 0 {
// get button above
adjacentButtons.append(arrayOfButtons[row - 1][col])
}
if col > 0 {
// get button to the left
adjacentButtons.append(arrayOfButtons[row][col - 1])
}
if row < arrayOfButtons.count - 1 {
// get button below
adjacentButtons.append(arrayOfButtons[row + 1][col])
}
if col < arrayOfButtons[row].count - 1 {
// get button to the right
adjacentButtons.append(arrayOfButtons[row][col + 1])
}
} else {
// adjacent includes diagonals
if row > 0 {
// get button above and to the left
if col > 0 {
adjacentButtons.append(arrayOfButtons[row - 1][col - 1])
}
// get button above
adjacentButtons.append(arrayOfButtons[row - 1][col])
// get button above and to the right
if col < arrayOfButtons[row].count - 1 {
adjacentButtons.append(arrayOfButtons[row - 1][col + 1])
}
}
if col > 0 {
// get button to the left
adjacentButtons.append(arrayOfButtons[row][col - 1])
}
if col < arrayOfButtons[row].count - 1 {
// get button to the right
adjacentButtons.append(arrayOfButtons[row][col + 1])
}
if row < arrayOfButtons.count - 1 {
// get button below and to the left
if col > 0 {
adjacentButtons.append(arrayOfButtons[row + 1][col - 1])
}
// get button below
adjacentButtons.append(arrayOfButtons[row + 1][col])
// get button below and to the right
if col < arrayOfButtons[row].count - 1 {
adjacentButtons.append(arrayOfButtons[row + 1][col + 1])
}
}
}
adjacentButtons.forEach { btn in
btn.backgroundColor = .red
}
}
}
How do I update a UILabel which calculates the total of two amounts and prints the answer on the label?
I have $50 and want to add $50 to the total on the label every time the button is pressed.
One of the amounts would be the total that is already printed on the label from the first click of the button. So I want to click the button again and it to add the 50 each time I click.
I'm using the latest versions of xcode and swift.
The button and label are connected and tested by printing to console.
func totalMoneyUpdate() {
totalMoney.text = "$50.00"
// this gives me $50 on the label but don't add it each time i hit the button.
// I have tried to change the label to a Int but have fail.
totalMoney:Int = $50.00
//and tried totalMoney:Int + $50.00
//I tried
var a = 25
var b = 25
let totalMoney.text = (a + b) // this only prints $50 to the label once.
//I tried
totalMoney.text = "50.00"
var a = (totalMoney.text as! NSString).floatValue
var b = (totalMoney.text as! NSString).floatValue
var sum = a + b
totalMoney.text = "\(sum)"
// this prints 100 to the label but don't do anything next time I press.
}
I think I somehow need to update the label each time button is pressed.
I expect the output to add 50 to the total every time I click the button and display the total on the label.
From what i have understood you want to add 50 every time you click a button. You could try the below code.
//Making local variable to keep track of money added
var addedMoney : Double = 0.00
// amount you want to add every button click
var addFifty : Double = 50.00
override func viewDidLoad() {
super.viewDidLoad()
// making sure totalMoney label is shown as 0.00 at start up
convertToCurrency(value: addedMoney)
}
#IBAction func addMoney(_ sender: Any) {
// in button click you want to get the local variable and addFifty
addedMoney = addedMoney + addFifty
convertToCurrency(value: addedMoney)
}
func convertToCurrency(value : Double) {
let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = .currency
currencyFormatter.locale = Locale.current
if let priceString = currencyFormatter.string(from: value as NSNumber) {
totalMoney.text = priceString
}
}
Example of code for animation:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
self.imageView.frame.origin.y = self.imageView.frame.origin.y + 100.0
self.imageView.animationImages = images
self.imageView.animationDuration = 2.0
self.imageView.animationRepeatCount = 1
self.imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)
NOTE: Both startAnimation calls are in the main thread but not in the same run loop.
In my case I want to change jumpImages to another set of images. I should cancel the previous animation and start the new one but it looks like I set the last image in jumpImages sequence instead.
How to solve this issue?
Okay, I think I'm understanding your problem now -- from your statement, "[...] but it looks like I set the last image in jumpImages sequence instead," I think you mean that when you call startAnimation(index: 1), you are seeing just the last frame of the index 0 animations, and none of the index 1 animations.
Assuming that is right, your problem here going to be a race condition.
When you call startAnimation() then second time with index 1, the first animation may or may not still be in progress.
The solution will be to call self.imageView.stopAnimating() before you change all the images and start the new animation. The best practice would be to check the imageView.isAnimating flag before you call it. Something like this:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
if self.imageView.isAnimating {
self.imageView.stopAnimating()
}
self.imageView.frame.origin.y = self.imageView.frame.origin.y + 100.0
self.imageView.animationImages = images
self.imageView.animationDuration = 2.0
self.imageView.animationRepeatCount = 1
self.imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)
Also, since you're in a function and not within a closure, you can remove all those references to self., which makes things a bit shorter:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
if imageView.isAnimating {
imageView.stopAnimating()
}
imageView.frame.origin.y = imageView.frame.origin.y + 100.0
imageView.animationImages = images
imageView.animationDuration = 2.0
imageView.animationRepeatCount = 1
imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)
I want to show a note/label on my tableview background when/if there is no data to load, or data is being fetched etc.
I can't see what am I doing wrong here. Xcode is showing a warning "Will never be executed" on this line of code: if mostUpTodateNewsItemsFromRealm?.count < 1 {
Here is the method.
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// create a lable ready to display
let statusLabel: UILabel = UILabel(frame: CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height))
statusLabel.textColor = globalTintColor
statusLabel.textAlignment = NSTextAlignment.Center
self.tableView.backgroundView = statusLabel
self.tableView.backgroundView?.backgroundColor = colourOfAllPickerBackgrounds
// 1) Check if we have tried to fetch news
if NSUserDefaults.standardUserDefaults().valueForKey("haveTriedToFetchNewsForThisChurch") as! Bool == false {
statusLabel.text = "Busy loading news..."
} else {
// If have tried to fetch news items = true
// 2) check church has channels
let numberOfChannelsSubscribedToIs = 0
if let upToDateSubsInfo = upToDateChannelAndSubsInfo {
let numberOfChannelsSubscribedToIs = 0
for subInfo in upToDateSubsInfo {
if subInfo.subscribed == true {
numberOfChannelsSubscribedToIs + 1
}
}
}
if numberOfChannelsSubscribedToIs < 1 {
// if no channels
// show messsage saying you aren't subscribed to any channels.
statusLabel.text = "Your church hasn't setup any news channels yet."
} else {
// 3) if we have tried to fetch news AND the church DOES have channels
// check if we have any news items to show
if mostUpTodateNewsItemsFromRealm?.count < 1 {
// If no news items
statusLabel.text = "Your church hasn't broadcast and news yet."
} else {
// if have tried to fetch AND church has channels AND there ARE news items
// remove the background image so doesn't show when items load.
self.tableView.backgroundView = nil
}
}
}
// in all circumstances there will be one section
return 1
}
Your code first created a constant:
let numberOfChannelsSubscribedToIs = 0
And then you check whether it is less than 1:
if numberOfChannelsSubscribedToIs < 1
Since it is a constant, it will never change. This means that the if clause will always be executed. Thus, the else clause will never be executed.
So first you need to make this constant variable:
var numberOfChannelsSubscribedToIs = 0
Then just change this:
if subInfo.subscribed == true {
numberOfChannelsSubscribedToIs + 1
}
to this:
if subInfo.subscribed == true {
numberOfChannelsSubscribedToIs += 1
}
This way, numberOFChannelSubscribedToIs can be some number other than 0. And the else clause can be executed.
var and let are very different!
I'm coding a carousel that display a string array. The string element that is selected is to be displayed in a UIView.
var plotValue:Int = 0
var flag:Int = 1
let plotList: [String] = ["Phenotype","SNP","Synthesis","PheWAS","Circos"]
Basically, the user can swipe left or right the UILabel gets updated with a new value from the plotList.
Swipe left decrements a counter by -1, swipe right increments a counter by +1.
If the user reaches the initial value of the plotList[0] continues swiping left, the code will wrap around and start from the maximum element in the plotList.
If the user reaches the maximum value of the plotList and continues to swipe right, the code will wrap around and start from the plotList[0].
Once the users taps the UIView, another process is launched.
var swipeCarouselLeft: UISwipeGestureRecognizer =
UISwipeGestureRecognizer(target: self, action: "carouselLeft")
swipeCarouselLeft.direction = UISwipeGestureRecognizerDirection.Left
self.labelView.addGestureRecognizer(swipeCarouselLeft)
var swipeCarouselRight: UISwipeGestureRecognizer =
UISwipeGestureRecognizer(target: self, action: "carouselRight")
swipeCarouselRight.direction = UISwipeGestureRecognizerDirection.Right
self.labelView.addGestureRecognizer(swipeCarouselRight)
var tapButton:UITapGestureRecognizer =
UITapGestureRecognizer(target: self, action: "carouselTap")
self.labelView.addGestureRecognizer(tapButton)
Here are the functions defined.
func carouselLeft(){
AudioServicesPlaySystemSound(1052)
flag = -1
getLabel(flag)
}
func carouselRight(){
AudioServicesPlaySystemSound(1054)
flag = 1
getLabel(flag)
}
and
func getLabel(flag: Int){
plotValue = plotValue + flag
println("plotValue \(plotValue)")
switch plotValue {
case (plotList.count):
plotValue = 0
case (-1):
plotValue = plotList.count - 1
default:
plotValue = 0
}
UIView.animateWithDuration(2, animations: { () -> Void in
self.labelOutlet.textColor = UIColor.blueColor()
})
self.labelOutlet.text = plotList[plotValue]
println("\(plotList[plotValue])UIView")
}
func carouselTap(){
AudioServicesPlaySystemSound(1057)
}
Basically, when user swipe the resulting string is either the first or last element in the plotList array, none of the other elements in the array are shown.
Maybe I'm overthinking this and there's a simpler way to do this? or the Apple "preferred" way of doing this? or a more OOP??
Changed the logic to this and works and will wrap in both directions.
func getLabel(flag: Int){
if (flag == -1 && plotValue == 0){
plotValue = plotList.count - 1
}
else if (flag == 1 && plotValue == plotList.count - 1)
{
plotValue = 0
}
else
{
plotValue = plotValue + flag
}
UIView.animateWithDuration(2, animations: { () -> Void in
self.labelOutlet.textColor = UIColor.blueColor()
})
self.labelOutlet.text = plotList[plotValue]
}