swift: Adding an action to an array of buttons - ios

I'm designing a simple Sudoku app and need to trigger an action when any one of the 81 buttons are clicked. I created an array of UIButtons in my ViewController:
class SudokuBoardController : UIViewController {
#IBOutlet var collectionOfButtons: Array<UIButton>?
override func viewDidLoad() {
collectionOfButtons.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
...
}
}
I'm able to add the buttons to the Array from the storyboard ok, its just when I try to addTarget I get this message:
Value of type 'Array<UIButton>?' has no member addTarget
Is there a solution to this problem that does not involve me creating 81 different outputs for each button?
Thanks for your help!
Cheers

You have an Array, so you want to iterate over the UIButtons in the array. And because you're in Swift, you'll want to do so in a Swifty way, not using a simple for loop.
collectionOfButtons?.enumerate().forEach({ index, button in
button.tag = index
button.addTarget(self, action: "buttonClicked:", forControlEvents: .TouchUpInside)
})
This also nicely handles the fact that collectionOfButtons is an optional by doing nothing if it is nil, as opposed to crashing.

You need to iterate through array of buttons and add target to each of the button. Try out following code
var index = 0
for button in collectionOfButtons! {
button.tag = index // setting tag, to identify button tapped in action method
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
index++
}

Related

Swift - Push to ViewController from UIButton in CollectionViewCell

I am trying to make my button, when tapped, to push to a new View Controller. I've tried many different ways but it won't trigger the function that I have it linked to. I also checked the 3D stack view of my layers and the button is on top and clickable, even when I check the background color, it's not being covered by anything else.
Does anyone have any ideas to what I am doing wrong?
For now I am trying to make the button print out the sentence in the console, however whenever I press it, the string doesn't pop up, so I haven't bothered to connect it to the view controller yet.
Also, I am coding this app without storyboards.
Here is my code below.
It is under the MainPageCell class declared as a UICollectionViewCell
private let playButton: UIButton = {
let button = UIButton()
button.setTitle("", for: .normal)
button.backgroundColor = .clear
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
return button
}()
#objc func buttonTapped() {
print("I PRESSED THE BUTTON")
}
This line is wrong:
button.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
You cannot assign self as the action target in a property declaration initializer, because the instance designated by self does not exist yet. There is no error or warning (I regard that as a bug), but the action method is never called.
Move that assignment elsewhere and rewrite it, like this:
self.playButton.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
Maybe try defining your button action under the UIView Class, I've had a problem like that before, only worked when i linked it to the View Class, Good luck

Swift - Assign titles to multiple buttons

I have multiple buttons that all get their titles from an array. I would like to be able to assign the title with a loop, but I can't figure how to refer to each button as I go through the loop.
Currently I am adding each title with a line of code like this:
button0.setTitle(title[0], forState: .Normal)
button1.setTitle(title[1], forState: .Normal)
button2.setTitle(title[2], forState: .Normal)
button3.setTitle(title[3], forState: .Normal)
etc...
I have added an IBOutlet to each button, but I am also using tags for another purpose, so if there is a way to use tags to assign the titles, I would be happy to do that.
Any thoughts?
You need an IBOutletCollection
In your Swift ViewController, assign all your buttons to the below
#IBOutlet var buttons: [UIButton]!
Then to assign the titles
var buttonTitles = ["Button1","Button2"]
for (index,button) in buttons.enumerate()
{
if buttonTitles.count > index
{
if let title : String = buttonTitles[index]
{
button.setTitle(title, forState: .Normal)
}
}
}

chnage image of button in swift everytime the user clicks on it

I have a button in a tableview cell. I want that initially the button has an image "A", when the user clicks on it, it changes to "B", when the user clicks on it again it changes back to "A".
Let the two images to be "A" and "B" in this scenario
Wherever the button is:
button.addTarget(self, action: "pressed:", forControlEvents: .TouchUpInside)
button.setImage(UIImage(named: "a.png")!, forState: .Normal)
button.tag = 999
func pressed(sender: UIButton!) {
if sender.tag == 999 {
sender.setImage(UIImage(named: "b.png")!, forState: .Normal)
sender.tag = 0
} else {
sender.setImage(UIImage(named: "a.png")!, forState: .Normal)
sender.tag = 999
}
}
You can add tag to the button.
// This code comes in viewDidLoad
button.tag = 1 // for A
button.titleLabel.text = "A"
// On-click of the button, check the tag and change the name
if button.tag == 1
{
button.tag = 2
button.titleLabel.text = "B"
}
Subclass UIButton, add click handler in this class and make reference in Interface Builder. Then, create boolean property in your class, which you will trigger in click handler every time. In didSet of this property set proper image
If we are just dealing with two states then this solution is easier and less messy. You can simply use UIButton states.
You can assign different images for default state and selected state in storyboard itself.
func pressed(sender:UIButton){
sender.selected = !sender.selected
}
This will just change the states and images will be displayed depending on state.

How to change UIButton label programmatically

When I first run my app, I retrieve a number from my server and display it for my UIButton label. Think of this as a notification number displayed on a red UIButton.
When I remove a notification within the app, I want my UIButton label decrement by 1. I am able to get the decremented number from the server after I delete a notification, but I can't display this new number on the UIButton. The button always displays the number when the app is first fired.
I call makeButtonView() method after I remove a notification to update the UIButton
func makeButtonView(){
var button = makeButton()
view.addSubView(button)
button.tag = 2
if (view.viewWithTag(2) != nil) {
view.viewWithTag(2)?.removeFromSuperview()
var updatedButton = makeButton()
view.addSubview(updatedButton)
}else{
println("No button found with tag 2")
}
}
func makeButton() -> UIButton{
let button = UIButton(frame: CGRectMake(50, 5, 60, 40))
button.setBackgroundImage(UIImage(named: "redBubbleButton"), forState: .Normal)
API.getNotificationCount(userID) {
data, error in
button.setTitle("\(data)", forState: UIControlState.Normal)
}
button.addTarget(self, action: "targetController:", forControlEvents: UIControlEvents.TouchUpInside)
return button
}
Use this code for Swift 4 or 5
button.setTitle("Click Me", for: .normal)
I need more information to give you a proper code. But this approach should work:
lazy var button : UIButton = {
let button = UIButton(frame: CGRectMake(50, 5, 60, 40))
button.setBackgroundImage(UIImage(named: "redBubbleButton"), forState: .Normal)
button.addTarget(self, action: "targetController:", forControlEvents: UIControlEvents.TouchUpInside)
return button
}()
func makeButtonView(){
// This should be called just once!!
// Likely you should call this method from viewDidLoad()
self.view.addSubview(button)
}
func updateButton(){
API.getNotificationCount(userID) {
data, error in
// be sure this is call in the main thread!!
button.setTitle("\(data)", forState: UIControlState.Normal)
}
}
There have been some updates since Swift 4. This works for me:
self.button.setTitle("Button Title", for: UIControl.State.init(rawValue: 0))
Replace button with your IBOutlet name. You can also use a variable or array in place of the quoted text.
It's fairly simple ...
import UIKit
class ViewController: UIViewController {
#IBOutlet var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("hello world", forState: UIControlState.Normal)
}
}
I believe if you set the state to normal, the value will propagate by default to other states so long as you haven't explicitly set a title for those states.
Said differently, if you set it for normal, it should also display this title when the button enters additional states
UIControlState.allZeros
UIControlState.Application
UIControlState.Disabled
UIControlState.Highlighted
UIControlState.Reserved
UIControlState.Selected
Lastly, here's Apple's documentation in case you have other questions.
Since your API call should be running on a background thread you need to dispatch your UI update back to the main thread like this:
DispatchQueue.main.async {
button.setTitle(“new value”, forState: .normal)
}
After setting the title, just a simple redraw of the button will do:
button.setNeedsDisplay();

UIButton Action not working in swift

I have two classes UIVIewController and UITableviewcell. In UITableviewcell, i have a button and the button action should be in UIViewController. How can i pass a class and set delegate in button action.
i tried this code,
//UIVIewController
cell.createButton(self)
func buttonClick(object:AnyObject){
println("Button Clicked")
}
//UITableViewCell
func createButton(delegate:AnyObject){
//button created.
button.addTarget(delegate, action: "buttonClick:", forControlEvents: UIControlEvents.TouchUpInside)
}
But the above code is not working. 'buttonClick' function is not calling
I had the same problem a while ago, trying to put self in place of "delegated".
otherwise try to take a look at this question:
link
I hope to be helped.

Resources