Recording A Users Interaction With Interface - ios

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!

Related

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 glow a UIButton randomly from a sequence of buttons continuously

I am working on a memory based matching puzzle game. For that I need to glow buttons randomly from sequence. I have been googling sample puzzles,games & code snippets since a week or so, yet I was unable to trace out an appropriate sample project or some source to get started.
I am glowing the lights(buttons) by changing its background images(imitation), I have already set the default images for buttons(coloured bulb when in stable state) in story board. I have used NSMutableArray of IBOutletCollection i.e. tapLightButtons. Here is what I tried to get the sequence of lights glowing in a random mode to get the game experience.
-(void)startGlowingLights
{
[self shuffleValuesInArray:_tapLightButtons];
[self shuffleValuesInArray:lightArray];
[self shuffleValuesInArray:_initialButtonImages];
[self performSelector:#selector(animateButtonSequence) withObject:self afterDelay:0.2];
}
- (void) animateButtonSequence
{
__block UIButton *tapLight;
[UIView animateWithDuration:0.15
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
// button flash animation
} completion:^(BOOL finished) {
if (finished) {
if (i < _tapLightButtons.count) {
i++;
[self performSelector:#selector(animateButtonSequence) withObject:self afterDelay:1.0];
tapLight = [self.tapLightButtons objectAtIndex:i-1];
UIImage *glownLight = [UIImage imageNamed:[lightArray objectAtIndex:i-1]];
[tapLight setBackgroundImage:glownLight forState:UIControlStateNormal];
[[TTLMusicPlayer sharedInstance] playGlowLightSound:[[NSBundle mainBundle] pathForResource:#"beep" ofType: #"mp3"]];
//Reset the completed glowed button to previous state(off mode)
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (i != 0 && i < _initialButtonImages.count) {
[tapLight setBackgroundImage:[self.initialButtonImages objectAtIndex:i-1] forState:UIControlStateNormal];
}
});
}
else {
i = 0;
}
NSLog(#"i value is %d",i);
}
}];
}
-(NSMutableArray *)shuffleValuesInArray:(NSMutableArray *)shuffledArray
{
for (NSInteger x=0;x<[shuffledArray count];x++)
{
NSInteger randomNumber = (arc4random() % ([shuffledArray count] - x)) + x;
[shuffledArray exchangeObjectAtIndex:x withObjectAtIndex:randomNumber];
}
return shuffledArray;
}
tapLightButtons is the array which holds puzzled buttons(lights) which glows automatically one after another in random fashion.
lightArray is an array holding all the appropriate colour images(flash to produce glowed affect)
initialButtonImages contains array of all default(initial) images of buttons(off mode lights)
Some how I could partially achieve what I wanted to, surprisingly even after shuffling all arrays, still I see variations in the order because the glowed image should be the corresponding of stable(off mode coloured light) and then after resetting, the stable image should match the one before the light was actually glowed.
Posted the question on game dev site from Stack Exchange as well!!
Game Screen for better understanding
After flashing of coloured light
How to properly glow the buttons randomly from sequence?
I would do this a different way that leads to simpler code. I created an array of 9 custom buttons in IB and added them to an outlet collection. I subclassed the buttons, and added a flashWithOnTime: method, so the button itself would take care of switching between the normal and highlighted states.
This is the method in the button subclass,
-(void)flashWithOnTime:(CGFloat)onTime {
[self setHighlighted:YES];
[self performSelector:#selector(setHighlighted:) withObject:#NO afterDelay:onTime];
}
This is the code in the view controller that starts the sequence (from a button tap). Rather than shuffling, I pick a random index out of an array, and then delete that entry so it can't be picked again. The methods offImageWithSolidColor and onImageWithSolidColor, are just methods I used to create the images.
#interface ViewController ()
#property (strong, nonatomic) IBOutletCollection(RDFlashingButton) NSArray *buttons;
#property (strong,nonatomic) NSArray *indexes;
#property (strong,nonatomic) NSMutableArray *mutableIndexes;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.indexes = #[#0,#1,#2,#3,#4,#5,#6,#7,#8];
for (RDFlashingButton *aButton in self.buttons) {
[aButton setBackgroundImage:[self offImageWithSolidColor] forState:UIControlStateNormal];;
[aButton setBackgroundImage:[self onImageWithSolidColor] forState:UIControlStateHighlighted];
}
}
-(void)flashRandomly {
if (self.mutableIndexes.count > 0) {
int index = arc4random_uniform(self.mutableIndexes.count);
int value = [self.mutableIndexes[index] intValue];
RDFlashingButton *aButton = self.buttons[value];
[aButton flashWithOnTime:0.4];
[self.mutableIndexes removeObjectAtIndex:index];
[self performSelector:#selector(flashRandomly) withObject:nil afterDelay:.4];
}
}
-(IBAction)startFlashing:(id)sender {
self.mutableIndexes = [self.indexes mutableCopy];
[self flashRandomly];
}

Check Which Order Buttons Are Pressed Objective-C

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");
}
}
}

multiple UIButtons for one action, read the current button and store it as a temp value

I'm a beginner working on an emotion annotation application. There are several buttons like "happy","angry" and etc... All the buttons will call a same action. And there is an UISlider. If you click on "happy" , then you adjust the slider to annotate how happy you are now. After that you click on button "angry", the current value of slider will be stored in an float variable relatived to the last button, like "happy". Then you adjust the same slider to annotate how "angry" you are. And the next button....
I don't have idea about how to store the slider value for the last button...
Are there any ideas?
Thank you very much!!
There are many ways to approach this. One of the most simple solutions is to tag your buttons, and then use a method to work out which button the action came from, set up a dictionary object with the slider value inside it, and then write it to a strong array accordingly.
MainViewController.h
int emotionNumber
#property (strong, nonatomic) NSMutableArray *array;
//Declare your slider and buttons
MainViewController.m
#implementation
#synthesise array;
- (void)viewDidLoad {
[happyButton setTag:0];
[angryButton setTag:1];
array = [[NSMutableArray alloc] initWithCapacity:X]; <---- number of emotions
}
- (IBAction)setValue:(id)sender {
// You will also want to keep track of emotionNumber (the int) here, and modify the
//code below to write it to the correct place in the array.
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
UIButton *button = (UIButton *)sender;
switch (button.tag) {
case 0:
[dictionary setValue:slider.value forKey:#"angry"];
[array addObject:dictionary];
break;
case 1:
[dictionary setValue:slider.value forKey:#"happy"];
[array addObject:dictionary];
break;
default:
break;
}
}

Resources