Using Swift, how do I make a:
Button with words "start", that changes to "pause" and vice versa when tapped? Not just once, but can be done infinitely? (e.g. in a stopwatch app)
Custom image button that changes between 2 image when tapped, that can also be done (tapped & change) infinitely?
Step 1: Create a Bool variable
var isPlaying:Bool = false
Step 2: Connect your button with this IBAction method:
#IBAction func btnStartStop(sender: UIButton) {
if isPlaying{
isPlaying = false
sender.setTitle("Pause", forState: UIControlState.Normal)
sender.setImage(pauseImage, forState: .Normal)
//Pause Stopwatch
}
else{
isPlaying = true
sender.setTitle("Play", forState: UIControlState.Normal)
sender.setImage(playImage, forState: .Normal)
//Play Stopwatch
}
}
In your storyboard, set the Default state title to "Start" and the Selected state title to "Pause"
Or alternatively, if you're doing it in code:
startPauseButton.setTitle("Start", forState: .Normal)
startPauseButton.setTitle("Pause", forState: .Selected)
In your IBAction func, toggle the button.selected property between true & false
#IBAction func toggleStopwatch(button:UIButton) {
if button.selected {
// Pause the stopwatch
} else {
// Start the stopwatch
}
button.selected = !button.selected
}
You can also set different images for different states: Default, Highlighted, Selected, Disabled
In the #IBAction of the button (assuming you are using storyboards), you change the title or image of the button to what you want. You can also check for the title or image and perform an action based on what you have.
#IBAction func pressbutton(sender: UIButton) {
switch sender.titleLabel?.text {
case "Play":
play()
case "Pause":
pause()
default:
other()
}
sender.titleLabel?.text = "Whatever you want"
}
Or with images (assuming you have an images array somewhere that stores the images, which you should):
#IBAction func pressbutton(sender: UIButton) {
switch sender.imageView!.image! {
case images[0]:
play()
case images[1]:
pause()
default:
other()
}
sender.imageView?.image = UIImage(...)
}
Try this....
#IBAction func btnStopWatch(sender: UIButton) {
if btnStopWatch.selected == true {
btnStopWatch.setTitle("stop", forState: UIControlState.Normal)
btnStopWatch.setImage(stopImage, forState: UIControlState.Normal)
btnStopWatch.selected = false
} else {
btnStopWatch.setTitle("start", forState: UIControlState.Normal)
btnStopWatch.setImage(startImage, forState: UIControlState.Normal)
btnStopWatch.selected = true
}
}
#1. Toggle a UIButton title
With Swift 3, UIButton has a method called setTitle(_:for:). setTitle(_:for:) has the following declaration:
func setTitle(_ title: String?, for state: UIControlState)
Sets the title to use for the specified state.
The following Playground code shows how to use setTitle(_:for:) in order to toggle the title of a button according to its state:
import PlaygroundSupport
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
let button = UIButton(type: UIButtonType.system)
button.addTarget(self, action: #selector(toggle(sender:)), for: UIControlEvents.touchUpInside)
// Set button's states
button.setTitle("Start", for: UIControlState.normal)
button.setTitle("Pause", for: UIControlState.selected)
// set layout
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
}
/// trigger action when button is tapped
func toggle(sender: UIButton) {
sender.isSelected = !sender.isSelected
print("Button state: \(sender.isSelected)")
}
}
let vc = ViewController()
PlaygroundPage.current.liveView = vc
Preview your view controller in the Playground assistant editor using View > Assistant Editor > Show Assistant Editor
#2. Toggle a UIButton image
UIButton has a method called setImage(_:for:). setImage(_:for:) has the following declaration:
func setImage(_ image: UIImage?, for state: UIControlState)
Sets the image to use for the specified state.
The following Playground code shows how to use setImage(_:for:) in order to toggle the title of a button according to its state:
import PlaygroundSupport
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
let button = UIButton()
button.addTarget(self, action: #selector(toggle(sender:)), for: UIControlEvents.touchUpInside)
// Set button's states
button.setImage(UIImage(named: "on.png"), for: UIControlState.normal)
button.setImage(UIImage(named: "off.png"), for: UIControlState.selected)
// set layout
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let heightConstraint = button.heightAnchor.constraint(equalToConstant: 100)
let widthConstraint = button.widthAnchor.constraint(equalToConstant: 100)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, heightConstraint, widthConstraint])
}
/// trigger action when button is tapped
func toggle(sender: UIButton) {
sender.isSelected = !sender.isSelected
print("Button state: \(sender.isSelected)")
}
}
let vc = ViewController()
PlaygroundPage.current.liveView = vc
Preview your view controller in the Playground assistant editor using View > Assistant Editor > Show Assistant Editor
Related
I've created a custom button and set two images, one is for normal, and the other is for the selected mode. But the voice-over always says the normal image name text when the button is not selected. I've tried a lot but could not disable it.
When I disable the button imageView accessibility it is not working.
button.imageView?.isAccessibilityElement = false
When I disable the button accessibility, the voice-over is not working in accessibility mode.
button.isAccessibilityElement = false
If I remove the '.normal' mode image then it works, but normal mode image functionality is not considered/worked there. I'm surfing a lot. Help anyone and thanks in advance.
Code:
self.setImage(UIImage.init(named: imageName1), for: .normal)
self.setImage(UIImage.init(named: imageName1), for: .selected)
You can do it with a simple function, this is an example...
Declare your image and your button under controller class:
let newButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = .red
button.tintColor = .white
button.imageView?.contentMode = .scaleAspectFit
button.clipsToBounds = true
return button
}()
let image1 = UIImage(named: "magnifier") // image in my assets
let image2 = UIImage(named: "user") // image in my assets
in viewDidLoad addTarget to your button and call the control function, in my case:
handleCange()
newButton.addTarget(self, action: #selector(handleCange), for: .touchUpInside)
now set control variable and handleCange() func
var controlButtonState = false
#objc fileprivate func handleCange() {
if controlButtonState == true {
newButton.setImage(image1, for: .normal)
controlButtonState = false
} else {
newButton.setImage(image2, for: .normal)
controlButtonState = true
}
}
Basically, it is not possible indirect way. On the other hand we can use accessibilityLabel
I found an alternative solution. I think it is not a proper solution. Nonetheless, I am sharing this alternative solution. The question is open if anyone gets any proper solutions. Thanks!
import UIKit
struct RadioViewControllerConstant {
static let dayImage = "RadioButtonDontSelect"
static let dayImageSelected = "RadioButtonSelect"
}
class RadioViewController: UIViewController {
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
let image1 = UIImage(named: RadioViewControllerConstant.dayImageSelected)
let image2 = UIImage(named: RadioViewControllerConstant.dayImage)
var controlButtonState1 = false
var controlButtonState2 = false
override func viewDidLoad() {
super.viewDidLoad()
setVO()
}
func setVO() {
button1.accessibilityTraits = .none
button2.accessibilityTraits = .none
button1.isSelected = true
button2.isSelected = true
handleCange1()
handleCange2()
button1.addTarget(self, action: #selector(handleCange1), for: .touchUpInside)
button2.addTarget(self, action: #selector(handleCange2), for: .touchUpInside)
}
#objc fileprivate func handleCange1() {
if controlButtonState1 == true {
button1.imageView?.accessibilityLabel = "Radio button deselected"
button1.setImage(image2, for: .selected)
controlButtonState1 = false
} else {
button1.imageView?.accessibilityLabel = "Radio button selected"
button1.setImage(image1, for: .selected)
controlButtonState1 = true
}
}
#objc fileprivate func handleCange2() {
if controlButtonState2 == true {
button2.imageView?.accessibilityLabel = "Radio button deselected"
button2.setImage(image2, for: .selected)
controlButtonState2 = false
} else {
button2.imageView?.accessibilityLabel = "Radio button selected"
button2.setImage(image1, for: .selected)
controlButtonState2 = true
}
}
}
I have a password UICollectionViewCell with a UITextField to enter the password. I want to have a button where the user can change the isSecureTextEntry property.
My Code
private lazy var button: UIButton = {
let view = UIButton()
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(toggle), for: .touchUpInside)
return view
}()
#objc func toggle(_ sender: UIButton) {
sender.isSelected == false ? button.setImage(secureImage, for: .normal) : button.setImage(unSecureImage, for: .selected)
}
Set Up Cell
self.toggle(button)
However, my button image is not changing.
sender.isSelected == false ? button.setImage(secureImage, for: .normal) : button.setImage(unSecureImage, for: .selected)
You're setting for normal state on one side and for selected state on the other.
Try this:
button.setImage((sender.isSelected ? unSecureImage : secureImage), for: .normal)
Can you try like this,
Setting the image for normal and selected state
On tapping the button changing the isSelected state of the button
private lazy var button: UIButton = {
let view = UIButton()
view.translatesAutoresizingMaskIntoConstraints = false
view.setImage(secureImage, for: .normal)
view.setImage(unSecureImage, for: .selected)
view.addTarget(self, action: #selector(toggleAction), for: .touchUpInside)
return view
}()
#objc func toggleAction(_ sender: UIButton) {
sender.isSelected.toggle()
}
SquareBox.swift
class SquareBox {
func createBoxes() {
for _ in 0..<xy {
let button = UIButton()
button.backgroundColor = .white
button.setTitleColor(UIColor.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.black.cgColor
stack.addArrangedSubview(button)
button.addTarget(self, action: #selector(click(sender:)) , for: .touchUpInside)
}
}
#objc func click(sender : UIButton) {
print("Click")
}
}
ViewController.swift
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let boxRow = SquareBox()
boxRow.createBoxes()
}
}
Also I've tried #IBAction instead of #objc, it doesn't work, but if I use "click" function in ViewController.swift that I created this object, it's working but I need this function inside of this class.
Now that you have posted relevant information in your question, the problem is quite clear. You have a memory management issue.
In your GameViewController's viewDidLoad you create a local instance of SquareBox. This local instance goes out of scope at the end of viewDidLoad. Since there is no other reference to this instance, it gets deallocated at the end of viewDidLoad.
Since the instance of SquareBox has been deallocated, it is not around to act as the button's target. And your click method is never called.
The solution is to keep a reference in your view controller:
class GameViewController: UIViewController {
let boxRow = SquareBox()
override func viewDidLoad() {
super.viewDidLoad()
boxRow.createBoxes()
}
}
var btnfirst:UIButton!
override func viewDidLoad()
{
super.viewDidLoad()
btnfirst = UIButton(type: .system)
btnfirst.setTitle("Press", for: .normal)
btnfirst.setTitleColor(.red, for: .normal)
btnfirst.frame = CGRect(x: 100, y: 200, width: 100, height: 30)
btnfirst.addTarget(self, action: #selector(benpress( sender:)),for: .touchUpInside)
self.view.addSubview(btnfirst)
}
func benpress( sender :UIButton)
{
//Your Code Here
}
For those who did not find a solution, here is mine.
If you constructed your UIButton as
let button: UIButton = {
return UIButton()
}()
Just convert those into
lazy var button: UIButton = {
return UIButton()
}()
I think this is because of somewhat deallocation as mentioned above.
button.addTarget(self, action:#selector(self.click), for: .touchUpInside)
func click(sender : UIButton) {
// code here
}
I guess the issue is how you are setting up layout of your buttons.
Try this:
func createBoxes() {
stack.backgroundColor = UIColor.red
for _ in 0..<xy {
// Create the button
let button = UIButton()
button.backgroundColor = UIColor.red
// Add constraints
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
button.widthAnchor.constraint(equalToConstant: 44.0).isActive = true
// Setup the button action
button.addTarget(self, action: #selector(SquareBox.click(sender:)), for: .touchUpInside)
// Add the button to the stack
stack.addArrangedSubview(button)
}
}
#objc func click(sender : UIButton) {
print("Click")
}
button.addTarget(self, action: #selector(self.buttonTapped), for: .touchUpInside)
func buttonTapped(sender : UIButton) {
// code here
}
Replace with this :
btn.addTarget(self, action: #selector(self.click(sender:)), for: .touchUpInside)
I think something else effect to your selector method try to find in your code because your code also working in my project.
I'm trying to figure out the logic in toggling button states in Swift. The concept is very simple:
I have three buttons on the screen.
When I click one button, it toggles to a 'selected' state
When I click a different button, I want it to toggle the currently selected button to a 'non-selected' state and toggle the new button to 'selected'
I have this function I use on TouchUpInside for the buttons on the screen but at the moment it's possible to have them all 'selected' which I don't want:
func highlightTrack(sender:UIButton){
if(!sender.selected){
sender.selected = true
sender.backgroundColor = UIColor.blueColor()
} else {
sender.selected = false
}
}
I come from the world of Javascript so I may just have my logic crossed up but is there a way to detect currently selected buttons on the screen and turn them off or is this closer to a 'radio' button type logic?
My issue is that these buttons are created programmatically depending on certain conditions so technically I'm not supposed to be created IBOutlets on the fly like that correct (IB meaning 'Interface Builder'?)?
Thanks for your help!
func highlightTrack(sender:UIButton) {
if sender.isSelected {
return
}
btn1.isSelected = false
btn2.isSelected = false
sender.isSelected = true
}
Connect following action function with all your three buttons using TouchUpInside control event.
button1.addTarget(self, action: #selector(self.highlightTrack(button:)), for: .touchUpInside)
button2.addTarget(self, action: #selector(self.highlightTrack(button:)), for: .touchUpInside)
button3.addTarget(self, action: #selector(self.highlightTrack(button:)), for: .touchUpInside)
#IBAction func highlightTrack(button: UIButton) {
if button.isSelected {
return
}
button1.isSelected = false
button1.backgroundColor = UIColor.white
button2.isSelected = false
button2.backgroundColor = UIColor.white
button3.isSelected = false
button3.backgroundColor = UIColor.white
button.isSelected = true
button.backgroundColor = UIColor.blue
}
Another solution:
#IBAction func highlightTrack(button: UIButton) {
if button.isSelected {
return
}
updateButtionSelectionState(button: button, isSelected: (button == button1))
updateButtionSelectionState(button: button, isSelected: (button == button2))
updateButtionSelectionState(button: button, isSelected: (button == button3))
}
func updateButtionSelectionState(button: UIButton, isSelected: Bool) {
button.isSelected = isSelected
button.backgroundColor = isSelected ? UIColor.blue : UIColor.white
}
Part way done with learning swift but I hit a small wall and yet again, I'm sure I'm just a bit new at this and an easy solution is there but I'm having trouble figuring out how to select/deselect buttons below is what I have so far and it is a button turns into a checkmark when clicked on... I've gotten that far but I need that button to deselect when clicked on again and then obviously be able to be clicked again if need be.
#IBAction func buttonPressed(sender: AnyObject) {
sender.setImage(UIImage(named: "Checkmark.png"), forState: .Normal)
}
Swift 3 note: .selected and .checked are now lower case UIControlState values in the SDK, and some of the methods have been renamed:
let button = UIButton()
button.setImage(UIImage(named: "Unchecked"), for: .normal)
button.setImage(UIImage(named: "Checked"), for: .selected)
You can also now use image literals with Xcode 8 instead of UIImage(named:):
#imageLiteral(resourceName: "Unchecked")
Swift 2:
Why not use the .Selected state of the button as the "checked" state, and the .Normal state as the "unchecked" state.
let button = UIButton()
button.setImage(UIImage(named: "Unchecked"), forState: .Normal)
button.setImage(UIImage(named: "Checked"), forState: .Selected)
// ...
#IBAction func buttonPressed(sender: AnyObject) {
if let button = sender as? UIButton {
if button.selected {
// set deselected
button.selected = false
} else {
// set selected
button.selected = true
}
}
}
You dont need to set selected in condition. I just doing with following method in swift:
func selectDeselect(sender: UIButton){
sender.selected = !sender.selected
if(sender.selected == true)
{
sender.setImage(UIImage(named:"select_heart"), forState: UIControlState.Normal)
}
else
{
sender.setImage(UIImage(named:"heart"), forState: UIControlState.Normal)
}
}
Here is Working code for swift 4.
Make Sure you need to connect Button IBAction Outlet as UIButton and set default button image from storyboard whatever you want.
#IBAction func btnTapped(_ sender: UIButton) {
if sender.currentImage == UIImage(named: "radio_unchecked"){
sender.setImage(UIImage(named: "radio_checked"), for: .normal)
}else{
sender.setImage(UIImage(named: "radio_unchecked"), for: .normal)
}
}
update for Swift 4+ :
let button = UIButton()
button.setImage(UIImage(named: "Unchecked"), forState: .Normal)
button.setImage(UIImage(named: "Checked"), forState: .Selected)
#IBAction func buttonPressed(sender: AnyObject) {
if let button = sender {
if button.isSelected {
// set deselected
button.isSelected = false
} else {
// set selected
button.isSelected = true
}
}
}
Set uncheck image on default state from storyboard and check image on selected state from storyboard.
#IBAction func buttonPressed(sender: AnyObject) {
buttonOutlet.isSelected = !buttonOutlet.isSelected
}
private(set) lazy var myButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "Unchecked"), for: .normal)
button.setImage(UIImage(named: "Checked"), for: .selected)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
#objc
func buttonTapped(sender: AnyObject) {
sender.isSelected.toggle()
}
To select only the pressed button out of three buttons and deselect the others when designated button is pressed.
#IBAction func buttonPressed(_ sender: UIButton) {
// Deselect all buttons
button1.isSelected = false
button2.isSelected = false
button3.isSelected = false
// Select the pressed button
sender.isSelected = true
}