Creating duplicate outlets/actions on Xcode - ios

I currently have three separate sliders on my viewcontroller.swift that I would like to all perform the same function, which is to round the slider value to the nearest position. I have already completed the first one, as such:
#IBAction func changePos(_ sender: UISlider) {
slider.value = roundf(slider.value)
}
I have tried many different ways to add the same function to different UISliders by using slider2, changePos2, etc, but my app either crashes or doesn't perform the function.
How do I make separate UISliders perform the same function within the same viewcontroller?

You can hook all 3 sliders up to the same #IBAction. Just use the sender parameter instead of your slider output in the implementation.
#IBAction func changePos(_ sender: UISlider) {
sender.value = sender.value.rounded()
}

The good way to do this is using IBOutletCollections like below.
#IBOutlet var sliders: [UISlider]!
and then using nice swift syntax below:
sliders.forEach { (slider) in
slider.addTarget(self, action: #selector(sliderValueDidChange(sender:)), for: .valueChanged)
}
This way you will have same selector get called for each of the sliders.

Related

Should we make tapGesture component an IBAction or IBOutlet to capture the tap event?

When I want a tap response on my main View in my ViewController
A. I could create an IBOutlet as below
class ViewController: UIViewController {
#IBOutlet var tapGesture: UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
tapGesture.addTarget(self, action: #selector(tapped))
}
#objc private func tapped(_: UITapGestureRecognizer) {
print("Log is here")
}
}
Or
B. I could an IBAction on the TapGesture such as below
class ViewController: UIViewController {
#IBAction func tapGestureAction(_ sender: Any) {
print("Log is here")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Is there a preferred approach of one above the other? If not, which situation should we use A approach, and which we should use B approach?
Option B, i.e. just having the #IBAction outlet would be preferred when you already created your UITapGestureRecognizer in the storyboard, as this encapsulates as much logic as possible in the storyboard, reducing the overhead of reading unnecessary code and potential regressions if/when the code is refactored (but the storyboard remains unchanged).
You can still mark the #IBAction private (as it's effectively the same as using an #objc attribute). Also, if you need to access the gesture recognizer itself, you can have a regular #IBOutlet with a didSet to modify it, or change sender: Any to sender: UITapGestureRecognizer to access it in the action.
It is an interesting question, from my perspective this depends on how much from your application is in the storyboard or you want it explicitly written in the code.
My recommendation will be if you are doing something small and it should be done fast to use your storyboard. But if you have a big project with a big team then it will be better to have it in the code.
The other thing that can be a key factor for these approaches will be who is the owner of the reference and do you want to have some interactions of the gesture. For example, I have a gesture that should be enabled in specific cases and for others, it should be disabled. For this, you need to have a reference in the code.
What I'm trying to explain is that you should think for criteria like how and when you can use this gesture. And based on this to decide if you need less code or reference to the gesture or whatever you need

Customize a button and its highlighted state in Swift3 using a class

I'm new to Swift and I assume this is a fundamental question to programming for iOS.
I have three buttons in my storyboard and I want to customize how those buttons look if pressed once, twice and three times.
I also have three themes (pink, blue and orange). What I thought of doing is to create three new classes called pink,blue and orange.swift
I don't want to create them programmatically, only style them programmatically.
What I lack to understand is how do I call the function (Example: "ButtonIsPressed") from my pink.swift class into my #IBAction and #IBOutlet in the main view controller that is also object oriented (ie. I don't want to create a function for every button)?
I can't really find a decent and up-to-date Swift 3 Tutorial for this, any help or advice on this topic will be greatly appreciated.
Why can it not be as simple as?:
#IBAction func buttonPressed(_ sender: UIButton!) {
self.backgroundColor = myPinkCGolor
}
I think shallowThought's answer will work for changing backgroundColor based on button state of a specifically named IBOutlet.
I have three buttons in my storyboard and I want to customize how those buttons look if pressed once, twice and three times.
If you want to maintain "state", as in have a "counter" for how many times a button's been clicked or tapped, you can use the "tag" property of the button. Set it to zero, and in your IBAction functions increment it. (Like shallowThought said, use .touchUpInside and .touchDown for the events.)
Also, you have one minor - but important! - thing wrong in your code Brewski:
#IBAction func buttonPressed(_ sender: UIButton!) {
self.backgroundColor = myPinkCGolor
}
Should be:
#IBAction func buttonPressed(_ sender: UIButton!) {
sender.backgroundColor = myPinkCGolor
}
So combining everything - up vote to shallowThought (also, changing his AnyObject to UIButton and making it Swift 3.x syntax on the UIColors - and would end up with this. Note that there is no need for an IBOutlet, and you can wire everything up in IB without subclassing:
// .touchUpInside event
// can be adapted to show different color if you want, but is coded to always show white color
#IBAction func buttonClicked(sender: UIButton) {
sender.backgroundColor = UIColor.whiteColor()
}
// .touchDown event
// will show a different color based on tap counter
#IBAction func buttonReleased(sender: UIButton) {
switch sender.tag {
case 1:
sender.backgroundColor = UIColor.blue
case 2:
sender.backgroundColor = UIColor.red
case 3:
sender.backgroundColor = UIColor.green
default:
sender.backgroundColor = UIColor.yellow
}
sender.tag += 1
}
There is no methode to set the backgroundColor for a certain state, like there is for other UIButton properties, so you have to listen to the buttons actions:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
#IBAction func buttonClicked(sender: AnyObject) { //Touch Up Inside action
button.backgroundColor = UIColor.whiteColor()
}
#IBAction func buttonReleased(sender: AnyObject) { //Touch Down action
button.backgroundColor = UIColor.blueColor()
}
...
}
or set a unicolor image withimage:UIImage, forState:.selected.

How to detect when user finished moving Range slider in Swift

I use this is Range slider in my project. How use it.
I want to detect, when user finished moving Range slider.
I tried to use function SliderAction(sender: RangeSlider), but I get each moving points in the slider. I think I need to use this function: func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?), but it is doesn't work.
How can I make it?
To detect when user finished moving the range slider you can add a controlevent to your slider , you can add it programatically :
mySlider.addTarget(self, action: "sliderDidEndSliding:", forControlEvents: .UIControlEventTouchUpInside)
then you have to do your logic the recieving methode .
func sliderDidEndSliding(sender: UISlider) {
}
For swift 3 I used this:
mySlider.addTarget(self, action: #selector(self.sliderDidEndSliding), for: .touchUpInside)
func sliderDidEndSliding() {
// process stuff
}
reading values is done with:
#IBAction func mySlider(_ sender: Any) {
}
based on this example viewcontroller, you should check the value sender.selectedMax value changed should be equal to the one you set in storyboard for that slider view
You need to override endTrackingWithTouch. It'll get called when tracking ends by the underlying machinery, giving you a chance to respond.
Be sure and call the super of this during your version of endTrackingWithTouch so the the parents classes can do their bit.
Swift 4
slider.addTarget(self, action: #selector(sliderDidEndSliding), for: .touchUpInside)
and add
#objc func sliderDidEndSliding(sender: UISlider) {
}

How can properly use 'sender' argument in #UIAction code Swift?

I'm relatively new to Swift programming and just learned from this article on Stack Overflow that I can't change UIButton text via the 'sender' argument in #IBAction code, but must setup an #IBOutlet from the button and use methods on the outlet variable. How broad of a rule is that? What methods are OK to apply to the 'sender' argument, and which are not?
I don't know what they're referring to in that post you've linked to, as it's not correct. You don't need an #IBOutlet for anything specifically. It doesn't grant any special powers to the button, it's just a handy pointer to the object you can use.
You of course need a pointer to the object if you want to call it's methods, but the sender attribute to an #IBAction is just as good for that IBAction's code.
You do have to make sure the sender is the right type.
For example:
#IBAction func buttonPressed(sender: UIButton) {
sender.setTitle("New Title", forState: .Normal)
}
That will work fine and change the button tapped. Doesn't matter if there's an #IBOutlet pointing to it anywhere. The sender variable is all you need.
In some cases, you'll want to use an AnyObject type for sender, in which case you'll need to check the type first:
#IBAction func buttonPressed(sender: AnyObject) {
if let button = sender as? UIButton {
button.setTitle("New Title", forState: .Normal)
}
}
Now, if you wanted to change a different button's title, then an #IBOutlet can make it easier.
#IBOutlet weak var someOtherButton: UIButton!
#IBAction func buttonPressed(sender: UIButton) {
someOtherButton.setTitle("New Title", forState: .Normal)
}
But again, an #IBOutlet isn't required (it is recommended). To show #IBOutlet doesn't have any special powers, you could set a tag value (e.g. 100) on the button in Interface Builder and use code like this:
#IBAction func buttonPressed(sender: AnyObject) {
if let button = view.viewWithTag(100) as? UIButton {
button.setTitle("New Title", forState: .Normal)
}
}
The 100 number I've used here is arbitrary and could be any number so long as you only use the number for one item in the view.
#IBAction func buttonClicked(sender : AnyObject) {
println("Button was clicked", sender)
}
here sender means your button's reference. you can perform different on button on click by using sender like sender.backgroundColor etc.
Hope this will help :)
The sender argument you mention is part of the Target-Action Mechanism.
The sender parameter usually identifies the control sending the action message (although it can be another object substituted by the actual sender). The idea behind this is similar to a return address on a postcard. The target can query the sender for more information if it needs to. If the actual sending object substitutes another object as sender, you should treat that object in the same way. For example, say you have a text field and when the user enters text, the action method nameEntered: is invoked in the target:
As the sender could be substituded, there is no guarantee that someone could use another sender in the parameter if calling it manually, while if you own a reference to your button in your class, via an IBOutlet, then it could sometimes be wiser to use it for any method you expect to call as you are sure that it refers to the button.
However, if you are sure the method is only linked with the correct sender, you could potentially call any method.
The question would not as much be "what action can I call" but "am I really sure sender really is the relevent button"

how to programmatically fake a touch event to a UIButton?

I'm writing some unit tests and, because of the nature of this particular app, it's important that I get as high up the UI chain as possible. So, what I'd like to do is programmatically trigger a button-press, as if the user had pressed the button in the GUI.
(Yes, yes -- I could just call the IBAction selector but, again, the nature of this particular app makes it important that I fake the actual button press, such that the IBAction be called from the button, itself.)
What's the preferred method of doing this?
It turns out that
[buttonObj sendActionsForControlEvents:UIControlEventTouchUpInside];
got me exactly what I needed, in this case.
EDIT: Don't forget to do this in the main thread, to get results similar to a user-press.
For Swift 3:
buttonObj.sendActions(for: .touchUpInside)
An update to this answer for Swift
buttonObj.sendActionsForControlEvents(.TouchUpInside)
EDIT: Updated for Swift 3
buttonObj.sendActions(for: .touchUpInside)
Swift 3:
self.btn.sendActions(for: .touchUpInside)
If you want to do this kind of testing, you’ll love the UI Automation support in iOS 4. You can write JavaScript to simulate button presses, etc. fairly easily, though the documentation (especially the getting-started part) is a bit sparse.
In this case, UIButton is derived from UIControl. This works for object derived from UIControl.
I wanted to reuse "UIBarButtonItem" action on specific use case. Here, UIBarButtonItem doesn't offer method sendActionsForControlEvents:
But luckily, UIBarButtonItem has properties for target & action.
if(notHappy){
SEL exit = self.navigationItem.rightBarButtonItem.action;
id world = self.navigationItem.rightBarButtonItem.target;
[world performSelector:exit];
}
Here, rightBarButtonItem is of type UIBarButtonItem.
For Xamarin iOS
btnObj.SendActionForControlEvents(UIControlEvent.TouchUpInside);
Reference
Swift 5:
class ViewController: UIViewController {
#IBOutlet weak var theTextfield: UITextField!
#IBOutlet weak var someButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
theTextfield.text = "Pwd"
someButton.sendActions(for: .touchUpInside)
}
#IBAction func someButtonTap(_ sender: UIButton) {
print("button tapped")
}
}
It's handy for people who write Unit Tests without UI Tests ;-)
Swift 5 way to solve it for UIBarButtonItem, which does not have sendAction method like UIButton etc.
extension UIBarButtonItem {
func sendAction() {
guard let myTarget = target else { return }
guard let myAction = action else { return }
let control: UIControl = UIControl()
control.sendAction(myAction, to: myTarget, for: nil)
}
}
And now you can simply:
let action = UIBarButtonItem(title: "title", style: .done, target: self, action: #selector(doSomething))
action.sendAction()
Swift 4:
self .yourButton(self)

Resources