In my small app, user clicks on "ON" button then I call a method with a while loop and "OFF" button should appear, however my "OFF" button does not show up.
-(void) myMethod{
while (_onButton.selected) {
[self vibrate];
NSLog(#"working");
}
}
- (IBAction)on:(id)sender {
_offButton.hidden=NO;
_offButton.selected=NO;
_onButton.hidden=YES;
_onButton.selected=YES;
[self myMethod];
}
- (IBAction)off:(id)sender {
_onButton.hidden=NO;
_offButton.hidden=YES;
_onButton.selected=NO;
_offButton.selected=YES;
}
As you are using this loop:
while (_onButton.selected) {
[self vibrate];
NSLog(#"working");
}
You aren't allowing the runloop to process new events, and so the button will never have the opportunity to change state (as it won't receive touches from the user). This is very bad and you shouldn't do this.
Instead continue your vibration by using a sound delegate method that continues all the time the button is selected (you don't show how you perform your vibration, so I cannot give more detail here).
EDIT After comment from OP:
- (void)vibrate
{
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
OK, AudioServices provides a "completion procedure" via AudioServicesAddSystemSoundCompletion() which will allow you to replay the vibration "sound" all the time the button is selected. Use this mechanism rather than the while loop.
It's an infinite loop mate. change the while loop like this:
while (_onButton.selected) {
[self vibrate];
NSLog(#"working");
_onButton.selected = NO;
}
or
while (_onButton.selected) {
[self vibrate];
NSLog(#"working");
break;
}
Related
I have a view with a UITapGestureRecognizer instance assigned to it. It correctly responds when the user taps once, but I'd like to prevent it from recognizing again if the user taps again within a short period of time.
I'm using this in a game where the user taps on locations to find hidden objects. I'm trying to prevent the "tap like crazy all over the screen" strategy from working.
Is there a simple solution to this?
I would not recommend using an NSTimer for resolutions less than 1 second. Plus, it has more overhead. Read this answer for more information on NSTimer vs CACurrentMediaTime().
- (IBAction)handleTap:(UITapGestureRecognizer *)tgr {
static NSTimeInterval previousTapTime = 0.0; // Or an ivar
if ((CACurrentMediaTime() - previousTapTime) > 1.0) {
// A valid tap was detected, handle it
}
previousTapTime = CACurrentMediaTime();
}
Use a timer to determine whether to accept the tap or not.
Create a BOOL ivar named something like denyTap. Also add an NSTimer ivar named tapTimer.
Then in your tap recognizer method you do something like:
- (void)tapHandler:(UITapGestureRecognizer *)gesture {
if (!denyTap) {
dentTap = YES;
// process the tap as needed
// Now setup timer - choose a desired interval
tapTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(tapTimer:) userInfo:nil repeats:NO];
}
}
- (void)tapTimer:(NSTimer *)timer {
denyTap = NO;
tapTimer = nil;
}
This function is designed to simulate a wait if the user is successful logging in. As you can see I dismiss the keyboard first but that doesn't stop NSThread from sleeping before the keyboard is dismissed. I think I need to harness the power of the dispatch queue but not quite sure. Any way I can dismiss the keyboard before sleep occurs?
-(IBAction)userLoginButtonPressed:(id)sender
{
/* resign first responders so that the user
can see the label of the server trying to log in */
[self.usernameField resignFirstResponder];
[self.passwordField resignFirstResponder];
self.statusLabel.text = #"Logging In...";
// create the server object and pass in the username and password values
IONServer *server = [[IONServer alloc] init];
NSString *user = self.usernameField.text;
NSString *pw = self.passwordField.text;
[server loggingInWithUserName:user password:pw];
// redirect based on result
if (server.result.success) {
[self serverSuccess];
[NSThread sleepForTimeInterval:2.0f];
} else {
[self serverFailure];
}
// store the server object as this class' server var
self.server = server;
NSLog(#"Result From Server: %#", server.result);
}
dismissing the keyboard is animated and this is enough for us to know that it happens async-ly, on main thread. In other words - you code block starts, dismissing the keyboard being added to main thread runloop, the thread sleeps for 2 seconds because you said so (Terrible thing to do if you ask me), and only than it's the keyboard's turn to get animated down and be dismissed.
A nice trick can be
[UIView animateWithDuration:0 animations: ^{
[self.usernameField resignFirstResponder];
[self.passwordField resignFirstResponder];
} completion: ^(BOOL finished) {
// Do whatever needed...
[NSThread sleepForTimeInterval:2.0f];
}];
BUT - I highly recommend finding a better solution than freezing the main thread.
Also - no what you asked for, but, assuming all your text fields are subviews of self.view you can just call [self.view endEditing:YES]; and you don't need to care about which text field is corrently the responder.
I'm developing a radio streaming app with XCode 4.5 for iOS 6 and mainly using storyboards.
I successfully made it to be able to play in background. So wherever I go from my app, whether to other tabs or even after clicking the Home button on the simulator,it keeps playing.
I'm using Matt Gallagher's audio streamer, which I include in my app delegate .m file below
#pragma mark - audio streaming
- (void)playAudio:(indoRadio *)np withButton:(NSString *)currentButton
{
if ([currentButton isEqual:#"playbutton.png"])
{
[self.nowplayingVC.downloadSourceField resignFirstResponder];
[self createStreamer:np.URL];
[self setButtonImageNamed:#"loadingbutton.png"];
[audioStreamer start];
}
else
{
[audioStreamer stop];
}
}
- (void)createStreamer:(NSString *)url
{
if (audioStreamer)
{
return;
}
[self destroyStreamer];
NSString *escapedValue = (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(nil,(CFStringRef)url,NULL,NULL,kCFStringEncodingUTF8);
NSURL *streamurl = [NSURL URLWithString:escapedValue];
audioStreamer = [[AudioStreamer alloc] initWithURL:streamurl];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playbackStateChanged:)
name:ASStatusChangedNotification
object:audioStreamer];
}
- (void)playbackStateChanged:(NSNotification *)aNotification
{
NSLog(#"playback state changed? %d",[audioStreamer isWaiting]);
if ([audioStreamer isWaiting])
{
[self setButtonImageNamed:#"loadingbutton.png"];
}
else if ([audioStreamer isPlaying])
{
[self setButtonImageNamed:#"stopbutton.png"];
}
else if ([audioStreamer isIdle])
{
[self destroyStreamer];
[self setButtonImageNamed:#"playbutton.png"];
}
}
- (void)destroyStreamer
{
if (audioStreamer)
{
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:ASStatusChangedNotification
object:audioStreamer];
[audioStreamer stop];
audioStreamer = nil;
}
}
- (void)setButtonImageNamed:(NSString *)imageName
{
if (!imageName)
{
imageName = #"playButton";
}
self.nowplayingVC.currentImageName = imageName;
UIImage *image = [UIImage imageNamed:imageName];
[self.nowplayingVC.playBtn.layer removeAllAnimations];
[self.nowplayingVC.playBtn setImage:image forState:0];
if ([imageName isEqual:#"loadingbutton.png"])
{
[self.nowplayingVC spinButton];
}
}
The problem I got is that when I click the play button, it starts the audio streamers but doesn't change into either loading button or stop button. This makes me unable to stop the audio since the button image is not changed. And since I put an NSLog inside the playbackStateChanged: method,I know that the playback state did change. It seems like the setButtonImagedNamed: method is not firing. Any thoughts?
Please have look of my answer:Disabled buttons not changing image in
For a button there is normal, selected, highlighted and disabled state.
So in your case you have to handle the normal and selected state.
In the xib, set image to a button for normal and selected state.
Now in the function of playAudio instead of checking the image name check the button state as below:
- (void)playAudio:(indoRadio *)np withButton:(NSString *)currentButton
{
if (playButton.selected)
{
//Stop the streaming
//set selection NO
[playButton setSelected:NO];
}
else
{
//Start the streaming
//set selection YES
[playButton setSelected:YES];
}
}
playButton is the IBOutlet of play button.
As you have set the images in xib, so the image automatically get changed as per the state of the image
In case you have added the playBtn programmatically, check if the button is declared as weak or its getting released somewhere. If it is weak, make it strong.
In case the button is in the nib file, then the problem is in the IBOutlet. Redo the connection properly.
It turned out that the delegate didn't know which nowplayingVC I was calling. #sarp-kaya's question about Calling a UIViewController method from app delegate has inspired me.
I actually have put this code in my view controller viewDidLoad:
myAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
but I forgot to add this line:
appDelegate.nowplayingVC = self;
So, all I need to do is adding that one line and it works now. okay, so silly of me for not noticing such a basic thing. But thanks for your helps :D
just wondering how to make a button "START" that when pressed triggers a function and the text changes to "STOP". then when pressed again the function will stop and the text changes back to "START"
Ive got the button already that starts the function. and i can handle changing the title, just not sure on what to use to make the 1 button have 2 functions
Add the IBAction method like:
- (IBAction)buttonTapped:(id)sender
{
UIButton *btn = (UIbutton *)sender;
NSString *title=btn.titleLabel.text;
if ([title isEqualToString:#"Start"])
{
//Start
}else
{
//Stop
}
}
Please try this:
- (IBAction) buttonAction:(id)sender
{
if([[(UIButton *)sender currentTitle]isEqualToString:#"START"])
{
[actionButton setTitle:#"STOP" forState:UIControlStateNormal];
//start the action here and change the button text to STOP
}
else if([[(UIButton *)sender currentTitle]isEqualToString:#"STOP"])
{
[actionButton setTitle:#"START" forState:UIControlStateNormal];
//stop the action here and change the button text to START
}
}
you have at least two options here:
Check for the title of your button and depending on the value call an action or the other.
Create two different buttons, each one with his action and show/hide them.
Is there a way to cancel a touch for a UIButton? I envision it as something like:
- (BOOL)shouldProcessTouch {
return NO;
}
You know, a place where you can run logic and cancel the touch in certain scenarios. Any ideas?
** EDIT **
For those familiar with event based systems, I'm looking for the equivalent of:
event.stopPropagation();
In most cases, a UIButton will call a method when touched. It may look like this:
-(IBAction)submitButtonPressed:(id)sender{
if(shouldRespond){
//do things normally
}
else{
return; //do nothing
}
}
In this example, shouldRespond is your flag. You should set this flag based on whatever conditions you have.
edit: the flag is a BOOL
Why not just putting button in disabled state?
button.enabled = NO;
Or, disable user interaction for it:
button.userInterationEnabled = NO;