Im creating an app and one part of it has a tap counter in it.
The UI consists of a button which is pressed for counting, a reset button and a label which displays the amount of taps. The problem is that I want the iDevice to vibrate or make a sound after a specific amount of taps and then end there so the counter label doesn't react to the button anymore.
Here is my code so far:
.h
#interface TapsViewController : UIViewController {
int counter;
IBOutlet UILabel *count;
}
-(IBAction)plus;
-(IBAction)reset;
#end
.m
- (void)viewDidLoad {
counter=0;
count.text = #"Start";
}
-(IBAction)plus{
counter=counter + 1;
count.text = [NSString stringWithFormat:#"%i", counter];
}
-(IBAction)reset{
counter=0;
count.text = [NSString stringWithFormat:#"Start"];
}
How do I have the app vibrate or make a sound when the counter reaches a predetermined value?
Thank you for your help!
well to make it stop responding to the taps, just do an if statement
For example,f if you want it to stop after 5 taps.
-(IBAction)plus{
if (counter < 4) {
//just a tip, counter++; does the same thing as counter += 1; which does the same thing as counter = counter+1;
counter++;
}
else if (counter == 4) {
//after four taps, counter will equal 4, so this part will be called on the 5th tap.
counter++;
//play your sound or do your vibrate here
}
count.text = [NSString stringWithFormat:#"%i",counter];
}
To do the vibrate, look at Wain's answer. To play a sound, check out AVAudioPlayer.
Create an outlet to the button and disable it when the count limit is reached (so it won't respond to touches any more). Then enable it again when you reset.
To vibrate:
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
Related
I have a lot of UIButtons in my app. I need to record which ones the user pushed, exactly the time elapsed between each button press, and then relay the information back to the user. Now, I've tried figuring out the logic to do this for quite some time. This is what I've tried first:
I start by firing an NSTimer. Every time the user presses a button, I store the time he pushed the button in arrayOne, and store the button he pushed in arrayTwo.
I have a "Playback" button. The goal of this playback button is to loop through both arrays, programatically pushing the buttons the user pressed at the elapsed time in which they pressed them.
I can probably achieve my goal this way, but this is just messy code and I don't like the design of it at all. What I'm looking for is an easier path. Is there an API that will record and playback the interaction?
I've found various links such as:
Audio Recording and Playback
That achieve similar to what I'm trying to do, but the sounds I'm loading into the app that play when each button is tapped are not recorded via microphone. They are included in the bundle.
What I did for the solution was make every button have a unique tag and call the same method "onAppButtonPressed" using tags to execute specific code for each button. Before anything though I created a fake button execution so I could mark when the app was opened. After executing button specific code when a UIButton was pressed, I stored information in an array with the button's tag and the button's fire date. When I call the startPlayback method, a for loop loops through all of the button data and schedules each button with the correct timing since the view loaded. I made a simple storyboard with three test buttons and a start playback button.
View Controller:
#import "PlaybackButtonEventViewController.h"
#import "ButtonPlaybackData.h"
#interface PlaybackButtonEventViewController ()
#end
#implementation PlaybackButtonEventViewController
{
NSMutableArray *playbackInformationArray;
BOOL playbackMode;
}
#define kInitialPlaybackData -1
#define kButtonOneId 1
#define kButtonTwoId 2
#define kButtonThreeId 3
#define kPlaybackButtonId 4
- (void) viewDidLoad
{
[super viewDidLoad];
self->playbackInformationArray = [NSMutableArray array];
self->playbackMode = false;
ButtonPlaybackData *initialPlaybackData = [[ButtonPlaybackData alloc] init];
initialPlaybackData.buttonTag = kInitialPlaybackData;
initialPlaybackData.buttonFireDate = (NSDate*) [NSDate date];
[self->playbackInformationArray addObject:initialPlaybackData];
}
- (void) startPlaybackMode
{
self->playbackMode = true;
for (int playbackIndex = 0; playbackIndex < self->playbackInformationArray.count; playbackIndex++)
{
ButtonPlaybackData *currentPlaybackData = [self->playbackInformationArray objectAtIndex:playbackIndex];
if (currentPlaybackData.buttonTag == kInitialPlaybackData)
continue;
ButtonPlaybackData *initialPlaybackData = [self->playbackInformationArray objectAtIndex:0];
long timeToWait = (currentPlaybackData.buttonFireDate.timeIntervalSince1970 - initialPlaybackData.buttonFireDate.timeIntervalSince1970);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeToWait * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self onAppButtonPressed:[self.view viewWithTag:currentPlaybackData.buttonTag]];
if ((playbackIndex + 1) == self->playbackInformationArray.count)
{
self->playbackMode = false;
NSLog(#"Playback has ended!");
}
});
}
}
- (IBAction) onAppButtonPressed: (id) sender
{
if (![sender isKindOfClass:[UIButton class]])
return;
UIButton *button = (UIButton*) sender;
switch (button.tag)
{
case kButtonOneId:
NSLog(#"Button one pressed!");
break;
case kButtonTwoId:
NSLog(#"Button two pressed!");
break;
case kButtonThreeId:
NSLog(#"Button three pressed!");
break;
case kPlaybackButtonId:
[self startPlaybackMode];
break;
}
if (!playbackMode)
{
ButtonPlaybackData *buttonPlaybackData = [[ButtonPlaybackData alloc] init];
buttonPlaybackData.buttonTag = button.tag;
buttonPlaybackData.buttonFireDate = (NSDate*) [NSDate date];
[self->playbackInformationArray addObject:buttonPlaybackData];
}
}
#end
Now for the ButtonPlaybackData object, it has the following header file with an empty m file:
#import <Foundation/Foundation.h>
#interface ButtonPlaybackData : NSObject
#property(nonatomic) NSInteger buttonTag;
#property(nonatomic, retain) NSDate *buttonFireDate;
#end
Hope this helps!
I'm developing a calculator app and would like to add 10 storage registers that the User can store numbers to. The calculator's storyboard has a "STO" button that is pressed when the User wants to store an entry. The next numeric button pressed (button 0, button 1 ... button 9) would signify the register to store the entry in.
The app currently contains the method "STOButtonPressed":
- (IBAction)STOButtonPressed:(UIButton *)sender{
STOButtonPressed = YES;
}
I have a method for entering numbers into the calculator, called digitPressed:
- (IBAction)digitPressed:(UIButton *)sender{
NSString* digit = sender.currentTitle;
[_audioPlayer play];
if(!(self.dotNotation && [digit isEqualToString:#"."])){
if([digit isEqualToString:#"."])
self.dotNotation=YES;
if(self.userIsInTheMiddleOfTyping){
self.displayLabel.text = [self.displayLabel.text stringByAppendingString:digit];
}
else
{
self.displayLabel.text = digit;
self.userIsInTheMiddleOfTyping = YES;
}
}
}
Each number button on the calculator in the storyboard is tagged (button "0" is tag "0", button "1" is tag "1", etc....).
After pressing the "STO" button, I'd like the next button (0 thru 9) entry to be the storage register number. And I'd like to set the storage register number within the STOButtonPressed method. Not sure if that's possible, or how to do it if it is. I currently have several "IF statements" in the digitPressed method for determining the storage register number (not shown here). But that seems very cumbersome and the method has gotten very messy. I'd like to keep all code for the storage feature within the STOButtonPressed method. I've been working this unsuccessfully for many days, and feel I'm missing something. Can someone tell me how to make set the storage register number within the STOButtonPressed method, if that's even possible?
I'm using Xcode 5.
If you have set your button's tags properly, it should be pretty straightforward:
in your properties, add:
#property (nonatomic, assign) BOOL hasPressedSTOButton;
and change this because you don't want confusion between your var and your method name:
- (IBAction)STOButtonPressed:(UIButton *)sender{
self.hasPressedSTOButton = YES;
}
And change your tags to 1-10 instead of 0-9 because you want to make sure you are checking a button with a non-zero tag, and not some other button.
then change your digitPressed like this
- (IBAction)digitPressed:(UIButton *)sender{
NSString* digit = sender.currentTitle;
[_audioPlayer play];
if(!(self.dotNotation && [digit isEqualToString:#"."])){
if([digit isEqualToString:#"."])
self.dotNotation=YES;
if(self.userIsInTheMiddleOfTyping){
self.displayLabel.text = [self.displayLabel.text stringByAppendingString:digit];
}
else
{
self.displayLabel.text = digit;
self.userIsInTheMiddleOfTyping = YES;
}
} else if (sender.tag != 0) {
//store your stuff here. Tags range from 1 to 10. Use sender.tag - 1 if you want the values to range from 0 to 9.
}
}
I am making a stopwatch, but I only have the start button working. When the start button is pressed it enters a loop:
- (void)stopwatch
{
NSInteger hourInt = [hourLabel.text intValue];
NSInteger minuteInt = [minuteLabel.text intValue];
NSInteger secondInt = [secondLabel.text intValue];
if (secondInt == 59) {
secondInt = 0;
if (minuteInt == 59) {
minuteInt = 0;
if (hourInt == 23) {
hourInt = 0;
} else {
hourInt += 1;
}
} else {
minuteInt += 1;
}
} else {
secondInt += 1;
}
NSString *hourString = [NSString stringWithFormat:#"%02d", hourInt];
NSString *minuteString = [NSString stringWithFormat:#"%02d", minuteInt];
NSString *secondString = [NSString stringWithFormat:#"%02d", secondInt];
hourLabel.text = hourString;
minuteLabel.text = minuteString;
secondLabel.text = secondString;
CGRect hourFrame = self->hourBar.frame;
CGRect minuteFrame = self->minuteBar.frame;
CGRect secondFrame = self->secondBar.frame;
if ((NSInteger)hourFrame.size.height != hourInt) { // check if we need to modify
hourFrame.origin.y -= ((hourInt * 10.0) - hourFrame.size.height);
hourFrame.size.height = (hourInt * 10.0);
self->hourBar.frame = hourFrame;
}
if ((NSInteger)minuteFrame.size.height != minuteInt) { // check if we need to modify
minuteFrame.origin.y -= ((minuteInt * 4.0) - minuteFrame.size.height);
minuteFrame.size.height = (minuteInt * 4.0);
self->minuteBar.frame = minuteFrame;
}
if ((NSInteger)secondFrame.size.height != secondInt) { // check if we need to modify
secondFrame.origin.y -= ((secondInt * 4.0) - secondFrame.size.height);
secondFrame.size.height = (secondInt * 4.0);
self->secondBar.frame = secondFrame;
}
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(stopwatch) userInfo:nil repeats:NO];
}
When the stop button is pressed, I want this loop to be paused so that when the user presses start, it resumes the stopwatch.
When the reset button is pressed, I want the loop to stop and reset back to 0.
If you could make your answer as simple as possible, that would be really great because I'm only a beginner!
You should take the timer out of your 'loop' method and make it repeating. It should be the thing that is driving the stopwatch, not that fact that you have started your 'loop'. Then, you can stop the timer when you need to and restart it later. You can also google to find out the correct way to pause the timer (you need to change the timer to start at a specified fire date to be 100% accurate, but just knowing how many seconds are left may be enough for your case).
Based on your previous question it sounds like you have moved your timer outside of your stopwatch method. Remember, timers and loops are two very different things.
You can stop an NSTimer by calling invalidate on it. Please read Apple's documentation for more details.
One way that might not be completely thread safe but I just tested it and it works is to have a variable that is set to true when you click start and false when you click stop.
For example, in my .h, I added #property (nonatomic) BOOL go;, then as the first line of the method you provided, add an if check that looks like this:
- (void)stopwatch {
if (!self.go) return;
//..do the rest of your stopwatch code
//the timer call
}
then my start and stop button calls look like:
- (void)start {
if (!self.go) { // Do not call the stopwatch method again if it is already going.
self.go = YES;
[self stopwatch];
}
}
- (void)stop {
self.go = NO;
}
Loop is the wrong construct for this type of operation. For better control, use NSTimer.
A relevant example can be found here.
EDIT:
My above answer was based on prior version of your question.
So yes, the NSTimer should be the controlling thing for your loop, not part of the loop.
General implementation:
Start should only mark timer starting. Also note the current time from system clock. (this is optional though)
Timer function should change value of the time variable, and compare it against the stopwatch set value. If time elapsed == set value, stop the timer using invalidate.
Stop should interrupt the timer and stop it, and also the elapsed time value should be set to 0. Time Variable should be reset too, if you do not want to reuse this same time value again.
I am developing a game in cocos 2D. In my game I need to tap my view a certain number of times. If the user attempts to tap the view beyond the limit, it should display an alert.
Please can someone help me to find number of taps on the view. Most important is number of taps is not simultaneously. In the Total game the user can tap only a certain number of times, after which they should not tap the view.
You can do this with NSTimeInterval.
//decalre this in interface file
NSTimeInterval mLastTapTime;
.
-(id)init
{
if(self = [super init])
{
mLastTapTime = [NSDate timeIntervalSinceReferenceDate];
}
return self;
}
//in touch method
NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval diff = currentTime - mLastTapTime;
if(diff < 0.3 )
{
//do whatever you want if user press with 0.3second
}
mLastTapTime = [NSDate timeIntervalSinceReferenceDate];
As LearnCocos suggested you can simply use a tap gesture recognizer. it takes a variable number to taps and/or touches to fire.
UITapGestureRecognizer *tapLimitRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapLimitRecognizer:)];
[tapLimitRecognizer setNumberOfTapsRequired:6];
[self addGestureRecognizer:tapLimitRecognizer];
[tapLimitRecognizer release]; // if not using arc.
-(void)tapLimitRecognizer:(UITapGestureRecognizer *)tapLimitRecognizer
{
...add your alert view here
}
I am currently wondering how you can record audio in iOS. I know many people comprehend this as recording from a microphone and playing it back, but this is not the case. I am making a sound recording application for the iPad. In the GarageBand application that Apple has on the iOS App store, you can record your own sounds and play them back from within the application. If this doesn't make sense, think of it as this:
All I am trying to do is make a button that plays a sound. I need to know how to record that button sound and be able to play the sound sequence back. So if I pressed "record" then buttons "A, F, J" and then "stop" then press "play" it would playback what it recorded (sounds A F and J).
I am trying to make it so that you can record and make your own music within this app. Sorry if this is confusing, please help me to the best of your abilities. Thanks!
You could create two NSMutableArrays and empty them when you hit record. You would also need an NSTimer and an int. So in header:
NSTimer *recordTimer;
NSTimer *playTimer;
int incrementation;
NSMutableArray *timeHit;
NSMutableArray *noteHit;
Include in your header all of the voids and IBActions and such below.
Make your sound buttons all have different, unique tags.
and then in your main file:
-(void)viewDidLoad {
timeHit = [[NSMutableArray alloc] init];
noteHit = [[NSMutableArray alloc] init];
}
-(IBAction)record {
recordTimer = [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(timerSelector) userInfo:nil repeats:YES];
[timeHit removeAllObjects];
[noteHit removeAllObjects];
incrementation = 0;
}
-(void)timerSelector {
incrementation += 1;
}
-(IBAction)hitSoundButton:(id)sender {
int note = [sender tag];
int time = incrementation;
[timeHit addObject:[NSNumber numberWithInt:time]];
[noteHit addObject:[NSNumber numberWithInt:note]];
[self playNote:note];
}
-(IBAction)stop {
if ([recordTimer isRunning]) {
[recordTimer invalidate];
} else if ([playTimer isRunning]) {
[playTimer invalidate];
}
}
-(IBAction)playSounds {
playTimer = [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(playback) userInfo:nil repeats:YES];
incrementation = 0;
}
-(void)playback {
incrementation += 1;
if ([timeHit containsObject:[NSNumber numberWithInt:incrementation]]) {
int index = [timeHit indexOfObject:[NSNumber numberWithInt:incrementation]];
int note = [[noteHit objectAtIndex:index] intValue];
[self playNote:note];
}
}
-(void)playNote:(int)note {
//These notes would correspond to the tags of the buttons they are played by.
if (note == 1) {
//Play your first note
} else if (note == 2) {
//Play second note
} else if (note == 3) {
//And so on
} else if (note == 4) {
//etc.
}
}
With a little bit of fiddling (I doubt this code is perfect), you might get this to work. Like you will probably want the play/record buttons to be disabled once you hit one of them. Good luck!