Check Which Order Buttons Are Pressed Objective-C - ios

I am developing an app that I need to check which order buttons are pressed in. I had 3 buttons and if they are pressed in the incorrect order I will have a UIAlertView. How can I check the order of presses?
Thanks

You could wire up an action to the buttons (like "Touch Up Inside"), and have it log which buttons are pressed, and maybe have a counter incrementing as well. Then when the counter gets to three, have it go through the list of button presses, and verify if they are the order you anticipate.
Below is an example of what I mean. For this example, you have to wire up all 3 buttons "Touch Up Inside" to that same IBAction. Of course you replace the NSLogs with your UIAlertView, but this shows the gist of what I said.
#interface comboSOTestViewController ()
#property (strong, nonatomic) NSMutableArray *buttonTitles;
#end
#implementation comboSOTestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.buttonTitles = [[NSMutableArray alloc]init];
}
- (IBAction)comboButtonPress:(UIButton *)sender
{
[self.buttonTitles addObject:sender.titleLabel.text];
if (self.buttonTitles.count > 2)
{
BOOL bad = NO;
NSArray *correctOrder = #[#"Second", #"Third", #"First"];
for (int i=0; i < 3; i++)
{
if (![self.buttonTitles[i] isEqualToString:correctOrder[i]])
{
bad = YES;
}
}
if (bad == YES)
{
NSLog(#"WRONG ORDER");
}
else
{
NSLog(#"CORRECT ORDER");
}
}
}

Related

Recording A Users Interaction With Interface

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!

Simultaneous Button Press

I am trying to build an app where two buttons need to be pressed simultaneously.
if (self->button1.touchInside && self->button2.touchInside) {
NSLog(#"Two buttons pressed");
}
else if (!self->button1.touchInside | !self->button2.touchInside){
NSLog(#"One button pressed");
}
Both buttons are attached to the View Controller using the 'Touch Down' gesture option. When I press both buttons at the same time (with one press) the console window prints:
One button pressed
Two buttons pressed
This interferes with how my application works. I only want the console to print
Two buttons pressed
Thanks
What i understand is you need to take some action when both the buttons are pressed. Watever you try, there is going to be a lag between the touches of these buttons. A better approach would be to check if press on both buttons is finished. Hope following works for you -
#property(nonatomic, assign) BOOL firstButtonPressed;
#property(nonatomic, assign) BOOL secondButtonPressed;
//in init or viewDidLoad or any view delegates
_firstButtonPressed = NO;
_secondButtonPressed = NO;
//Connect following IBActions to Touch Down events of both buttons
- (IBAction)firstButtonPressed:(UIButton *)sender {
_firstButtonPressed = YES;
[self checkForButtonPress];
}
- (IBAction)secondButtonPressed:(UIButton *)sender {
_ secondButtonPressed = YES;
[self checkForButtonPress];
}
- (void)checkForButtonPress {
if (_firstButtonPressed && _secondButtonPressed) {
NSlog(#"Two buttons pressed");
}
}
You can do it using two boolean flags like this:
#property(nonatomic, assign) BOOL firstButtonPressed;
#property(nonatomic, assign) BOOL secondButtonPressed;
- (void)firstButtonTouchDown:(id)sender
{
_firstButtonPressed = YES;
if (_secondButtonPressed)
{
// Both buttons pressed.
}
}
- (void)secondButtonTouchDown:(id)sender
{
_secondButtonPressed = YES;
if (_firstButtonPressed)
{
// Both buttons pressed.
}
}
- (void)firstButtonTouchCancelled:(id)sender
{
_firstButtonPressed = NO;
}
- (void)secondButtonTouchCancelled:(id)sender
{
_secondButtonPressed = NO;
}
Alternatively, you can also start a timer when a touch is down and check if the second touch happens during time interval you specify.

Xcode - Saving button click sequence and replaying the actions in saved order?

I am trying to save the order in which the buttons are pressed, and then replay that order and run the actions assigned to the buttons in the order they were originally pressed? Can anyone please help me with this?
Each UIControl element has a tag which you can use to be able to identify between the various buttons that are going to be tapped. As each button is tapped, the method (selector) associated with that button will be called (you can even have a single selector be called for all the buttons and differentiate between them via their tags).
As each button is tapped, keep track of which button is tapped by adding the tag of each button to a queue (or in Objective-C: NSMutableArray). Then to replay the actions you can merely read the tag values from the queue and call the corresponding selector.
An example to illustrate:
#property (nonatomic, strong) NSMutableArray *taskArray;
// in your init or viewDidLoad:
_taskArray = [NSMutableArray new];
// in the selector that is called by *all* buttons
-(IBAction) buttonTapped:(id)sender {
[_taskArray addObject:[NSNumber numberWithInteger:sender.tag]];
[self executeActionWithTag:sender.tag];
}
-(void) executeActionWithTag:(NSUInteger)tag {
if(tag == 1) {
// perform specific action 1 ...
} else if (tag == 2) {
// perform specific action 2 ...
}
// ...
}
-(void) replayButtonActions {
for (NSNumber *tag in _taskArray) {
[self executeActionWithTag:[tag integerValue]];
}
}

How to implement two IBActions in UIButton without overlap?

I drag 2 IBActions from a UIButton, one with touchDown event and second with drag Inside.
- (IBAction)clickButton:(UIButton *)sender {
NSLog(#"Click Button");
}
- (IBAction)dragInsideButton:(UIButton *)sender {
NSLog(#"Drag Button");
}
But when I drag inside, the touchDown action also gets fired.
How to disable touchDown event when dragInside.
Thanks!
i have solved a problem like this with using drag Events
add events to your button in .xib file or programatically.
programmatically is:
[mybut addTarget:self action:#selector(dragBegan:withEvent: )
forControlEvents: UIControlEventTouchDown];
[mybut addTarget:self action:#selector(dragMoving:withEvent: )
forControlEvents: UIControlEventTouchDragInside];
[mybut addTarget:self action:#selector(dragEnded:withEvent: )
forControlEvents: UIControlEventTouchUpInside |
UIControlEventTouchUpOutside];
then defininitons of events are:
- (void) dragBegan: (UIButton *) c withEvent:ev
{
NSLog(#"dragBegan......");
count=NO;//bool Value to decide the Down Event
c.tag=0;
[self performSelector:#selector(DownSelected:) withObject:mybut afterDelay:0.1];
//user must begin dragging in 0.1 second else touchDownEvent happens
}
- (void) dragMoving: (UIButton *) c withEvent:ev
{
NSLog(#"dragMoving..............");
c.tag++;
}
- (void) dragEnded: (UIButton *) c withEvent:ev
{
NSLog(#"dragEnded..............");
if (c.tag>0 && !count)
{
NSLog(#"make drag events");
}
}
-(void)DownSelected:(UIButton *)c
{
if (c.tag==0) {
NSLog(#"DownEvent");
count=YES;//count made Yes To interrupt drag event
}
}
This method is tested, and should do what I think you're trying to do. You can change the delay in the timer to get the effect you want. I had to connect 3 actions to the button to make this work -- the third action is a touchUp that resets the system to the starting condition.
#interface LastViewController ()
#property (nonatomic) BOOL touchedDown;
#property (strong,nonatomic) NSTimer *downTimer;
#end
#implementation LastViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.touchedDown = NO;
}
-(IBAction)clickDown:(id)sender {
self.downTimer = [NSTimer scheduledTimerWithTimeInterval:.3 target:self selector:#selector(buttonAction:) userInfo:nil repeats:NO];
}
-(IBAction)dragInside:(id)sender {
[self.downTimer invalidate];
[self buttonAction:self];
}
-(void) buttonAction:(id) sender {
if ([sender isKindOfClass:[NSTimer class]]) {
self.touchedDown = YES;
NSLog(#"click down");
}else{
if (! self.touchedDown) {
NSLog(#"Drag");
}
}
}
-(IBAction)touchUpAction:(id)sender {
self.touchedDown = NO;
}
Try this way:
isClicked is property of type BOOL. Set to YES.
- (IBAction)clickButton:(UIButton *)sender { //touch down
if(isClick==YES){
NSLog(#"Click Button");
//do all your stuffs here
}
isClick=YES;
}
- (IBAction)dragInsideButton:(UIButton *)sender {
NSLog(#"Drag Button");
isClick=NO;
}
On top of this you can also implement removeTarget:Action:
Which ever method gets called first set isClick=NO, I expect clickButton is called first in button action.
I got from Two action methods for an UIButton; next track and seek forward:
Change it As per your requirement.
Personally I'd just track the button's state with an integer on your view controller or within a button subclass. If you track what the button is doing you can control what each of the actions do. In your .h file put in some stuff like this:
enum {
MyButtonScanning,
MyButtonStalling,
MyButtonIdle
};
#interface YourClass : UIViewController {
NSInteger buttonModeAt;
}
#property (nonatomic) NSInteger buttonModeAt;
-(IBAction)buttonPushedDown:(id)sender;
-(void)tryScanForward:(id)sender;
-(IBAction)buttonReleasedOutside:(id)sender;
-(IBAction)buttonReleasedInside:(id)sender;
#end
And then in your .m file throw in some of this stuff:
#implementation YourClass
///in your .m file
#synthesize buttonModeAt;
///link this to your button's touch down
-(IBAction)buttonPushedDown:(id)sender {
buttonModeAt = MyButtonStalling;
[self performSelector:#selector(tryScanForward:) withObject:nil afterDelay:1.0];
}
-(void)tryScanForward:(id)sender {
if (buttonModeAt == MyButtonStalling) {
///the button was not released so let's start scanning
buttonModeAt = MyButtonScanning;
////your actual scanning code or a call to it can go here
[self startScanForward];
}
}
////you will link this to the button's touch up outside
-(IBAction)buttonReleasedOutside:(id)sender {
if (buttonModeAt == MyButtonScanning) {
///they released the button and stopped scanning forward
[self stopScanForward];
} else if (buttonModeAt == MyButtonStalling) {
///they released the button before the delay period finished
///but it was outside, so we do nothing
}
self.buttonModeAt = MyButtonIdle;
}
////you will link this to the button's touch up inside
-(IBAction)buttonReleasedInside:(id)sender {
if (buttonModeAt == MyButtonScanning) {
///they released the button and stopped scanning forward
[self stopScanForward];
} else if (buttonModeAt == MyButtonStalling) {
///they released the button before the delay period finished so we skip forward
[self skipForward];
}
self.buttonModeAt = MyButtonIdle;
}
After that just link the button's actions to what I've noted in the comments before the IBactions. I haven't tested this but it should work.
i'm not sure it will work but you can try
- (IBAction)dragInsideButton:(UIButton *)sender {
[sender removeTarget:self forSelector:#selector(clickButton:) forControlEvent:UIControlEventTouchDown];
}

How can I disable multiple buttons?

I have 2 buttons on my view and i want to disable the first button when i click on an other button and disable the second when I click again on the button.
I have tried with this code
if (button1.enable = NO) {
button2.enable = NO;
}
So I have in a NavigationBar a "+" button and 5 disable buttons in my view.
When I push the "+" button I want to enable the first button and when I push again that enable the second…
Thanks
if (button1.enabled == YES)
{
button1.enabled = NO;
button2.enabled = YES;
}
else (button2.enabled == YES)
{
button2.enabled = NO;
button1.enabled = YES;
}
Is that what your looking for? It would be an IBAction for the other button.
button1.enable = YES should be button1.enable == YES
a better readable form: [button1 isEnabled]
You're saying
if (button1.enabled = NO) {
when you probably mean
if (button1.enabled == NO) {
= is the assignment operator, and == is the boolean equality operator. What you're doing at the moment is assigning YES to button1.enable, which obviously enables button1. Then, because button.enable is true, control enters the if's clause and enables button2.
EDIT: To answer your new question ("When I push the "+" button I want to enable the first button and when I push again that enable the second..."), let's say that you initialise the button states somewhere. In your #interface add an instance variable
NSArray *buttons;
so your interface declaration looks something like
#interface YourViewController: UIViewController {
IBOutlet UIButton *button1;
IBOutlet UIButton *button2;
IBOutlet UIButton *button3;
IBOutlet UIButton *button4;
IBOutlet UIButton *button5;
NSArray *buttons;
}
and then initialise buttons like so:
-(void)viewDidLoad {
[super viewDidLoad];
buttons = [NSArray arrayWithObjects: button1, button2, button3, button4, button5, nil];
[buttons retain];
for (UIButton *each in buttons) {
each.enabled = NO;
}
-(void)viewDidUnload {
[buttons release];
[super viewDidUnload];
}
Let's say you hook up the + button's Touch Up Inside event handler to plusPressed:. Then you'd have
-(IBAction)plusPressed: (id) button {
for (UIButton *each in buttons) {
if (!each.enabled) {
each.enabled = YES;
break;
}
}
}
Each time plusPressed: is called, the next button in the array will be enabled. (I'm writing the above away from a compiler; there may be syntax errors.)
You could also make buttons a property. I didn't, because other classes have no business accessing buttons.

Resources