Swift handle action on segmented control - ios

I have a HMSegmentedControl with 4 segments. When it is selected, it should pop up view. And when the pop up dismissed, and trying to click on same segment index it should again show the pop up. By using following does not have any action on click of same segment index after pop up dissmissed.
segmetedControl.addTarget(self, action: "segmentedControlValueChanged:", forControlEvents: UIControlEvents.ValueChanged)

You can add the same target for multiple events.
So lets say your segmentedControlValueChanged: looks like this:
#objc func segmentedControlValueChanged(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
// value for first index selected here
}
}
Then you can add targets for more than 1 events to call this function:
segmentedControl.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .valueChanged)
segmentedControl.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .touchUpInside)
Now your function will get called when a value was changed and when the user releases his finger.

with sender, use the sender name sender when you want to access in the action:
segmentControl.addTarget(self, action: #selector(changeWebView(sender:)), for: .valueChanged)
or
addTarget(self, action: #selector(changeWebView), for: .valueChanged)

Swift 5
// add viewController
#IBOutlet var segmentedControl: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
segmentedControl.addTarget(self, action: #selector(CommentsViewController.indexChanged(_:)), for: .valueChanged)
}
// using change
#objc func indexChanged(_ sender: UISegmentedControl) {
if segmentedControl.selectedSegmentIndex == 0 {
print("Select 0")
} else if segmentedControl.selectedSegmentIndex == 1 {
print("Select 1")
} else if segmentedControl.selectedSegmentIndex == 2 {
print("Select 2")
}
}

You set your target to fire just when the value change, so if you select the same segment the value will not change and the popover will not display, try to change the event to TouchUpInside, so it will be fired every time you touch inside the segment
segmentedControl.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .touchUpInside)

#IBAction func segmentedControlButtonClickAction(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
print("First Segment Select")
}
else {
print("Second Segment Select")
}
}

Swift4 syntax :
segmentedControl.addTarget(self, action: "segmentedControlValueChanged:", for:.touchUpInside)

Drag and drop create an action which is a value type.
#IBAction func actionSegment(_ sender: Any) {
let segemnt = sender as! UISegmentedControl
print(segemnt.selectedSegmentIndex)// 0 , 1
}

Related

How to select one checkbox at a time in swift?

I have implemented four checkboxes. Now, I want to select one checkbox at a time and if there's other checkbox selected, I want it deselected at the same time.
This is what I've tried but it selects all the checkboxes.
#IBAction func checkboxTapped(_ sender:UIButton){
if (sender.isSelected == false){
sender.setImage(UIImage(named: "selected"), for: UIControl.State.normal)
sender.isSelected = true;
} else {
sender.setImage(UIImage(named: "unselected"), for: UIControl.State.normal)
sender.isSelected = false;
}
}
Here, I have given same IBAction connection to all the checkboxes.
First create a set up function that sets the corresponding image for the button state.
(All code below was created in a playground so it might be a little different than yours)
private func setUp(button: UIButton) {
button.setImage(UIImage.init(systemName: "checkmark.circle"), for: .selected)
button.setImage(UIImage.init(systemName: "xmark.circle"), for: .normal)
button.addTarget(self, action: #selector(checkboxTapped), for: .touchUpInside) //Not needed if you use a storyboard
}
Then call it from viewDidLoad in your view controller (I have my buttons in an array buttons)
override func viewDidLoad() {
super.viewDidLoad()
buttons.forEach {
setUp(button: $0)
self.view.addSubview($0) //Not needed if you use a storyboard
}
buttons[0].isSelected = true
And then check your action method so it updates all buttons properly
#IBAction func checkboxTapped(_ sender:UIButton){
for button in buttons {
button.isSelected = false
}
sender.isSelected = true
}
If you are not using an array then replace my loops with calls to the individual buttons

Prepare for Segue function not passing data correctly

My prepareForSegue method isn't passing data to the destination view controller.
var buttonsDictionary = [Int: UIButton]()
func createButtonArray() {
for item in statTitles {
let statisticButton = StatButton()
statisticButton.layer.cornerRadius = 10
statisticButton.backgroundColor = UIColor.darkGray
statisticButton.setTitle(String(item.value), for: UIControlState.normal)
statisticButton.setTitleColor(UIColor.white, for: UIControlState.normal)
statisticButton.titleLabel?.font = UIFont.systemFont(ofSize: 43)
statisticButton.titleEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0)
statisticButton.contentHorizontalAlignment = .left
statisticButton.addTarget(self, action: #selector(displayStatDetail), for: .touchUpInside)
statisticButton.buttonIndex = item.key
buttonsDictionary[item.key] = (statisticButton) // Assign value at item.key
print(statisticButton.buttonIndex)
}
}
func viewSavedStatistics() {
for button in buttonsDictionary {
statisticsView.addArrangedSubview(button.value)
}
}
#objc func displayStatDetail() {
self.performSegue(withIdentifier: "StatDetailSegue", sender: UIButton())
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "StatDetailSegue" {
if let destinationVC = segue.destination as? StatDetailViewController,
let index = (sender as? StatButton)?.buttonIndex {
destinationVC.statID = index
print("Destination STATID: \(destinationVC.statID)")
}
}
}
All of the above code is written in the ViewController class.
StatButton is a custom UIButton class.
The prepare is meant pass on the buttonIndex of the tapped button, but only passes on 0 and doesn't print so I don't think it's being called.
You sender is a new instance of UIButton which doesn't have any of the information you need. Instead pass the button calling the selector.
#objc func displayStatDetail(_ sender: StatisticButton) {
self.performSegue(withIdentifier: "StatDetailSegue", sender: sender)
}
You would need to change the target selector like this in the loop.
statisticButton.addTarget(self, action: #selector(displayStatDetail(_:)), for: .touchUpInside)
You’re passing a new instance of UIButton as sender here:
self.performSegue(withIdentifier: "StatDetailSegue", sender: UIButton())
Instead you should probably have your statisticButton there. Your button target selector method can have a parameter - the button instance the user clicked on. Use it as sender.
You had a mistake in performSeguefunction you've send always a new object of UIButton, not the one you've clicked on. Here is what you should do.
statisticButton.addTarget(self, action: #selector(displayStatDetail(_ :)), for: .touchUpInside)
#objc func displayStatDetail(_ sender: UIButton) {
self.performSegue(withIdentifier: "StatDetailSegue", sender: sender)
}

Swift create custom logic for radio button

I have four custom UIButton, I applied button image radio (checked and checked). four button I have separate action method I can change the Image easily but if I checked first button another three button need uncheck. it should react like radio button.
Here, below my code
#IBAction func first_scheme(_ sender: Any) {
bRec = !bRec
if (bRec == true) {
firstscheme_button.setImage(UIImage(named: "uncheck.png"), for: .normal)
} else {
firstscheme_button.setImage(UIImage(named: "check.png"), for: .normal)
}
}
If you have 4 radio buttons at all times, you can put them in an array.
var radioButtons: [ButtonType] = [r1, r2, r3, r4]
You can now access the button in a loop and set the values for the other button to 'unchecked'.
func setRadioButtons(button: ButtonType) {
for radioButton in radioButtons {
if radioButton !== button {
radioButton.setImage(UIImage(named: "uncheck.png"), for: .normal)
}
}
}
#IBAction func first_scheme(_ sender: Any) {
bRec = !bRec
if bRec {
firstscheme_button.setImage(UIImage(named: "uncheck.png"), for: .normal)
} else {
firstscheme_button.setImage(UIImage(named: "check.png"), for: .normal)
}
setRadioButtons(button: sender)
}
alternate method
If all you want to do is check the button clicked and uncheck the other buttons, the logic is simpler.
Create the common action for all radio buttons as well create the IBOutletcollection for your all UIButtons ,
var radioButtons: [UIButton] = [r1, r2, r3, r4]
finally execute the common method as
func setRadioButtons(button: UIButton) {
for getradioButton in radioButtons {
getradioButton.setImage(UIImage(named: "uncheck.png"), for: .normal)
}
button.setImage(UIImage(named: "check.png"), for: .normal)
}
I suggest you set tags on the buttons with the simple tag property, and then you save it from a generic listener.
//first button selected
var lastTag = 0
#IBAction func first_scheme(_ sender: UIButton) {
buttonArray[lastTag].setImage(UIImage(named: "uncheck.png"), for: .normal)
lastTag = sender.tag
sender.setImage(UIImage(named: "check.png"), for: .normal)
}
You Can override isSelected variable in the custom UIButton and set the image depend on the isSelected value.
class customButton: UIButton {
override var isSelected: Bool {
willSet {
self.setImage(UIImage.init(named: "checked"), for: .normal)
}
didSet {
self.setImage(UIImage.init(named: "unchecked"), for: .normal)
}
}
}
After making 4 IBOutlets and 4 IBActions for the four custom UIButtons. you can easily select and unselect the buttons and apply your custom behaviour.
#IBAction func firstButtonAction(_ sender: Any) {
if let button = sender as? customButton {
button.isSelected = !button.isSelected
}
secondButton.isSelected = false
thirdButton.isSelected = false
fourthButton.isSelected = false
}

Make multiple buttons highlight when touch begins

I have placed three buttons together in a view each of which will have a different profile image. I need all three buttons to highlight when touch begins on any individual one so that the three together appear to be one button. I have tried the following code but it does not work. Any ideas?
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(normalTap))
tapGesture.numberOfTapsRequired = 1
friendsBtn1.addGestureRecognizer(tapGesture)
func normalTap(sender: UITapGestureRecognizer){
if sender.state == .began {
friendsBtn2.isHighlighted = true
friendsBtn3.isHighlighted = true
}
if sender.state == .ended {
friendsBtn2.isHighlighted = false
friendsBtn3.isHighlighted = false
}
print("Normal tap")
}
i think no need to UITapGesture you can manage like this way.
First you need to set highlighted image to the button And Setup button event as per bellow.
override func viewDidLoad() {
super.viewDidLoad()
friendsBtn2.setImage(UIImage(named: "highlighted.png"), for: .highlighted)
friendsBtn3.setImage(UIImage(named: "highlighted.png"), for: .highlighted)
friendsBtn1.addTarget(self, action: #selector(YourViewController.touchDownEvent), for: .touchDown)
friendsBtn1.addTarget(self, action: #selector(YourViewController.touchUpInsideEvent), for: .touchUpInside)
}
After that add method touchDownEvent(),touchUpInsideEvent() in your ViewController.
func touchDownEvent() {
self.allButtonHighlighted(fleg: true)
}
func touchUpInsideEvent() {
self.allButtonHighlighted(fleg: false)
}
func allButtonHighlighted( fleg: Bool) {
friendsBtn2.isHighlighted = fleg
friendsBtn3.isHighlighted = fleg
}
I hope it will Works.
I did solve the problem. Here is the code.
[friendsBtn1, friendsBtn2, friendsBtn3].forEach { button in
button?.addTarget(self, action:#selector(highlightAllButtons(sender:)), for: .touchDown)
button?.addTarget(self, action:#selector(unhighlightAllButtons(sender:)), for: [.touchUpInside, .touchUpOutside])
}
func highlightAllButtons(sender: UIButton) {
[friendsBtn1, friendsBtn2, friendsBtn3].forEach { button in
button.isHighlighted = true
}
}
func unhighlightAllButtons(sender: UIButton) {
[friendsBtn1, friendsBtn2, friendsBtn3].forEach { button in
button.isHighlighted = false
}
}
Try [button setSelected:YES]; for all the three buttons in your button action.

isHighlighted called multiple times on UIButton

I have subclassed UIButton and want to call a delegate method just once when the button goes into the highlighted state and call it again just once when it goes into the unhighlighted state:
override var isHighlighted: Bool {
didSet {
if isHighlighted {
delegate?.buttonHighlightStateDidChange(highlighted: true)
} else {
delegate?.buttonHighlightStateDidChange(highlighted: false)
}
}
}
However when I touch down on the button it seems that didSet is getting repeatedly called. What am I doing wrong here? How can I call the delegate method just once?
I would recommend against using your subclass in this way. UIControl has a builtin mechanism for getting callbacks in response to control events:
func registerActions(for button: UIButton) {
button.addTarget(self, action: #selector(MyClass.buttonIsHighlighted(sender:)), for: .touchDown)
button.addTarget(self, action: #selector(MyClass.buttonIsUnHighlighted(sender:)), for: .touchUpInside)
button.addTarget(self, action: #selector(MyClass.buttonIsUnHighlighted(sender:)), for: .touchUpOutside)
}
func buttonIsHighlighted(sender: UIButton) {
// highlighted
}
func buttonIsUnHighlighted(sender: UIButton) {
// unhighlighted
}

Resources