I'm implementing a UISlider that allows users to select a position on a football field. The idea of the slider is for the field to scroll forward when the slider value is above a certain value (the section with the arrow).
My issue is that I can only respond to the slider on UIControlEventValueChanged - so the field will only scroll forward when the user is actually moving the slider. I'd like it to move forward as long as the value is above a certain amount.
Any idea how I can do this? (I'm open to any suggestion, including an implementation that does not use a UISlider, composite implementations, etc.).
Here's the implementation:
The easiest way to handle this is with a timer. Add an NSTimer instance variable to your class, named—for the sake of the example below—moveTimer, then set up something like this:
- (void)sliderChanged:(UISlider *)slider
{
if(slider.value > 5)
{
if(moveTimer == nil)
{
moveTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self action:#selector(move) repeats:YES];
}
}
else
{
if(moveTimer != nil)
{
[moveTimer invalidate];
moveTimer = nil;
}
}
}
- (void)move
{
// update the background behind your slider
}
I found a solution that works for me:
[self performSelector:#selector(sliderMoved) withObject:nil afterDelay:0.5];
I call the sliderMoved method after every 0.5 seconds when slider.value > 0.5.
Try setting the continuous property of the slider to YES.
Related
I am making a game with a boost button. Of course, just leaving it as enabled would allow the player to tap it constantly. So, I need a 20 second delay before it is possible to push the button again. Also, would it be possible to show this progression on the button, preferably on the button itself?
In the future please try to show what you have tried to solve your problem. However, since you are new I'm going to let it slide once!
This code uses a NSTimer that is called once per second. It will fire what ever code you specify for "Boost". It will also disable user interaction on your button until it has been 20 seconds from when the button was pressed at which point it will allow the user to press the button again, and finally, this code displays how many seconds remain until "Boost" can be used again on the titleLabel property of your button.
- (IBAction)buttonPressed:(id)sender
{
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(activateBoost) userInfo:nil repeats:YES];
}
- (void)activateBoost
{
if (myButton.userInteractionEnabled == YES) {
//Put your Boost code here!
}
if ([myButton.titleLabel.text intValue] == 0) {
[myTimer invalidate];
[myButton setTitle:#"20" forState:UIControlStateNormal];
myButton.userInteractionEnabled = YES;
}else{
myButton.userInteractionEnabled = NO;
int currentTime = [myButton.titleLabel.text intValue];
int newTime = currentTime - 1;
myButton.titleLabel.text = [NSString stringWithFormat:#"%d",newTime];
}
}
In order for the above code to work, you will need the declare a NSTimer named "myTimer" and a UIButton "myButton". You will also need to set the buttons initial text to "20".
I'm working on a very simple iPhone game that involves choosing the right colored button as many times in a row based on a randomized voice prompt. I have it set up so that if the button is one color and gets clicked, it always goes to a hard-coded color every time (e.g. if you click red, it always turns blue). The color change method is set up in an IBOutlet. I have a timer set up in a while loop, and when the timer ends it checks if the player made the right selection. The problem is that the button color change does not occur until after the timer runs out, and this causes a problem with the method used to check the correct answer. Is there a way to make this color change happen instantly? From what I've searched I know it has something to do with storyboard actions not occurring until after code executes, but I haven't found anything with using a timer. Here is a section of the method that calls the timer if the answer is correct:
BOOL rightChoice = true;
int colorNum;
NSDate *startTime;
NSTimeInterval elapsed;
colorNum = [self randomizeNum:middle];
[self setTextLabel:colorNum];
while (rightChoice){
elapsed = 0.0;
startTime = [NSDate date];
while (elapsed < 2.0){
elapsed = [startTime timeIntervalSinceNow] * -1.0;
NSLog(#"elapsed time%f", elapsed);
}
rightChoice = [self correctChoice:middleStatus :colorNum];
colorNum = [self randomizeNum:middle];
}
One of two things stood out
You're using a while loop as a timer, don't do this - the operation is synchronous.
If this is run on the main thread, and you code doesn't return, your UI will update. The mantra goes: 'when you're not returning you're blocking.'
Cocoa has NSTimer which runs asynchronously - it is ideal here.
So let's get to grips with NSTimer (alternatively you can use GCD and save a queue to an ivar, but NSTimer seems the right way to go).
Make an ivar called timer_:
// Top of the .m file or in the .h
#interface ViewController () {
NSTimer *timer_;
}
#end
Make some start and stop functions. How you call these is up to you.
- (void)startTimer {
// If there's an existing timer, let's cancel it
if (timer_)
[timer_ invalidate];
// Start the timer
timer_ = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:#selector(onTimerFinish:)
userInfo:nil
repeats:NO];
}
- (void)onTimerFinish:(id)sender {
NSLog(#"Timer finished!");
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
- (void)stopTimer {
if (!timer_)
return;
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
And now
Put your timer test code in the onTimerFinish function.
Make an ivar that stores the current choice. Update this ivar when a choice is made and make the relevant changes to the UI. Call stopTimer if the stop condition is met.
In the onTimerFinished you can conditionally call and startTimer again if you desire.
Hope this helps!
I have a global variable p.
I have a function that draws (or redraws) my complex UIView objects depending on the value of this global variable.
When I increase p my uiview objects needs to be redrawed. I need this be done using animation.
double pInitialValue=0.5;
double pNewValue=1.0;
//somewhere before animation we must call [self redraw] to draw our view with p=pInitialValue;
//then, when we call animate function.
//we change p to pNewValue
//and every step must be redrawed using [self redraw]
p=pNewValue; //using animation p must slowly grow from pInitialValue to pNewValue
[self redraw]; //and ofcourse user must see every step of animation so we need to call redraw function
for example I need an animation with duration 4.
That means that during this 4 seconds my p must grow from pInitialValue to pNewValue and in every step my redraw function must be called
Help me, please. How can it be done?
You can use a timer to increase your p value. Do that like this: have an instance variable in your class defined like
NSTimer *timer;
After that just before you want your animation to happen do:
CGFloat timerInterval = 1 / 30; // This means 30 frames per second. Change this as you want.
timer = [NSTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:#selector(increaseP) userInfo:nil repeats:YES];
Than have a method called increasePlike this:
- (void)increaseP
{
if (p < maxPValue) {
// Increase p here and redraw your things
}
else {
[timer invalidate];
}
}
Let me know if you have any question and if this works for you.
I want to have a custom loading menu made from a series of stills, that loops 3 times and then reveals a picture. Currently the picture is visible from the start. I want to use isAnimating to find out when the loading animation has stopped, and either change myImage.hidden off, or have the UIImageView initially containing a white image, then being replaced with the picture when isAnimating returns NO.
Apple's website just says
- (BOOL)isAnimating
and that it returns a Boolean YES or NO.
but how do you use this?
I need things to happen depending on whether something is animating or not, so i do i put the result it returns in a variable and check that in the conditional of an if statement?
put it in an if statement itself?
or is it a while statement?
or is it like:
- (BOOL)isAnimating{
//stuff to do if it is
}
or am i just getting the whole concept entirely wrong?
I guess isAnimating method just tells you if an UIViewImage is actually performing an animation.
Since you just want to create a short loading before displaying your image, why don't you simply use a timer?
You can do something like this
- (void)startAnimation {
yourImageView.hidden = YES; // Keep you image hidden while loading
[yourLoadingAnimation startAnimating]; // Start you loading animation
NSInteger timeout = 2; // Duration in seconds of your loading animation
[NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(stopAnimation) userInfo:nil repeats:NO]; // Set the timer
}
- (void)stopAnimation {
[yourLoadingAnimation stopAnimating]; // Stop your loading animation
yourLoadingAnimation.hidden = YES; // Hide your laading animation
yourImageView.hidden = NO; // Display your image
}
You know how Mario just keeps running to the right when you press and hold the right-button on the D-Pad? In the same manner, I want my UIButton to continuously fire its action for the duration that it is held down. Is this possible for a UIButton? If not, is this possible to do with a UIImageView by overriding a touch handling method in a certain way? Actually, before trying to do get this done with UIButton I had some UIImageViews (Arranged to function as a D-Pad) that were checked by touch handling methods but things started to get messy so I thought this could be done easier with UIButton and thus switched over. Anybody who knows how to get recognition of a continuous, stationary (not-moved) down-touch, please share.
You can also do similar to what is shown in the previous answer and still use a UIButton.
Just have the timer started on the "Touch Down" and have the timer stopped on either "Touch Up Inside" or "Touch Up Outside".
Personally, I like using UIButtons because they offer some built in visual enhancements you don't have to code on your own.
Don't use a button, use multi-touch and NSTimer:
Make a view-local NSTimer object inside your interface, then use it to start/cancel the timer
-(void)movePlayer:(id)sender {
<Code to move player>
}
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:#selector(movePlayer:) userInfo:nil repeats:YES];
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
if (timer != nil)
[timer invalidate];
timer = nil;
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
if (timer != nil) {
[timer invalidate];
timer = nil;
}
}
This way, you can repeat the event at a predefined interval, and not have to rely on a button, and get the repeat behaviour you're looking for.
Note the touchesMoved trigger - if they move their finger, this cancels the timer, and the player stops moving.
For me the following works:
Create a button.
Create 2 methods (stop touching and start touching) either in view controller or to a subclass.
Add 3 Control Events. Touch Up Inside and Touch Drag Exit that both of them go to stop touching method and Touch Down goes with start touching method.
When start touching method invokes we should start an NSTimer with interval approximately 0.2 (it's up to you how fast you would like to be invoked), repeat true and as a selector a method that you want to be invoked (having the actual stuff you want to execute when user hits the button).
When stop touching method invokes we should invalidate timer
(.invalidate()) and assign timer as nil.
That's all!
And Now for Something Completely Different:
ReactiveCocoa 6.
self.button.reactive
.controlEvents([.touchDown])
.observeValues { button in
SignalProducer.timer(interval: .milliseconds(500), on: QueueScheduler.main)
.take(until: button.reactive.controlEvents([.touchDragOutside, .touchDragExit, .touchUpInside, .touchUpOutside, .touchCancel]).map { _ in return })
.prefix(value: Date())
.startWithValues { date in
NSLog("\(date)")
}
}