views:

58

answers:

2

I'm not quite sure how UIButton works. For example, if I have a timer that goes off when I hit the start button, how do I make it so that if the button gets pressed again, it doesn't start another timer. Cause then my timer is going twice as fast. It seems like I'd want to invalidate my original timer if the button gets pressed twice and start a new timer, but I don't know how you can tell if the button gets pressed twice.

Also, is there a way to change the label on the UIButton once its pressed once, and then have it revert back when it gets pressed again? Like, Play/Pause? Thanks.

A: 

If your timer is not nil, somebody has already set it, which means the button has already been pressed. Consider:

@interface MyController : UIViewController {
    @private NSTimer *timer;
}
- (IBAction)pressButton: (id)sender;
@end

@implementation MyController
- (IBAction)pressButton: (id)sender {
    if (!self->timer) {
        self->timer = [NSTimer scheduledTimer...];
    }
}
@end
Jonathan Grynspan
Why do you have to use the arrow operator here? I thought you use the dot operator to access properties in Obj-C? Thanks.
Crystal
It's not a property, it's a field. See the class definition. If it were meant to be a *publicly accessible* property, you'd use `@property (...) NSTimer *timer;` but these sorts of timers are almost always private to the class that creates them. So, dot operator for @properties, arrow operator for fields. :)
Jonathan Grynspan
A: 

Encapsulation and state management are very useful for situations like this. Depending on how much you wish to encapsulate, you might consider having a new object to be modified by the UIButton (you mention Play/Pause so I'm imagining some kind of media player?)

(You might consider an enumeration of states in this example to make it more extendable, but for simplicity I'll use a BOOL)

@interface MediaManager : NSObject
{

    BOOL isPlaying;  // whether
    NSTimer *playTimer; // timer used exclusively by the media manager
    //... other vars relating to type of media/selected track etc   anything related
}

-(void) togglePlay;


@property (nonatomic, synthesize) NSTimer *playTimer;
@propety (nonatomic, assign) BOOL isPlaying;

@end


@implementation MediaManager

@synthesize playTier, isPlaying;


- (void)togglePlay 
{
    if (!self.isPlaying)  
    {
        if (self.playTimer == nil) // if it hasn't already been assigned
        {
            self.playTimer = [NSTimer ...]; // create and schedule the timer
            self.isPlaying = YES;
        }
    }
    else 
    {
        if (self.playTimer != nil)
        {
            // get the timer and invalidate it
            self.isPlaying = NO;
        }
    }

}

@end

This is not the best though out example, but by encapsulating the state within a seperate object, your view UIButton can rely solely on the model state for presenting itself (assuming it holds a reference to an initialized Media Manager:

-(IBAction) buttonPressed: (id) sender
{
    [self.mediaManager togglePlay]; // play from whatever state it is in 

    // update the button text to reflect state
    if (self.mediaManager isPlaying)
    {
        self.mediaButton.textLabel.text = @"Pause";
    }
    else
    {
        self.mediaButton.textLabel.text = @"Play";
    }

}

This way you don't have to include the state handling each time you want to use this functionality, and the button always reflects the true state of the object it is acting on.

You might go a step further and encapsulate the state strings in the object also, which could be queried in a similar manner.

(Apologies for poorly written code, I've not had a coffee yet)

davbryn