How to detect when user finished moving Range slider in Swift - ios

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) {
}

Related

Creating duplicate outlets/actions on Xcode

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.

Prevent parent UIControl from responding to touchDown events

I am creating a reusable MetaControl that consists of a few sub-controls (see pic). It inherits from UIControl, and contains 2 simple UIButtons and a custom slider I created. The CustomSlider also inherits from UIControl.
In the MetaControl's initializer, I add some target actions that receive events from the sub-controls to inform the MetaControl of changes.
class MetaControl: UIControl {
public override init(frame: CGRect) {
self.slider.addTarget(self, action: #selector(sliderValueChanged), for: .valueChanged)
self.slider.addTarget(self, action: #selector(sliderTouched), for: .touchDown)
self.button1.addTarget(self, action: #selector(button1Pressed), for: .touchDown)
self.button2.addTarget(self, action: #selector(button2Pressed), for: .touchDown)
}
}
and then later I put the selector:
#objc private func sliderTouched() {
print("The slider was touched")
}
The problem I'm having is with the sliderTouched() event. This event gets fired twice whenever I touch the slider.
The first time it seems to be sent from the MetaControl itself.
The second time is the correct event being passed up from the CustomSlider itself.
My question is, how can I prevent the MetaControl itself from responding to events, while still allowing my CustomSlider to send its actions?
I used 2 ways to confirm that it was the MetaControl itself which triggered the extra event:
Commented out the action sending code in the CustomSlider. Even without this line of code, sliderTouched() is still called:
class CustomSlider: UIControl {
var isTouched = false {
didSet {
//sendActions(for: .touchDown)
}
}
}
Replaced the .touchDown action with .applicationReserved. Since I'm no longer using .touchDown, the MetaControl's .touchDown event is never called.
class CustomSlider: UIControl {
var isTouched = false {
didSet {
sendActions(for: .applicationReserved)
}
}
}
Interestingly, the UIButtons do not cause this same problem. Pressing the UIButton does not cause two events to be fired. How can I achieve this same thing?
P.S. The way I am causing the events to be sent from the CustomSlider is through a custom UIPanGestureRecognizer which is embedded into the CustomSlider's own initializer.
I used this as a guideline: https://www.raywenderlich.com/82058/custom-control-tutorial-ios-swift-reusable-knob
P.P.S I hope I'm not going about this the wrong way-- embedding all these sub-controls into a MetaControl that then itself inherits from UIControl. The MetaControl itself never responds to touch events-- only the subcontrols. I'm using the MetaControl mostly as a container to manage the shared state, provide structure to the layout, and to make the control re-usable. Eventually, I need many of them.
The answer was to make sure to also override touchesBegan in the slider.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}

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.

iOS: Swift: UISlider editingDidEnd not being called

I've set up a function for editing did end with a slider, but it doesn't look like it's being called. I've thrown a print statement into it, and a breakpoint. Is there something else that needs to be done to trigger a function when my user lets go of the slider?
#IBAction func sliderEditingDidEnd(sender: UISlider) {
print("did end");
}
Use the event "Value Changed". If you just want updates for the final value, alter UISlider.continuous
var slider = UISlider()
slider.continuous = false
//add slider to view
#IBAction func valueChanged(sender: UISlider) {
println("Value changed.")
}
//prints "Value changed." once upon releasing slider.
If the slider needs to be continuous, you can implement an event for UIControlEvent.TouchUpInside.
Or in code:
override func viewDidLoad() {
super.viewDidLoad()
mySlider.addTarget(self, action: "userReleasedSlider:", forControlEvents: UIControlEvents.TouchUpInside)
// Do any additional setup after loading the view, typically from a nib.
}
func userReleasedSlider(slider: UISlider) {
print("User released slider.")
}
I think what you are trying to do is to get the notification when the value is changed. Then you have to set event to 'Value Changed' not 'Editing Did End'.
If you set both Touch Up Inside and Touch Up Outside to point to your #IBAction then you can get the function to fire when the user finishes sliding and keep the slider as continuous.

Having trouble targeting a DesignableView in a UITableViewCell upon tapping it in order to change

and thanks in advance for taking the time to help.
Inside my CellForRowAtIndexPath, I have the following line:
cell.timeView.addTarget(self, action: "ButtonDidPress:", forControlEvents: UIControlEvents.TouchUpInside)
and my selector function is:
func ButtonDidPress (sender: DesignableView!){
let view:DesignableView = sender
cell.timeView.shadowColor = UIColor.yellowColor()
table.reloadData()
}
and the error i get is:
unrecognized selector sent to instance
I'm thinking that perhaps one can't send a View as a selector (am I using the correct terminology?), but how else can I target that particular view in that cell?
UPDATE:
I also tried using gestureRecognizer instead:
var tap = UIGestureRecognizer(target: self, action: Selector( "viewDidTap:"))
cell.timeView.addGestureRecognizer(tap)
and
func viewDidTap (sender: DesignableView!){
but I got the same error here.
Thanks!
There's a couple of strange things happening in your code. It seems you want to change the shadowColor property of timeView when a user touch it, right?
Two possible solutions are:
(This one is IMO the better one) Change DesignableView to inherit from UIButton. Then you can set:
timeView.addTarget(self, action: "ButtonDidPress:", forControlEvents: .TouchUpInside). Make sure you set it just once for each cell. Otherwise you will get multiple calls on one tap.
Use UITapGestureRecognizer, but you should put it in your UITableViewCell subclass, not to the view controller. Also, the sender in viewDidTap is not the view itself, but the recognizer. So the method will go like this:
func viewDidTap(sender: UITapGestureRecognizer) {
let location = sender.locationInView(sender.view)
if timeView.hitTest(location, withEvent: nil) == timeView {
timeView.shadowColor = UIColor.yellowColor()
// table.reloadData() - you don't need to reload the table
}
}

Resources