Implementing steps/snapping UISlider - iphone

I am trying to implement some form of snapping or steps with the UISlider. I have written the following code but it does not work as smooth as I hoped for. It works, but when the I slide it upwards it snap 5points to the right leaving the finger not centered over the "slide-circle"
This is my code where self.lastQuestionSliderValue is a property of the class which I have set to the initial value of the slider.
if (self.questionSlider.value > self.lastQuestionSliderValue) {
self.questionSlider.value += 5.0;
} else {
self.questionSlider.value -= 5.0;
}
self.lastQuestionSliderValue = (int)self.questionSlider.value;

It's actually considerably easier than I first thought. Originally I was trying to get the thumbrect property and do complicated math. Here's what I ended up with:
h File:
#property (nonatomic, retain) IBOutlet UISlider* questionSlider;
#property (nonatomic) float lastQuestionStep;
#property (nonatomic) float stepValue;
m File:
- (void)viewDidLoad {
[super viewDidLoad];
// Set the step to whatever you want. Make sure the step value makes sense
// when compared to the min/max values for the slider. You could take this
// example a step further and instead use a variable for the number of
// steps you wanted.
self.stepValue = 25.0f;
// Set the initial value to prevent any weird inconsistencies.
self.lastQuestionStep = (self.questionSlider.value) / self.stepValue;
}
// This is the "valueChanged" method for the UISlider. Hook this up in
// Interface Builder.
-(IBAction)valueChanged:(id)sender {
// This determines which "step" the slider should be on. Here we're taking
// the current position of the slider and dividing by the `self.stepValue`
// to determine approximately which step we are on. Then we round to get to
// find which step we are closest to.
float newStep = roundf((questionSlider.value) / self.stepValue);
// Convert "steps" back to the context of the sliders values.
self.questionSlider.value = newStep * self.stepValue;
}
Make sure you hook up the method and the outlet for your UISlider view and you should be good to go.

The simplest solution to me was just
- (IBAction)sliderValueChanged:(id)sender {
UISlider *slider = sender;
slider.value = roundf(slider.value);
}

Another Swift approach is to do something like
let step: Float = 10
#IBAction func sliderValueChanged(sender: UISlider) {
let roundedValue = round(sender.value / step) * step
sender.value = roundedValue
// Do something else with the value
}

SWIFT VERSION
Example:
You want a slider to go from 1-10000 in steps of 100.
UISlider setup is as follows:
slider.maximumValue = 100
slider.minimumValue = 0
slider.continuous = true
In the action func() for the slider use:
var sliderValue:Int = Int(sender.value) * 100

Maybe someone will need!
In my situation I needed any integer step, so I used the following code:
-(void)valueChanged:(id)sender {
UISlider *slider = sender;
slider.value = (int)slider.value;
}

A really simple one:
- (void)sliderUpdated:(UISlider*)sli {
CGFloat steps = 5;
sli.value = roundf(sli.value/sli.maximumValue*steps)*sli.maximumValue/steps;
}
Great if you want a fast solution and you've added the target by UIControlEventValueChanged.

Related

Display a number in a label but have it count up the number

I have a UILabel in my class and I would like the view to display the number but to count up to the number. For example, If the number was 368, it would climb quickly to the 250-300's then start to slow down as it gets to the number. Now this number will not always be 368, the number varies. It could be 6 or 129 or 1023 so it can't be a set-in-stone number. I obviously know how to create the UILabel as I already have it being used in my app, I just need help with counting upwards to it.
I have a "Player view", blah blah blah they get a score and it is passed to another view "Gameover view" and the score is display in a the UILabel.
Here is the code for the "Player view" to pass the data:
//finalScore is a int and so is currentScore
second.finalScore = self.currentScore;
Here is the code for the "Gameover view" to pass the data:
- (void)viewWillAppear:(BOOL)animated {
// scoreLabel is the IBOutlet to the UILabel in IB for displaying the score.
self.currentIndex++;
if(self.currentIndex == [self.numbersArray count]) self.currentIndex = 0;
self->scoreLabel.text = [NSString stringWithFormat:#"%d",self->finalScore];
}
Thanks in advance!
I figured it out myself. I found this control and it works just as I need it to! Click Here
You can update the UILabel object in a loop, using the Timer. As time progresses at a certain interval, "slow it down" by adding on the difference in time. Sample code for updating a UI object via Timer is a common bit of code on the Internet, as it's the way you code progress bars.
how to use the progress bar in the iphone app
Just call this method every certain interval (ex every 0.1 seconds), using a NSTimer
It will increment the value up to the target number.
-(void)countUp:(id)sender {
int targetNumber = yourNumber;
int currentNumber = [[myLabel text] intValue];
// Calculate the increment for this time. Mess with this for different speeds.
// Just multiply some of the numbers by some constants (ex. 2) until you
// achieve the desired effect.
double increment = (targetNumber - currentNumber) / targetNumber;
NSString *newValue = [NSString stringWithFormat:#"%f",currentNumber + increment];
[myLabel setText:newValue];
if ([newValue doubleValue] >= targetValue) {
[(NSTimer *)sender invalidate];
}
}
For the timer:
[NSTimer scheduledTimerWithInterval:0.1
target:self
selector:#selector(countUp:)
userInfo:nil
repeats:YES];
using literals it become quite easy to play
UILabel * label;
label.text = #(label.text.integerValue+1).stringValue;

Call method only once every app launch

Hey guys at Stackoverflow!
I need your help. I am looking for a way to call my method only once after the application has launched and "save" the state of the color of the UIView. At first I will show you my code, that I can explain it in a better way:
-(void)viewWillAppear:(BOOL)animated {
NSArray *colors = [NSArray arrayWithObjects:[UIColor myWhiteColor],[UIColor myBlueColor],[UIColor myCyanColor],[UIColor myGreenColor],[UIColor myOrangeColor],[UIColor myPurpleColor],[UIColor myRedColor],[UIColor myViolettColor],[UIColor myYellowColor], nil];
NSInteger randomIndex = random() % [colors count];
colorTransparentView.backgroundColor = [colors objectAtIndex:randomIndex];
colorTransparentView.opaque = NO;
colorTransparentView.alpha = 1.0;
}
Now I explain you my issue.
As you can see, the code above changes the color of the UIView, every time the "viewWillAppear" method was called. The code randomly changes the color of the UIView(in the .xib) which is linked with an IBOulet to the header file. The problem is that every time I get back to the view I will get a different color.
However, I want to set the random color of the UIView only once the application started. This color should stay until the application was closed from the Multitasking. I can't see any way to solve this. I tried to call the code in the applicationDidFinishedLaunchingWithOptions method, but I wasn't succsessful.
Also I tried the dispatch_once method to call it only once, but as you may think of the color was never called again, so the view got no color the second time loaded.
I really would appreceate if you could help me with this.
Thanks in Advance,
Noah
EDIT:
My header:
#interface ViewController : UIViewController {
IBOutlet UIView *colorTransparentView;
}
#end
What about using a static variable? initialise it with 0 and after changing color in your view will appear. set it to 1 and keep checking it.
int static colortaken = 0;
int static colorindex;
- (void)viewWillAppear:(BOOL)animated
{
NSArray *colors = [NSArray arrayWithObjects:[UIColor myWhiteColor],[UIColor myBlueColor],[UIColor myCyanColor],[UIColor myGreenColor],[UIColor myOrangeColor],[UIColor myPurpleColor],[UIColor myRedColor],[UIColor myViolettColor],[UIColor myYellowColor], nil];
if (colortaken == 0)
{
NSInteger randomIndex = random() % [colors count];
colorindex = randomIndex;
colorTransparentView.backgroundColor = [colors objectAtIndex:randomIndex];
colorTransparentView.opaque = NO;
colorTransparentView.alpha = 1.0;
}
else
{
// do nothin
colorTransparentView.backgroundColor = [colors objectAtIndex:colorindex];
colorTransparentView.opaque = NO;
colorTransparentView.alpha = 1.0;
}
// at end
colortaken = 1;
}
Use dispatch_once. You can read up on singleton approaches in general, but this is the recommended approach.

Linking stepper to Slider UIObject in C4

I am very new to C4 so please be gentle...
If I want to link a slider value to a label this is done with NSString stringWithFormat... e.g.:
self.mylabel.text = [NSString stringWithFormat:#"%4.2f",slider.value];
I added a stepper object as well, and now it also updates mylabel:
self.mylabel.text = [NSString stringWithFormat:#"%4.2f",stepper.value];
But it would be intuitive if the slider position follows the label value when I'm using the stepper. but .value is not an available property in UILabel... so how do I take the mylabel.text property and push that into the slider.value property without getting a datatype mismatch error?
This question has 2 answers, how to do it using C4 objects and how to do it with Interface Builder / UIControls. I'll show both ways, UI first so that I can compare the C4 way afterwards.
UIControl
To do this with UIControls first set up your C4WorkSpace.h header so that it has the following methods and properties:
#property (assign, nonatomic) IBOutlet UILabel *myLabel;
#property (assign, nonatomic) IBOutlet UISlider *mySlider;
#property (assign, nonatomic) IBOutlet UIStepper *myStepper;
-(IBAction)sliderWasUpdated:(UISlider *)slider;
-(IBAction)stepperWasUpdated:(UIStepper *)stepper;
Then, in your drag all three components onto your projects XIB file (i.e. a UISlider, UILabel and UIStepper). Link the action sliderWasUpdated: to the slider using the valueChanged option, and the stepperWasUpdated: action to the stepper also using the valueChanged option. You do this step by selecting C4Canvas.xib from your project then right-clicking on the yellow cube, then dragging from the actions listed in the pop-up menu to each of the objects that you recently placed on the canvas.
Next, add the following code to your C4WorkSpace.m file:
#implementation C4WorkSpace
-(void)setup {
self.myStepper.minimumValue = 0.0f;
self.myStepper.maximumValue = 10.0f;
self.mySlider.minimumValue = 0.0f;
self.mySlider.maximumValue = 10.0f;
}
-(IBAction)sliderWasUpdated:(UISlider *)slider {
slider.value = [C4Math round:slider.value];
self.myLabel.text = [NSString stringWithFormat:#"%4.2f",slider.value];
self.myStepper.value = slider.value;
[self.myLabel sizeToFit];
}
-(IBAction)stepperWasUpdated:(UIStepper *)stepper {
self.myLabel.text = [NSString stringWithFormat:#"%4.2f",stepper.value];
self.mySlider.value = stepper.value;
[self.myLabel sizeToFit];
}
#end
In the setup we make sure that the min/max values of both UI objects are the same (so that we can keep them matched up).
In the stepperWasChanged: method we do two things:
We use the stepper's value to set the label's text
We also use the stepper's value to set the slider's value!
In the sliderWasChanged: method we do the same thing, updating the stepper, but we also round the value of the slider so that it increments in steps (just to keep things tidy).
C4Control
To do the same with C4 objects, instead of native UI objects, we set things up a little differently. First, we don't add anything to our C4Canvas.xib, instead we'll set the objects up manually.
In your C4WorkSpace.h file, add the following lines of code:
#property (readwrite, nonatomic, strong) C4Label *myLabel;
#property (readwrite, nonatomic, strong) C4Slider *mySlider;
#property (readwrite, nonatomic, strong) C4Stepper *myStepper;
-(void)sliderWasUpdated:(C4Slider *)slider;
-(void)stepperWasUpdated:(C4Stepper *)stepper;
Notice that most of this is the same except we're using C4 instead of UI prefixes. Also, we call our methods -(void) instead of -(IBAction) because we're not using Interface Builder.
Next, add the following code to your C4WorkSpace.m:
#implementation C4WorkSpace
-(void)setup {
[self createAddObjects];
//calibrate the min/max values
self.myStepper.minimumValue = 0.0f;
self.myStepper.maximumValue = 10.0f;
self.mySlider.minimumValue = 0.0f;
self.mySlider.maximumValue = 10.0f;
}
-(void)sliderWasUpdated:(C4Slider *)slider {
slider.value = [C4Math round:slider.value];
self.myLabel.text = [NSString stringWithFormat:#"%4.2f",slider.value];
self.myStepper.value = slider.value;
[self.myLabel sizeToFit];
}
-(void)stepperWasUpdated:(C4Stepper *)stepper {
self.myLabel.text = [NSString stringWithFormat:#"%4.2f",stepper.value];
self.mySlider.value = stepper.value;
[self.myLabel sizeToFit];
}
-(void)createAddObjects {
//set up the objects
self.myLabel = [C4Label labelWithText:#"values"];
self.myStepper = [C4Stepper stepper];
self.mySlider = [C4Slider slider:CGRectMake(0, 0, 192, 44)];
//position them
CGPoint centerPoint = CGPointMake(self.canvas.center.x,
self.canvas.center.y - 100);
self.myStepper.center = centerPoint;
centerPoint.y += 100;
self.myLabel.center = self.canvas.center;
centerPoint.y += 100;
self.mySlider.center = centerPoint;
//set up action bindings
[self.mySlider runMethod:#"sliderWasUpdated:"
target:self
forEvent:VALUECHANGED];
[self.myStepper runMethod:#"stepperWasUpdated:"
target:self
forEvent:VALUECHANGED];
[self.canvas addObjects:#[self.myStepper,self.myLabel,self.mySlider]];
}
#end
DIFFERENCES
The major difference between the two approaches is whether or not you use Interface Builder. In the C4 approach we need to add a method called createAddObjects to our project so that our slider, label and stepper all get added to the canvas.
This method also contains the methods for binding the actions of our C4UIElements to our code, which happens in the lines:
[self.mySlider runMethod:#"sliderWasUpdated:"
target:self
forEvent:VALUECHANGED];
[self.myStepper runMethod:#"stepperWasUpdated:"
target:self
forEvent:VALUECHANGED];
Once these are set up the only difference is specifying the use of C4 objects instead of UI objects, like:
-(void)sliderWasUpdated:(C4Slider *)slider {...}
instead of
-(IBAction)sliderWasUpdated:(UISlider *)slider {...}

how to get the value of two sliders during sliding?

I have two sliders in my view and I want to get both value when I slide each one .
but whenever I try to do this the value of the other slider which is not sliding becomes zero and literally I get only one value.
this is what I have done
create two IBAction for each slider
create two property for each slider
#property (retain , nonatomic ) IBOutlet UISlider * slider2;
-(IBAction) slidingN : (id) sender
{
UISlider * si = (UISlider *) sender;
int value = (int) si.value;
NSString * newText = [[NSString alloc] initWithFormat:#"%i" , value];
myLable.text = newText;
int valueD = (int)slider2.value;
[self callSubView:value :valueD];
}
but the value is 0 how can I have both values?
Problem Solved (surpassingly) after restarting Xcode

UIButton Analog for reversesTitleShadowWhenHighlighted for Selected State

I am using the reversesTitleShadowWhenHighlighted property on UIButton to reverse the title's text shadow(and it works great), but I really want the same thing for the selected state of the button(UIControlStateSelected).
Any answers to the related questions are also welcome:
Is there a way to do this with a UIButton without modifications?
Is there a way to set the shadowOffset of the title label per state with UIButton(similar to what is possible with the shadowColor)?
If you were to extend UIButton to add this, how would you do it?
I run into the same problem, so I guess it's no for your first two questions. Here is how I subclassed it :
#interface MyButton : UIButton
#property (nonatomic) BOOL reversesTitleShadowWhenSelected;
#end
#implementation MyButton
#synthesize reversesTitleShadowWhenSelected;
- (void)setSelected:(BOOL)selected
{
if (self.reversesTitleShadowWhenSelected)
{
if ((selected && !self.isSelected) ||
(!selected && self.isSelected))
{
CGSize offset = self.titleLabel.shadowOffset;
offset.width *= -1;
offset.height *= -1;
self.titleLabel.shadowOffset = offset;
}
}
[super setSelected:selected];
}
#end