Adding another picker to UISlider - ios

I am trying to create a UISlider that has two pickers.I already have a UISlider which is working and functioning correctly.
Is there a way where I can add a picker to the UISlider that I already have or do I need to scrap everything that I have and programatically code it so that it has two pickers?
I would like the slider to have a minimum picker and a maximum picker. Im not sure if it is possible to add another picker to the slider i already have.
Here is the code i have for my current uislider.
- (IBAction)slider:(id)sender {
UISlider *slide = (UISlider *)sender;
CGFloat value = slide.value;
NSUInteger index = value * (priceDelegate.values.count - 1);
NSString *priceString = priceDelegate.values[index];
[self.tf_price setText:priceString];
_appDelegate.int_minPriceV = index;
}
If some one could help me I would be really grateful, thank you.

If it's a range slider you want then you're out of luck with the standard UISlider as it does not provide this kind of functionality.
You can subclass UISlider and do your own thing but we can all agree that that's somewhat a tedious job.
You can try these instead:
link1: https://github.com/muZZkat/NMRangeSlider
link2A: https://github.com/buildmobile/iosrangeslider
OR...
link2B: https://www.cocoacontrols.com/controls/rangeslider
If you're feeling a bit adventurous then follow this tutorial:
http://www.sitepoint.com/wicked-ios-range-slider-part-one/

Related

Accessibility on custom UISlider

I have created a custom range UISlider with two thumb. However in VoiceOver mode, I am not able to swipe up and down to adjust the thumb.
UIAccessibilityElement *minElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
minElement.accessibilityFrame = [self convertRect:currentThumbRect
toView:nil];
minElement.accessibilityLabel = NSLocalizedString(#"Minimum", nil);
minElement.accessibilityTraits = UIAccessibilityTraitAdjustable;
[_accessibleElements addObject:minElement];
UIAccessibilityElement *maxElement = [[UIAccessibilityElement alloc]
initWithAccessibilityContainer:self];
maxElement.accessibilityFrame = [self convertRect:currentUpperThumbRect
toView:nil];
maxElement.accessibilityLabel = NSLocalizedString(#"Maximum", nil);
maxElement.accessibilityTraits = UIAccessibilityTraitAdjustable;
[_accessibleElements addObject:maxElement]
I have added code above so that VoiceOver can recognize both thumb separately, but I couldn't adjust the thumb. Any idea to make the thumb adjustable in VoiceOver.
Problem Solved:
I use 'UIAccessibilityCustomAction' to add in custom behavior. Instead of slide up and down to adjust the value, in custom behavior slide up and down can be use to choose the action and double tap to perform it.
UIAccessibilityElement *minElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
minElement.accessibilityLabel = NSLocalizedString(#"Minimum", nil);
UIAccessibilityCustomAction *increaseMinAction = [[UIAccessibilityCustomAction alloc] initWithName:NSLocalizedString(#"Increase minimum", #"action to increase min")
target:self selector:#selector(accessibilityMinIncrement)];
UIAccessibilityCustomAction *decreaseMinAction = [[UIAccessibilityCustomAction alloc] initWithName:NSLocalizedString(#"Decrease minimum", #"action to decrease min")
target:self selector:#selector(accessibilityMinDecrement)];
minElement.accessibilityCustomActions = #[increaseMinAction, decreaseMinAction];
[_accessibleElements addObject:minElement];
An adjustable element will be called with accessibilityIncrement and accessibilityDecrement When the user swipes up or down. When that happens it is expected to change its accessibilityValue. If the value doesn't change, VoiceOver will interpret that as having reached the boundaries of possible values (either the lowest or heights value that's allowed) and will play a "ding" sound to indicate to the user that the gesture had no effect.
A plain UIAccessibilityElement doesn't (to the best of my knowledge) implement the increment and decrement methods and you don't seemt to be modifying the value of those elements. As such, when the user focuses on one of those elements, the method isn't implemented and the value doesn't change so VoiceOver plays the "ding" sound.
One possible solution is to subclass and have each knob element either hold the current value, or to have each knob element ask a delegate for the value and forward the increment and decrement methods to that delegate.
You may use custom actions to solve your problem but I don't think that's the recommended solution for a UISlider with VoiceOver.
You were initally following the right path using the increment and decrement methods with the adjustable trait.
The problem dealt with your implementation : in my opinion, the best way is to define the former 2 methods so as to adapt the slider value according to the knob location.

Loop in a variable of type IBOutlet

I have twelve text fields as you can see below:
IBOutlet UITextField *ce_1;
IBOutlet UITextField *ce_2;
IBOutlet UITextField *ce_3;
....
IBOutlet UITextField *ce_12;
All I have to do is to set an existing object in an array in each of the variables that are responsible for the text fields, I'm currently doing as follows:
ce_1.text = myArray[1];
ce_2.text = myArray[2];
ce_3.text = myArray[3];
....
ce_12.text = myArray[12];
Not to be writing a lot, I thought I'd put this in an automated way within a loop as follows:
for(i=1;i<13;i++){
ce_[i].text = myArray[i];
}
But this command does not work the way I expected, so I would like your help to try to solve my idea and put it into practice, is there any way of doing this?
Research and start using IBOutletCollection. It will give you an array of text fields that you can build in your storyboard XIB.
Note that you may need to consider the order of the array, and that you might want to sort it (possibly based on the tag of each view).
Technically, you could use string formats and KVC to do what you're currently trying to but it is far from ideal.
You can't just replace ce_1 ce_2 ce_3 with ce_[i] it doesn't work that way. You can only use [number] with an nsarray variable (or decendents).
for example:
NSArray* myArray = #[#1];
NSLog(#"%#", myArray[0]);
You might want to look into IBOutletCollection in order to achieve something similar to what you're looking for.
However, contrary to other answers here IBOutletCollection are ordered by how you link them in the interface builder.
Refer to this for IBOutletCollections: How can I use IBOutletCollection to connect multiple UIImageViews to the same outlet?
You can use IBOutletCollection. You can also use key-value coding:
for(NSUInteger i = 0; i < 13; i++)
{
[[self valueForKey:[NSString stringWithFormat:#"ce_%u", i]] setText: myArray[i]];
}
This will give you what you want.
The way I like to handle these situations is creating a temporary array containing all the text fields:
NSArray *allFields = #[_ce_1, _ce_2, _ce_3, ...];
NSInteger i = 0;
for (UITextField *tf in allFields)
{
tf.text = myArray[i];
i++
}
IBOutletCollection also work but sometimes it gets hard to figure out when you come back to your project which label is #3 or #5 and such... I find this works better for me usually :)

Continuously Update Label Based on UISlider Value

I currently have a series of 5 UISliders that continuously update their respective labels to display what value the user is selecting in an hh:mm:ss format. I have the properties of each slider set to continuous and have their targets set to when the slider is changed (This example would be for just the first slider).
sliderOne.continuous=YES;
[sliderOne addTarget:self action:#selector(sliderOneChanged:) forControlEvents:UIControlEventTouchUpInside];
Each slider's individual label updates the time selected perfectly. Now however, I need to update another label underneath all of these sliders that continuously sums up the value of the sliders.
At the same time, I am also doing some conversions with the value of the slider using two segmented controllers -- a miles/kilometers segmented controller and various distances in another segmented controller. Each conversion results in a time in seconds that is then converted back into the hh:mm:ss format and displayed in the last label when the user selects which combo from the segment controller they want. I have all the math worked out for this, and the label will update correctly when changing segments, but not continuously.
How could I update a label to continuously show the sum of every UISlider as the user changes them?
For a continuous updating, use the UIControlEventValueChanged event along with the continuous property
sliderOne.continuous=YES;
[sliderOne addTarget:self action:#selector(sliderOneChanged:) forControlEvents:UIControlEventValueChanged];
Use KVO to get notified of changes in UISliders and update the UI accordingly. You want to wrap UI updates with dispatch_async to avoid locking the interface.
Using KVO-Notification-Manager it can be something like this (in viewDidLoad or similar):
id token1 = [slider1 addKVOBlockForKeyPath:#"value" options:NSKeyValueObservingOptionNew handler:^(NSString *keyPath, id object, NSDictionary *change) {
dispatch_async(dispatch_get_main_queue(), ^{
whateverSliderNeedsUpdate.value = /* your calculation result */;
});
}];
id token2 = [slider2 /* repeat for as many sliders as you want */];
...
It's important to remove the observers at some point.
- (void)dealloc {
[slider1 removeKVOBlockForToken:token1];
[slider2 ...];
}
If you feel adventurous you can try with ReactiveCocoa.
You could create a method that does all of your math and updates your UILabel with the result. For example:
- (void)updateSumLabel {
// this is obviously very simple
float num1 = [_sliderOne value];
float num2 = [_sliderTwo value];
float sum = num1 + num2;
[_sumLabel setText:[NSString stringWithFormat:#"#f",sum]];
}
The you can call updateSumLabel from any of your existing methods like sliderOneChanged. This is probably the most simple way todo this.

How to replace UISlider with UIProgressView playing MPMoviePlayerController?

I am having a hard time trying to replace UISlider on my app playing a invisible MPMoviePlayerController with UIProgressView.
UISlider is just create, very intuitive to change, Pretty much on the Update selector from MPMoviePlayerController I update its current status:
and previously I defined the min and max from the content of the MPMoviePlayerController:
-(void)UpdateTime:(NSTimer*)Timer
{
[self.sldProgress1 setValue:[[NSNumber numberWithDouble:playerSong.currentPlaybackTime] floatValue]];
}
UIProgressView doesn't provide similar approach, and I didn't find on the web a sample that matches what I am trying to do:
sldProgress2.maximumValue = [playerSong playableDuration];
sldProgress1.value = 0.0;
any suggestions?
UIProgressView doesn't have the concept of a user-settable max value - it's just 100.0. To make it work as you want, you'll have to do some math. Luckily, it's easy math:
CGFloat percentComplete = [playerSong.currentPlaybackTime floatValue] / [playerSong.playableDuration floatValue];
That will give you the percentage of the song that's complete, and you just update your UIProgressView to that number.
progressView.progress = percentComplete;
More or less - this code was mostly pseudo based on your question, but that's the idea.
You can keep using the UISlider and hide the thumbImage using this line:
[mySlider setThumbImage:[[UIImage alloc] init] forState:UIControlStateNormal];
This worked for me.

Display UIProgressView with percentage in iOS

How can I display a UIProgressView with a percentage?
As Vlad mentioned you can have a method which updates the value of progressView and its associated label. Call the following method when any update happens.
- (void)updateProgress
{
CGFloat stepSize = 0.1f;
[self.progressView setProgress:self.progressView.progress+stepSize];
self.progressLabel.text = [NSString stringWithFormat:#"Completed : %.0f",self.progressView.progress*100];
}
The progress indicator control does not offer the option of also displaying progress percent.
In order to achieve this, one solution would be to add a label positioned below / above your progress bar, and at each step when you refresh the progress on your progress indicator view, you also update the string value in on your label to display the percentage loaded.
There is one widget already available in iOS SDK - UIProgressView
Where you can set and get its property: progress
Hope this may help you.

Resources