I'm a beginner to Xcode nd Objective-C! I'm making an ios app which is basically a stop watch. The idea is only to have two buttons. One to reset the stopwatch and the other to start and stop the clock. (Basically, you click the start button which then turns into a stop button which when clicked will stop the stopwatch. The problem is I have two errors which are the same and read the following: (I don't know how to fix them which is the question!)
No visible #interface fro 'UIButton' declares the selector 'setTitle:forControlState:'
This has come up twice at two points! Point one:
[button setTitle:#"Start" forControlState:UIControlStateNormal];
And point two...
[button setTitle:#"Stop" forControlState:UIControlStateNormal];
Here is my code for VeiwController.h:
//
#import <UIKit/UIKit.h>
float addingtheTime;
#interface ViewController : UIViewController
{
IBOutlet UILabel *Label;
NSTimer *Timer;
}
#end
Here is my code for ViewController.m
//
#import "ViewController.h"
#interface ViewController ()\
#end
#implementation ViewController
-(IBAction)Start:(id)start
{
UIButton *button = (UIButton *)start;
if (button.tag == 0){
button.tag = 1;
[button setTitle:#"Start" forControlState:UIControlStateNormal];
Timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(addingthetime) userInfo:nil repeats:YES];
}else {
button.tag = 0;
[button setTitle:#"Stop" forControlState:UIControlStateNormal];
//Invoke your method to stop Timer.
}
}
-(IBAction)Restart:(id)sender
{
[Timer invalidate];
addingtheTime = 0;
Label.text = [NSString stringWithFormat:#"0.00"];
}
-(void)addingthetime
{
addingtheTime = addingtheTime + 0.01;
Label.text = [NSString stringWithFormat:#"%.2f", addingtheTime];
}
- (void)viewDidLoad {
[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.
}
#end
Please note I am a beginner so please explain as much as you can and thanks you in advance for any help!!!
its forstate not forControlState
[button setTitle:#"start" forState:UIControlStateNormal];
[button setTitle:#"stop" forState:UIControlStateNormal];
Related
I'm using Objective-C to make a stopwatch. I already have a basic stop watch with 3 buttons: Start, Stop and reset. Here is my code for viewcontroller.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
-(IBAction)Start:(id)start
{
Timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(addingthetime) userInfo:nil repeats:YES];
}
-(IBAction)Stop:(id)sender
{
[Timer invalidate];
}
-(IBAction)Restart:(id)sender
{
[Timer invalidate];
addingtheTime = 0;
Label.text = [NSString stringWithFormat:#"0.00"];
}
-(void)addingthetime
{
addingtheTime = addingtheTime + 0.01;
Label.text = [NSString stringWithFormat:#"%.2f", addingtheTime];
}
#end
And here is my code for viewcontroller.h:
#import <UIKit/UIKit.h>
float addingtheTime;
#interface ViewController : UIViewController
{
IBOutlet UILabel *Label;
NSTimer *Timer;
}
#end
So my question is how do I make the Start button do the following once clicked:
Start the timer
Change the title to stop
(Click the same button again)
Stop the timer
Change the title to start
So the aim is to have only one button which starts and stops the timer but please I want the title to change as well.
Don't worry about the reset button as I want that to remain by itself.
PS. Please explain things clearly because I find it really hard to understand some things and I am a beginner so try and keep it fairly simple thanks, and sorry if the question isn't clear.
You can use button.tag property to achieve this. By Default the button.tag property have 0. So assume that 0 is a state when timer is Not started and 1 is the state when timer is started. You can manipulate .tag property according to your custom logic.
-(IBAction)Start:(id)start
{
UIButton *button = (UIButton *)start;
if (button.tag == 0){
button.tag = 1;
[button setTitle:#"Stop" forControlState:UIControlStateNormal];
Timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(addingthetime) userInfo:nil repeats:YES];
}else {
button.tag = 0;
[button setTitle:#"Stop" forControlState:UIControlStateNormal];
//Invoke your method to stop Timer.
}
}
In the ViewController.h:
#import <UIKit/UIKit.h>
float addingtheTime;
#interface ViewController : UIViewController
{
IBOutlet UILabel *Label;
NSTimer *Timer;
}
#end
In the ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)addingthetime
{
addingtheTime = addingtheTime + 0.01;
Label.text = [NSString stringWithFormat:#"%.2f", addingtheTime];
}
- (IBAction)startOrStopAction:(UIButton *)sender {
// stop
if ([sender.titleLabel.text isEqualToString:#"stop"]) {
[Timer setFireDate:[NSDate distantFuture]];
[sender setTitle:#"start" forState:UIControlStateNormal];
}
// start
else {
if (!Timer) {
Timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(addingthetime) userInfo:nil repeats:YES];
}else {
[Timer setFireDate:[NSDate date]];
}
[sender setTitle:#"stop" forState:UIControlStateNormal];
}
}
#end
The result:
In iPhone:
In iPad:
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];
}
}
It seems like such a simple thing to remove a button from a view, but it is not working.
MyViewController.h
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController
#property (strong, nonatomic, readonly, getter = getMyButton) UIButton* myButton;
- (id) init;
- (id) getMyButton;
#end
MyViewController.m
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
#synthesize myButton = _myButton;
- (id) init
{
if([super initWithNibName: nil bundle: nil])
{
_myButton = nil;
}
return self;
}
- (id) getMyButton
{
if(!_myButton) _myButton = [self createMyButton];
return _myButton;
}
- (void) viewDidLoad
{
[super viewDidLoad];
UIButton* myButton = self.myButton;
[self.view addSubview: myButton];
}
- (void) didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (UIButton*) createMyButton
{
UIButton *button = [[UIButton alloc] init];
[button setTitle: #"My Button"
forState: UIControlStateNormal];
[button addTarget: self
action: #selector(myAction:)
forControlEvents: UIControlEventTouchUpInside];
button.frame = CGRectMake(10, 10, 100, 40);
return button;
}
- (void) myAction: (id) sender
{
[self.myButton performSelectorOnMainThread: #selector(removeFromSuperview) withObject: nil waitUntilDone: NO];
}
#end
But no luck. Clicking the button simply does nothing.
If it is not a problem of concurrency then maybe it is a memory management problem? Maybe it is just something daft, I don't know.
I tried putting the following line into the myAction method
NSLog(#"Test 1");
if(_startButton.superview) NSLog(#"Test 2");
Only 'Test 1' is logged. Perhaps that is a clue but what I don't know is why the button has no superview when it is added to view and is visible on the screen
Additional information
I don't know, if any of this is relevant, but maybe
I just updated Xcode to the latest version from the developer program (it supported up to iOS version 7.0 before, now 7.1)
I just started testing the app on an actual iPhone (I get the same problem testing with the simulator though)
Around the same time as this problem I also noticed that NSLog function doesn't work inside AppDelegate applicationHasLaunched method
Thanks
You didn't set the action for that button.
Change your createMyButton method like:
- (UIButton*) createMyButton
{
UIButton *button = [[UIButton alloc] init];
[button setTitle: #"My Button"
forState: UIControlStateNormal];
button.frame = CGRectMake(10, 10, 100, 40);
[button addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
return button;
}
Also change myAction method like:
- (void)myAction:(UIButton *)sender
{
[sender performSelectorOnMainThread: #selector(removeFromSuperview) withObject: nil waitUntilDone: NO];
}
Your code seems overly complicated:
Why do you need to lazy create the button?
Why does it need to be readonly?
Why do you need the performselector in your action?
I have taken the liberty to rewrite your code:
Header file
#property (nonatomic,strong) UIButton * button;
Code file
- (void)viewDidLoad
{
[super viewDidLoad];
self.button = [UIButton buttonWithType:UIButtonTypeSystem];
[self.button setTitle:#"MyButton" forState:UIControlStateNormal];
self.button.frame = self.view.bounds;
[self.button addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.button];
}
-(void)myAction:(UIButton*)sender
{
[sender removeFromSuperview];
}
Happy coding!
Edit
If you insist on going with your original code, try changing
- (id) getMyButton
to
-(UIButton*)myButton
Buttons are a class family and should be created with [UIButton buttonWithType:]
I am sure that your myAction is not get fired. Cause you forget to addTarget
on your createMyButton method just add one more line
[button addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
UPDATE: You've also marked this question as iOS but you are importing OSX headers
#import "NSViewController.h"
#interface MyViewController: NSViewController
you should be inheriting from UIViewController
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController
Are you creating the view controller via the init code you've posted?
- (id) init
{
if([super initWithNibName: nil bundle: nil])
{
_myButton = nil;
}
return self;
}
Here you are not allocating anything to self the code should be
- (id) init
{
self = [super initWithNibName: nil bundle: nil];
if(self)
{
_myButton = nil;
}
return self;
}
I am developing a calculator app and I am trying to highlight touched buttons by changing the background color. My problem is that when I touch the button for the first time, it displays the numbers but the background color does not change. But later on it works fine, just the first touch does not work at all. I appreciate any help!
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <AudioToolbox/AudioToolbox.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize resultLabel;
BOOL isDecimal;
NSArray *array;
NSNumber *number;
NSNumberFormatter *formatter;
- (void)viewDidLoad
{
[super viewDidLoad];
array = [[NSArray alloc]initWithObjects:ACbuttonOutlet, deleteButtonOutlet, plusMinusOutlet, divideOutlet, number7Outlet, number8Outlet, number9Outlet, multiplicationOutlet, number4Outlet, number5Outlet, number6Outlet, plusOutlet, number1Outlet, number2Outlet, number3Outlet, minusOutlet, number0Outlet, decimalOutlet, resultOutlet, nil];
for(UIButton *button in array)
{
[button.layer setBorderWidth:0.25];
[button.layer setBorderColor:[UIColor blackColor].CGColor];
}
resultLabel.adjustsFontSizeToFitWidth = YES;
/*formatter = [[NSNumberFormatter alloc]init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setGroupingSize:3];
[formatter setGroupingSeparator:#" "];
number = [NSNumber numberWithInt:[resultLabel.text intValue]];
resultLabel.text = [formatter stringFromNumber:number];*/
// 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)highlightButton:(UIButton *)button
{
AudioServicesPlaySystemSound(0x450);
[button setBackgroundColor:[UIColor colorWithRed:239/255.0 green:232/255.0 blue:232/255.0 alpha:1]];
NSLog(#"1");
}
- (void)resetButtonBackGroundColor: (UIButton *)sender{
[sender setBackgroundColor:[UIColor whiteColor]];
NSLog(#"2");
}
- (IBAction)ACbutton:(id)sender {
[sender addTarget:self action:#selector(highlightButton:) forControlEvents:UIControlEventTouchDown];
[sender addTarget:self action:#selector(resetButtonBackGroundColor:) forControlEvents:UIControlEventTouchUpInside];
isDecimal = NO;
Method = 0;
SelectNumber = 0;
RunningTotal = 0;
resultLabel.text = [NSString stringWithFormat:#"0"];
}
Try to add this line in viewDidLoad method into your cycle
for(UIButton *button in array)
{
[button.layer setBorderWidth:0.25];
[button.layer setBorderColor:[UIColor blackColor].CGColor];
[button addTarget:self action:#selector(highlightButton:) forControlEvents:UIControlEventTouchDown];
}
I would suggest you not to work upon background color of the button . Instead toggle/change the background image of the button.
I want to have a certain method run and repeat itself for as long as someones finger is pressed down on a button. I want that method to stop repeating itself when the finger is not on the button anymore
Is there a way to check if the touchDown is still occurring during the method implementation? Help!
You can use the UIControlEventTouchDown control event to start the method running, and UIControlEventTouchUpInside, or similar, to detect when the button is no longer being "pressed".
Set up the actions to the button, e.g.:
[myButton addTarget:self action:#selector(startButtonTouch:) forControlEvents:UIControlEventTouchDown];
[myButton addTarget:self action:#selector(endButtonTouch:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
(Note the above will cause touch up inside and outside the button to invoke the endButtonTouch: method.)
Then add the startButtonTouch: and endButtonTouch methods, e.g., :
- (void)startButtonTouch:(id)sender {
// start the process running...
}
- (void)endButtonTouch:(id)sender {
// stop the running process...
}
Drawing upon the bobnoble's answer here's a helper view
#import <UIKit/UIKit.h>
#interface YOIDCAutorepeatingButton : UIButton
// you COULD pinch pennies switching to nonatomic, but consider
// how much time it would take to debug if some day some moron decides without checking this spec
// to alter this prop off another thread
#property (atomic) NSTimeInterval delayUntilAutorepeatBegins;
#property NSTimeInterval delayBetweenPresses;
#property (weak) id<NSObject> recipient;
#property SEL touchActionOnRecipient;
#end
-------------> .m
#import "YOIDCAutorepeatingButton.h"
#interface YOIDCAutorepeatingButton()
{
NSTimeInterval _pressStartedAt;
}
#end
#implementation YOIDCAutorepeatingButton
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self addTarget:self action:#selector(startButtonTouch:) forControlEvents:UIControlEventTouchDown];
[self addTarget:self action:#selector(endButtonTouch:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
self.delayUntilAutorepeatBegins = .250;
self.delayBetweenPresses = .080;
}
return self;
}
-(void)killLastCharacter:(id)sender
{
[self.recipient performSelector:self.touchActionOnRecipient withObject:sender];
}
- (void)performAutorepeat:(id)sender
{
if(!self.delayBetweenPresses) {
// bail
return;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.delayBetweenPresses * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if(_pressStartedAt) {
[self killLastCharacter:sender];
[self performAutorepeat:sender];
}
});
}
- (void)startButtonTouch:(id)sender {
[self killLastCharacter:sender];
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
_pressStartedAt = now;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.delayUntilAutorepeatBegins * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if(_pressStartedAt) {
[self killLastCharacter:sender];
[self performAutorepeat:sender];
}
});
}
- (void)endButtonTouch:(id)sender {
_pressStartedAt = 0;
}
#end
-------- sample usage -------
- (IBAction)killLastDigit:(id)sender {
.....
- (void)viewDidLoad
{
assert(self.backSpace);
[YOIDCAutorepeatingButton class]; // if xib is in a bundle other than main gottal load the class
// otherwise you'd get -[UIButton setRecipient:]: unrecognized selector sent to instance
// on setRecipient:
self.backSpace.recipient = self;
self.backSpace.touchActionOnRecipient = #selector(killLastDigit:);