Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
I'm trying to create an app where one of the features is the ability to time multiple things at once. I have two UILabels and two UIButtons on a view, and have code to make one label start timing when its respective button is pressed. As you can see from my code, I have two of everything:
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *display;
- (IBAction)startPressed:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *display2;
- (IBAction)startPressed2:(id)sender;
#end
#implementation ViewController {
bool start;
bool start2;
NSTimeInterval time;
NSTimeInterval time2;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.display.text = #"0:00";
self.display2.text = #"0:00";
start = false;
start2 = false;
}
-(void) update1 {
if (start == false) {
return;
}
NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval elapsedTime = currentTime - time;
int minutes = (int)(elapsedTime / 60.0);
int seconds = (int)(elapsedTime = elapsedTime - (minutes * 60));
self.display.text = [NSString stringWithFormat:#"%u:%02u", minutes, seconds];
[self performSelector:#selector(update1) withObject:self afterDelay:0.1];
}
-(void) update2 {
if (start2 == false) {
return;
}
NSTimeInterval currentTime2 = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval elapsedTime2 = currentTime2 - time2;
int minutes2 = (int)(elapsedTime2 / 60.0);
int seconds2 = (int)(elapsedTime2 = elapsedTime2 - (minutes2 * 60));
self.display2.text = [NSString stringWithFormat:#"%u:%02u", minutes2, seconds2];
[self performSelector:#selector(update2) withObject:self afterDelay:0.1];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)startPressed:(id)sender {
if (start == false) {
start = true;
time = [NSDate timeIntervalSinceReferenceDate];
[sender setTitle:#"Stop" forState:UIControlStateNormal];
[self update1];
}else {
start = false;
[sender setTitle:#"Start" forState:UIControlStateNormal];
}
}
- (IBAction)startPressed2:(id)sender {
if (start2 == false) {
start2 = true;
time2 = [NSDate timeIntervalSinceReferenceDate];
[sender setTitle:#"Stop" forState:UIControlStateNormal];
[self update2];
}else {
start2 = false;
[sender setTitle:#"Start" forState:UIControlStateNormal];
}
}
#end
However, when I run the app, no matter which button I press, the first Label starts counting. Please help, I can't get both timers running at the same time.
Thanks!
Note: The question code has been substantially updated since this answer was first written.
Both update and update2 call the same method:
[self performSelector:#selector(update) withObject:self afterDelay:0.1];
update2 should call:
[self performSelector:#selector(update2) withObject:self afterDelay:0.1];
The best way to avoid errors like these is through good naming, update1 and update2 would tend to avoid this error. Also there is considerable code that could be factored into common menthols ensuring future changes that are common to both timers get made with one code change.
Objective-C uses BOOL with YES and NO as boolean constants not bool with true and false. It generally makes it easier to use the conventions of the system.
Naming is so important as is eliminating duplicate code. Here is a demonstration implementation:
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *display1;
#property (weak, nonatomic) IBOutlet UILabel *display2;
#property (nonatomic) NSTimeInterval time1;
#property (nonatomic) NSTimeInterval time2;
#property (nonatomic, getter = isRunning1) BOOL running1;
#property (nonatomic, getter = isRunning2) BOOL running2;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.display1.text = [self elapsedTimeInterval:0];
self.display2.text = [self elapsedTimeInterval:0];
self.running1 = NO;
self.running2 = NO;
}
-(void) update {
if (self.isRunning1) {
self.display1.text = [self elapsedTimeInterval:self.time1];
}
if (self.isRunning2) {
self.display2.text = [self elapsedTimeInterval:self.time2];
}
if (self.isRunning1 || self.isRunning2) {
[self performSelector:#selector(update) withObject:self afterDelay:0.1];
}
}
- (NSString *)elapsedTimeInterval:(NSTimeInterval)timeInterval {
NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
if (timeInterval == 0) {
timeInterval = currentTime;
}
int elapsedTime = currentTime - timeInterval;
int minutes = elapsedTime / 60;
int seconds = elapsedTime % 60;
NSString *displayText = [NSString stringWithFormat:#"%u:%02u", minutes, seconds];
return displayText;
}
- (IBAction)startPressed1:(UIButton *)button {
self.running1 = !self.isRunning1;
if (self.running1 == YES) {
self.time1 = [NSDate timeIntervalSinceReferenceDate];
[self update];
}
[self setTitleOfButton:button state:self.isRunning1];
}
- (IBAction)startPressed2:(UIButton *)button {
self.running2 = !self.isRunning2;
if (self.running2 == YES) {
self.time2 = [NSDate timeIntervalSinceReferenceDate];
[self update];
}
[self setTitleOfButton:button state:self.isRunning2];
}
- (void)setTitleOfButton:(UIButton *)button state:(BOOL)state {
NSString *title = state ? #"Start" : #"Stop";
[button setTitle:title forState:UIControlStateNormal];
}
#end
Note, there are a number of poor practices in this code which I purposely did not address.
Check your xib file, more specifically, check that the connections for display1 and display2 are correct.
It is possible that when copy/pasting your label you mis connected the labels.
Related
I recently downloaded code for creating a calendar in xCode using objective-c. The code works for the most part, it highlights the days a user selects, but it is supposed to log out the start and end dates the user has selected. A delegate is used for this between my view controller and uiview (calendar view), but when my delegate is set in viewDidLoad, I noticed that it is always set to null. I am new to app making so would appreciate any help.
viewcontroller.m:
#import "CalendarViewController.h"
#import "DSLCalendarView.h"
#interface CalendarViewController ()<DSLCalendarViewDelegate>
#property (nonatomic, weak) IBOutlet DSLCalendarView *calendarView;
#property (weak, nonatomic) IBOutlet UIButton *selectDatesButton;
#end
#implementation CalendarViewController
- (void)viewDidLoad;
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.calendarView.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#pragma mark - DSLCalendarViewDelegate methods
- (void)calendarView:(DSLCalendarView *)calendarView didSelectRange:(DSLCalendarRange *)range;
{
if (range != nil) {
NSLog( #"Selected %ld/%ld - %ld/%ld", (long)range.startDay.day, (long)range.startDay.month, (long)range.endDay.day, (long)range.endDay.month);
}
else {
NSLog( #"No selection" );
}
}
calendarView.m (not all code, just methods using the delegate)
#import "DSLCalendarDayCalloutView.h"
#import "DSLCalendarDayView.h"
#import "DSLCalendarMonthSelectorView.h"
#import "DSLCalendarMonthView.h"
#import "DSLCalendarView.h"
#import "DSLCalendarDayView.h"
#interface DSLCalendarView ()
#property (nonatomic, strong) DSLCalendarDayCalloutView *dayCalloutView;
#property (nonatomic, copy) NSDateComponents *draggingFixedDay;
#property (nonatomic, copy) NSDateComponents *draggingStartDay;
#property (nonatomic, assign) BOOL draggedOffStartDay;
#property (nonatomic, strong) NSMutableDictionary *monthViews;
#property (nonatomic, strong) UIView *monthContainerView;
#property (nonatomic, strong) UIView *monthContainerViewContentView;
#property (nonatomic, strong) DSLCalendarMonthSelectorView *monthSelectorView;
#end
#implementation DSLCalendarView {
CGFloat _dayViewHeight;
NSDateComponents *_visibleMonth;
}
- (void)positionViewsForMonth:(NSDateComponents*)month fromMonth:(NSDateComponents*)fromMonth animated:(BOOL)animated;
{
fromMonth = [fromMonth copy];
month = [month copy];
CGFloat nextVerticalPosition = 0;
CGFloat startingVerticalPostion = 0;
CGFloat restingVerticalPosition = 0;
CGFloat restingHeight = 0;
NSComparisonResult monthComparisonResult = [month.date compare:fromMonth.date];
NSTimeInterval animationDuration = (monthComparisonResult == NSOrderedSame || !animated) ? 0.0 : 0.5;
NSMutableArray *activeMonthViews = [[NSMutableArray alloc] init];
// Create and position the month views for the target month and those around it
for (NSInteger monthOffset = -2; monthOffset <= 2; monthOffset += 1)
{
NSDateComponents *offsetMonth = [month copy];
offsetMonth.month = offsetMonth.month + monthOffset;
offsetMonth = [offsetMonth.calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitCalendar fromDate:offsetMonth.date];
// Check if this month should overlap the previous month
if (![self monthStartsOnFirstDayOfWeek:offsetMonth]) {
nextVerticalPosition -= _dayViewHeight;
}
// Create and position the month view
DSLCalendarMonthView *monthView = [self cachedOrCreatedMonthViewForMonth:offsetMonth];
[activeMonthViews addObject:monthView];
[monthView.superview bringSubviewToFront:monthView];
CGRect frame = monthView.frame;
frame.origin.y = nextVerticalPosition;
nextVerticalPosition += frame.size.height;
monthView.frame = frame;
// Check if this view is where we should animate to or from
if (monthOffset == 0) {
// This is the target month so we can use it to determine where to scroll to
restingVerticalPosition = monthView.frame.origin.y;
restingHeight += monthView.bounds.size.height;
}
else if (monthOffset == 1 && monthComparisonResult == NSOrderedAscending) {
// This is the month we're scrolling back from
startingVerticalPostion = monthView.frame.origin.y;
if ([self monthStartsOnFirstDayOfWeek:offsetMonth]) {
startingVerticalPostion -= _dayViewHeight;
}
}
else if (monthOffset == -1 && monthComparisonResult == NSOrderedDescending) {
// This is the month we're scrolling forward from
startingVerticalPostion = monthView.frame.origin.y;
if ([self monthStartsOnFirstDayOfWeek:offsetMonth]) {
startingVerticalPostion -= _dayViewHeight;
}
}
// Check if the active or following month start on the first day of the week
if (monthOffset == 0 && [self monthStartsOnFirstDayOfWeek:offsetMonth]) {
// If the active month starts on a monday, add a day view height to the resting height and move the resting position up so the user can drag into that previous month
restingVerticalPosition -= _dayViewHeight;
restingHeight += _dayViewHeight;
}
else if (monthOffset == 1 && [self monthStartsOnFirstDayOfWeek:offsetMonth]) {
// If the month after the target month starts on a monday, add a day view height to the resting height so the user can drag into that month
restingHeight += _dayViewHeight;
}
}
// Size the month container to fit all the month views
CGRect frame = self.monthContainerViewContentView.frame;
frame.size.height = CGRectGetMaxY([[activeMonthViews lastObject] frame]);
self.monthContainerViewContentView.frame = frame;
// Remove any old month views we don't need anymore
NSArray *monthViewKeyes = self.monthViews.allKeys;
for (NSString *key in monthViewKeyes) {
UIView *monthView = [self.monthViews objectForKey:key];
if (![activeMonthViews containsObject:monthView]) {
[monthView removeFromSuperview];
[self.monthViews removeObjectForKey:key];
}
}
// Position the content view to show where we're animating from
if (monthComparisonResult != NSOrderedSame) {
CGRect frame = self.monthContainerViewContentView.frame;
frame.origin.y = -startingVerticalPostion;
self.monthContainerViewContentView.frame = frame;
}
self.userInteractionEnabled = NO;
[UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
for (NSInteger index = 0; index < activeMonthViews.count; index++) {
DSLCalendarMonthView *monthView = [activeMonthViews objectAtIndex:index];
for (DSLCalendarDayView *dayView in monthView.dayViews) {
// Use a transition so it fades between states nicely
[UIView transitionWithView:dayView duration:animationDuration options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
dayView.inCurrentMonth = (index == 2);
} completion:NULL];
}
}
// Animate the content view to show the target month
CGRect frame = self.monthContainerViewContentView.frame;
frame.origin.y = -restingVerticalPosition;
self.monthContainerViewContentView.frame = frame;
// Resize the container view to show the height of the target month
frame = self.monthContainerView.frame;
frame.size.height = restingHeight;
self.monthContainerView.frame = frame;
// Resize the our frame to show the height of the target month
frame = self.frame;
frame.size.height = CGRectGetMaxY(self.monthContainerView.frame);
self.frame = frame;
// Tell the delegate method that we're about to animate to a new month
if (monthComparisonResult != NSOrderedSame && [self.delegate respondsToSelector:#selector(calendarView:willChangeToVisibleMonth:duration:)]) {
[self.delegate calendarView:self willChangeToVisibleMonth:[month copy] duration:animationDuration];
}
} completion:^(BOOL finished) {
self.userInteractionEnabled = YES;
if (finished) {
// Tell the delegate method that we've animated to a new month
if (monthComparisonResult != NSOrderedSame && [self.delegate respondsToSelector:#selector(calendarView:didChangeToVisibleMonth:)]) {
[self.delegate calendarView:self didChangeToVisibleMonth:[month copy]];
}
}
}];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
{
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
self.draggingStartDay = touchedView.day;
self.draggingFixedDay = touchedView.day;
self.draggedOffStartDay = NO;
DSLCalendarRange *newRange = self.selectedRange;
if (self.selectedRange == nil) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
else if (![self.selectedRange.startDay isEqual:touchedView.day] && ![self.selectedRange.endDay isEqual:touchedView.day]) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
else if ([self.selectedRange.startDay isEqual:touchedView.day]) {
self.draggingFixedDay = self.selectedRange.endDay;
}
else {
self.draggingFixedDay = self.selectedRange.startDay;
}
if ([self.delegate respondsToSelector:#selector(calendarView:didDragToDay:selectingRange:)]) {
newRange = [self.delegate calendarView:self didDragToDay:touchedView.day selectingRange:newRange];
}
self.selectedRange = newRange;
[self positionCalloutViewForDayView:touchedView];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
{
if (self.draggingStartDay == nil) {
return;
}
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
DSLCalendarRange *newRange;
if ([touchedView.day.date compare:self.draggingFixedDay.date] == NSOrderedAscending) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:self.draggingFixedDay];
}
else {
newRange = [[DSLCalendarRange alloc] initWithStartDay:self.draggingFixedDay endDay:touchedView.day];
}
if ([self.delegate respondsToSelector:#selector(calendarView:didDragToDay:selectingRange:)]) {
newRange = [self.delegate calendarView:self didDragToDay:touchedView.day selectingRange:newRange];
}
self.selectedRange = newRange;
if (!self.draggedOffStartDay) {
if (![self.draggingStartDay isEqual:touchedView.day]) {
self.draggedOffStartDay = YES;
}
}
[self positionCalloutViewForDayView:touchedView];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
{
if (self.draggingStartDay == nil)
{
return;
}
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
if (!self.draggedOffStartDay && [self.draggingStartDay isEqual:touchedView.day]) {
self.selectedRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
self.draggingStartDay = nil;
// Check if the user has dragged to a day in an adjacent month
if (touchedView.day.year != _visibleMonth.year || touchedView.day.month != _visibleMonth.month)
{
// Ask the delegate if it's OK to animate to the adjacent month
BOOL animateToAdjacentMonth = YES;
if ([self.delegate respondsToSelector:#selector(calendarView:shouldAnimateDragToMonth:)])
{
animateToAdjacentMonth = [self.delegate calendarView:self shouldAnimateDragToMonth:[touchedView.dayAsDate dslCalendarView_monthWithCalendar:_visibleMonth.calendar]];
}
if (animateToAdjacentMonth)
{
if ([touchedView.dayAsDate compare:_visibleMonth.date] == NSOrderedAscending)
{
[self didTapMonthBack:nil];
}
else
{
[self didTapMonthForward:nil];
}
}
}
if ([self.delegate respondsToSelector:#selector(calendarView:didSelectRange:)])
{
[self.delegate calendarView:self didSelectRange:self.selectedRange];
}
}
calendarView.h
#import "DSLCalendarRange.h"
#import "NSDate+DSLCalendarView.h"
#protocol DSLCalendarViewDelegate;
#interface DSLCalendarView : UIView
#property (nonatomic, weak) id<DSLCalendarViewDelegate>delegate;
#property (nonatomic, copy) NSDateComponents *visibleMonth;
#property (nonatomic, strong) DSLCalendarRange *selectedRange;
#property (nonatomic, assign) BOOL showDayCalloutView;
#property (nonatomic, assign) BOOL daysSelected;
+ (Class)monthSelectorViewClass;
+ (Class)monthViewClass;
+ (Class)dayViewClass;
- (void)setVisibleMonth:(NSDateComponents *)visibleMonth animated:(BOOL)animated;
- (void)commonInit;
#end
#protocol DSLCalendarViewDelegate <NSObject>
#optional
- (void)calendarView:(DSLCalendarView*)calendarView didSelectRange:(DSLCalendarRange*)range;
- (void)calendarView:(DSLCalendarView *)calendarView willChangeToVisibleMonth:(NSDateComponents*)month duration:(NSTimeInterval)duration;
- (void)calendarView:(DSLCalendarView *)calendarView didChangeToVisibleMonth:(NSDateComponents*)month;
- (DSLCalendarRange*)calendarView:(DSLCalendarView*)calendarView didDragToDay:(NSDateComponents*)day selectingRange:(DSLCalendarRange*)range;
- (BOOL)calendarView:(DSLCalendarView *)calendarView shouldAnimateDragToMonth:(NSDateComponents*)month;
If the user keeps clicking on button1 one or two , progress2.progress keeps increasing/decreasing on each click and progress1.progress keeps the same value until the user stops clicking. And in case he will surely lose , if he also keeps clicking nothing happens until he stops clicking. I don't want it to be that way since I want to hide/disable the buttons as soon as it's confirmed that he's losing to fix this issue. Any way to fix that?
Here is my .m :
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (BOOL)prefersStatusBarHidden { return YES; }
- (void)viewDidLoad
{
progress1.progress=arc4random() % 11 * 0.1;
count1=0;
count2=0;
label1.hidden = NO;
gameOver.hidden = YES;
score=0;
[super viewDidLoad];
;
}
// Do any additional setup after loading the view, typically from a nib.
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)regulator{
if(timer1)
{
[timer1 invalidate];
timer1 = nil;
}
if(timer4)
{
[timer4 invalidate];
timer4 = nil;
}
timer4 =[NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:#selector(conditioner) userInfo:nil repeats:YES];
;}
-(void)conditioner {
if (fabs(progress2.progress-progress1.progress)<=0.25 )
{
score=score+1;
scorenumber.text= [NSString stringWithFormat:#"%i",score];
[self newGame];
;
} else{
stop1=YES;
stop2=YES;
gameOver.hidden=NO;
stick.hidden=YES;
bg.hidden=YES;
progress1.hidden=YES;
progress2.hidden=YES;
supply.hidden=YES;
demand.hidden=YES;
}}
-(void)newGame{
progress1.progress=arc4random() % 11 * 0.1;}
- (IBAction)start:(UIButton *)sender {
progress2.progress=arc4random() % 11 * 0.1;
if(timer4)
{
[timer4 invalidate];
timer4 = nil;
timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(regulator) userInfo:nil repeats:YES];
[self regulator];
stop1=NO;
stop2=NO;
label1.hidden=YES;
UIButton *button1 = (UIButton *)sender;
button1.enabled = NO;
UIButton *button2 = (UIButton *)sender;
button2.enabled = NO;
}
- (IBAction)button1:(UIButton *)sender {
if(stop1==YES){button12.hidden = TRUE;}
progress2.progress=progress2.progress-0.05;
;
[self regulator];
count2=0;
count1 = count1 +1;
}
- (IBAction)button2:(UIButton *)sender {
[self regulator];
progress2.progress=progress2.progress+0.05;
if(stop2==YES){button22.hidden = TRUE;}
count1 =0;
count2 = count2+1;
}
#end
and my .h:
#import <UIKit/UIKit.h>
int count1;
int count2;
int score;
void *regulator;
void *newGame;
void *conditioner;
BOOL stop1;
BOOL stop2;
void *firstLaunch;
#interface ViewController : UIViewController{
IBOutlet UILabel *scorenumber;
IBOutlet UIImageView *stick;
IBOutlet UILabel *label1;
IBOutlet UIImageView *bg;
IBOutlet UILabel *supply;
IBOutlet UILabel *demand;
IBOutlet UILabel *gameOver;
IBOutlet UIProgressView *progress1;
IBOutlet UIProgressView *progress2;
IBOutlet UIButton *button12;
IBOutlet UIButton *button22;
NSTimer *timer1;
NSTimer *timer2;
NSTimer *timer3;
NSTimer *timer4;
}
- (IBAction)button1:(UIButton *)sender;
- (IBAction)button2:(UIButton *)sender;
#end
Thanks a lot for any help or information. I edited my question with the full code to give further explanation about the issue I'm facing. Regards.
This is actually a coding issue. MVC basics.
I believe you miss some understanding of things. So I'll explain:
IBAction - It's an action sent from the view to the controller.
IBOutlet - Meant for the controller to control the view.
On your code you are getting the sender (which should be read only when coding right) and you are trying to set it up. I assume you need to define a new IBOutlet to represent the button then connect it on your storyboard and then inside this function to make it enable/disabled.
Also a good practice would be to use "TRUE" and "FALSE" and not "YES/NO".
Hope this helps.
There are couple of ways you can approach this to make sure when user touches the button then it doesn't do anything.
It wasn't clear from your question so I assume on touch down of button you call (IBAction)button1. So Try
- (IBAction)button1:(UIButton *)sender
{
if(stop1==YES)
{
//game has stopped so don't do anything
}
else
{
progress2.progress=progress2.progress-0.05;
[self regulator];
}
}
If you want to hide it so that the user can't do anything try this
- (IBAction)button1:(UIButton *)sender
{
if(stop1==YES)
{
//game has stopped so hide this button
//assuming you have connected an IBOutlet to your button1
button1.hidden = YES;
}
else
{
button1.hidden = NO;
progress2.progress=progress2.progress-0.05;
[self regulator];
}
}
So I have a method called: - (void)spawnNode and it basically regulates the spawning of my Node. My problem is that I want to make it so that when the BOOL runGame equals YES the spawnNode method will begin and keep spawning my node after intervals of time. Which I don't know how to do.
The BOOL runGame is initially set to NO.
Below is the full spawnNode method:
- (void)spawnNode{
//Spawn Interval Duration
spawnIntervalDuration = 0.75;
//SKAction spawnInterval
spawnInterval = [SKAction waitForDuration:spawnIntervalDuration];
if(runGame){
[self runAction:spawnInterval completion:^{
[self generateNode];
}];
}
}
So, How do I make this method always run whilst runGame is equal to YES?
You can create a property:
#property (nonatomic, assign) BOOL runGame;
Also, a NSTimer property:
#property (nonatomic) NSTimer *timer;
Then, you need to override the setter for it:
- (void)setRunGame:(BOOL)newValue
{
if (newValue == YES) {
[self spawnNode];
CGFloat spawnInterval = 0.75;
self.timer = [NSTimer scheduledTimerWithTimeInterval:spawnInterval
target:self
selector:#selector(spawnNode)
userInfo:kNilOptions
repeats:YES];
} else {
[self.timer invalidate];
self.timer = nil;
}
}
I am trying to create a long press gesture that presents a second view controller, once the press has been held for 3 seconds. However, I only want the second view controller presented if the device is in a certain accelerometer orientation for the ENTIRE 3 seconds. That is, if the gesture is not held long enough or the device is tilted too much, the gesture is dismissed and the user must try again.
// In FirstViewController.h
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
#interface FirstViewController : UIViewController
#property (nonatomic, strong) CMMotionManager *motionManager;
#end
// In FirstViewController.m
#import "FirstViewController"
#import "SecondViewController"
#implementation motionManager;
- (void) viewDidLoad
{
[super viewDidLoad];
self.motionManager = [[CMMotionManager alloc]init];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPress:)];
longPress.minimumPressDuration = 3.0;
[self.view addGestureRecognizer:longPress];
}
- (void) handleLongPress: (UILongPressGestureRecognizer *)sender
{
// Not sure what to do here
}
I have previously tried chunks of code in the last method but it looks obnoxious and just is not correct. Instead, I have listed several lines of code below that I know work individually, but I need assistance in making them all work together.
// Accelerometer
if ([self.motionManager isAccelerometerAvailable])
{
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData,NSError *error)
{
if (ABS(accelerometerData.acceleration.x) < 0.3 && ABS(accelerometerData.acceleration.y) < 0.30 && ABS(accelerometerData.acceleration.z) > 0.70) // Phone is flat and screen faces up
{
NSLog(#"Correct Orientation!!!");
[self.motionManager stopAccelerometerUpdates];
}
else
{
NSLog(#"Incorrect orientation!!!");
[self.motionManager stopAccelerometerUpdates];
}];
}
else
{
NSLog(#"Accelerometer is not available.");
}
// Go to second view controller
if (sender.state == UIGestureRecognizerStateBegan)
{
SecondViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self presentViewController:svc animated:YES completion:nil];
}
Any ideas? Or even a more general way to cancel the gesture unless a condition is met would be very helpful.
I hope the code is verbose enough for you to read, it's pretty self explanatory - I've stuck with your accelerometer code, and used the same variable names.
#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>
#interface ViewController () {
NSOperationQueue *motionQueue;
NSTimer *touchTimer;
NSTimeInterval initialTimeStamp;
BOOL touchValid;
float timerPollInSeconds;
float longPressTimeRequired;
}
#property (strong, nonatomic) NSTimer *touchTimer;
#property (assign, nonatomic) NSTimeInterval initialTimeStamp;
#property (assign, nonatomic) BOOL touchValid;
#property (assign, nonatomic) float timerPollInSeconds;
#property (assign, nonatomic) float longPressTimeRequired;
#property (strong, nonatomic) CMMotionManager *motionManager;
#property (strong, nonatomic) NSOperationQueue *motionQueue;
#end
#implementation ViewController
#synthesize touchTimer = _touchTimer, initialTimeStamp, touchValid, motionQueue = _motionQueue;
#synthesize timerPollInSeconds, longPressTimeRequired, motionManager = _motionManager;
- (void)viewDidLoad
{
self.timerPollInSeconds = 0.25f;
self.longPressTimeRequired = 3.0f;
self.touchTimer = nil;
self.touchValid = NO;
self.initialTimeStamp = NSTimeIntervalSince1970;
self.motionManager = [[CMMotionManager alloc] init];
self.motionQueue = [[NSOperationQueue alloc] init];
[_motionQueue setName:#"MotionQueue"];
[_motionQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
[super viewDidLoad];
self.view.multipleTouchEnabled = NO;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Operations
-(void) startLongPressMonitorWithTimeStamp:(NSTimeInterval) timeStamp {
NSLog(#"Starting monitoring - %g", timeStamp);
if( self.touchTimer ) {
if( [_touchTimer isValid] ) {
[_touchTimer invalidate];
}
}
self.touchTimer = [NSTimer timerWithTimeInterval:self.timerPollInSeconds target:self selector:#selector(timerPolled:) userInfo:nil repeats:YES];
if( [_motionManager isAccelerometerAvailable] ) {
NSLog(#"Accelerometer Available");
if( ![_motionManager isAccelerometerActive] ) {
NSLog(#"Starting Accelerometer");
[_motionManager startAccelerometerUpdatesToQueue:self.motionQueue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
if (ABS(accelerometerData.acceleration.x) < 0.3 && ABS(accelerometerData.acceleration.y) < 0.30 && ABS(accelerometerData.acceleration.z) > 0.70) // Phone is flat and screen faces up
{
dispatch_sync(dispatch_get_main_queue(), ^{
self.touchValid = YES;
});
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
self.touchValid = NO;
[self stopLongPressMonitoring:YES];
});
};
}];
}
else {
NSLog(#"Accelerometer already active");
}
}
else {
NSLog(#"Accelerometer not available");
}
self.initialTimeStamp = timeStamp;
self.touchValid = YES;
[_touchTimer fire];
[[NSRunLoop mainRunLoop] addTimer:self.touchTimer forMode:NSRunLoopCommonModes];
}
-(void) stopLongPressMonitoring:(BOOL) touchSuccessful {
[_motionManager stopAccelerometerUpdates];
[_touchTimer invalidate];
self.touchValid = NO;
if( touchSuccessful ) {
NSLog(#"Yes");
}
else {
NSLog(#"No");
}
}
#pragma mark - User Interaction
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//We're using the current times, interval since the touches timestamp refers to system boot up
// it is more than feasible to use this boot up time, but for simplicity, I'm just using this
NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
[self startLongPressMonitorWithTimeStamp:timestamp];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if( self.touchValid && [NSDate timeIntervalSinceReferenceDate] - self.initialTimeStamp == self.longPressTimeRequired ) {
[self stopLongPressMonitoring:YES];
}
else {
[self stopLongPressMonitoring:NO];
}
}
#pragma mark - Timer Call back
-(void) timerPolled:(NSTimer *) timer {
NSTimeInterval firedTimeStamp = [NSDate timeIntervalSinceReferenceDate];
NSLog(#"Timer polled - %g", firedTimeStamp);
if( self.touchValid ) {
NSLog(#"Time elapsed: %d", (int)(firedTimeStamp - self.initialTimeStamp));
if( firedTimeStamp - self.initialTimeStamp >= self.longPressTimeRequired ) {
NSLog(#"Required time has elapsed");
[self stopLongPressMonitoring:YES];
}
}
else {
NSLog(#"Touch invalidated");
[self stopLongPressMonitoring:NO];
}
}
#end
You may be able to do what you want by subclassing UIGestureRecognizer to make your own gesture recognizer similar to UILongPressGestureRecognizer that listens for accelerometer data in addition to presses of at least 3 second duration.
I'd probably override the onTouchesBegan, and onTouchesEnded methods, rather than using a gesture recogniser.
I'd then create a NSTimer object, an NSTimeInterval var and a BOOL in your view controller; for the sake of it, I'll call them, touchTimer, initialTouchTimeStamp, and touchValid.
for the sake of complexity, it's an assumption that the viewControllers view is not multi-touch.
Assume the repeatTime = 0.25f;
longPressTimeRequired = 3;
The timer selector would incorporate your accelerometer method, if the data inside your accelerometer method is invalid, I'd set a touchValid to false (and invalidate the timer), otherwise I'd set it to true After checking the accelerometer, I'd check if my initialTouchTimeStamp var is longPressTimeRequired or more seconds prior to [touchTimer fireDate] - repeatTime , if it is, and the touchValid is true, then I would go to my second controller.
onTouchesBegan, I'd invalidate touchTimer and create a new one that would repeat every repearTime seconds, and lasts for y seconds. Set touchValid to NO, and set initialTouchTimeStamp to touch.timestamp.
onTouchesEnded, I'd invalidate touchTimer, and check if my initialTouchTimeStamp var is longPressTimeRequired or more seconds prior to [touchTimer fireDate] - repeatTime , if it is, and the touchValid is true, then I would go to my second controller.
there are many common elements in here, and it's probably not the most elegant way of doing things, but it should work. Hope this helps.
The Addition works properly, but the subtraction is going into negative. I tried different possible logic but could not find the appropriate. Where first two inputs along with minus button gives proper solution .This solution when used with third input for subtraction does not happen the way it should .There are 0-9 buttons to input digits. One plus button to trigger the below method
-(IBAction)adding:(id)sender;
and one minus button to trigger this.
-(IBAction)subtract:(id)sender;
an equals button for triggering
-(IBAction)equals:(id)sender;
main functionality is to be added in the subtract and the main equals method
Below is the code of the function implementations, i.e .m file.
int eq;
#implementation ViewController
#synthesize temp = _temp;
#synthesize inputField = _inputField;
#synthesize outputField = _outputField;
-(IBAction)adding:(id)sender
{
self.temp = self.input + self.output;
self.input = 0;
eq = 0;
}
-(IBAction)subtract:(id)sender
{
self.temp = self.input - self.output;
self.input = 0;
eq = 1;
}
-(IBAction)digits:(id)sender
{
UIButton *b = (UIButton *)sender;
self.input = self.input * 10 + b.tag;
[self showInput];
}
-(IBAction)clear:(id)sender
{
self.input = 0;
[self showInput];
self.output = 0;
[self showOutput];
}
-(IBAction)clearE:(id)sender
{
self.input = self.input * 0.1;
[self showInput];
}
-(IBAction)equals:(id)sender
{
if (eq == 0) {
self.output = self.temp + self.input;
self.temp = 3;
}
if (eq ==1)
{
self.output = self.temp - self.input;
self.temp = 3;
}
[self showOutput];
self.input = 0;
[self showInput];
}
-(void)showInput
{
self.inputField.text = [NSString stringWithFormat:#"%lld", self.input];
}
-(void)showOutput
{
self.outputField.text = [NSString stringWithFormat:#"%lld", self.output];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.inputField.text = [NSString stringWithFormat:#"%lld",self.input];
self.outputField.text= [NSString stringWithFormat:#"%lld", self.output];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The .h file is this :
#interface ViewController : UIViewController
#property (nonatomic,readwrite,weak) IBOutlet UILabel *inputField;
#property (assign,readwrite,) unsigned long long input;
#property(assign,readwrite) unsigned long long temp;
#property (nonatomic,readwrite,weak) IBOutlet UILabel *outputField;
#property (assign,readwrite) unsigned long long output;
-(IBAction)digits:(id)sender;
-(IBAction)adding:(id)sender;
-(IBAction)subtract:(id)sender;
-(IBAction)clear:(id)sender;
-(IBAction)clearE:(id)sender;
-(IBAction)equals:(id)sender;
#end