I have a lot of custom buttons in my program and I need to use methods to get their names. I've figured out how to get their names through their tag, but I cant seem to follow it through to have the name in a string format.
Heres what I'm using:
-(void)pickRandomToHide {
for (int check = 1; check <=5; check++)
{
int eventNumber = 1 + arc4random() % 43;
UIButton *pick;
pick = (UIButton *)[_mapImageView viewWithTag:eventNumber];
[pick setHidden:YES];
NSString *buttonName;
buttonName = [pick currentTitle];
NSLog(#"%#",buttonName);
}
}
The NSLog just gives 'Null' five times. But 5 buttons are disappearing so the start is working.
The reason you're not seeing any titles is because the buttons are hidden and disabled, whereas the titles are likely only set to appear when the button's control state is "normal" (enabled and visible). Here's more information from Apple on the UIButton control states.
Try doing this:
-(void)pickRandomToHide {
for (int check = 1; check <=5; check++)
{
int eventNumber = 1 + arc4random() % 43;
UIButton *pick = (UIButton *)[_mapImageView viewWithTag:eventNumber];
if(pick)
{
[pick setHidden:YES];
NSString *buttonName = [pick titleForState: UIControlStateNormal];
NSLog(#"%#",buttonName);
} else {
NSLog( #"hmmm, no button with a tag corresponding to event number %d", eventNumber);
}
}
}
Related
I have a Xcode 5/Cocoa program that clicks the left mouse button after specified interval a specified number of times. That part works fine. The problem occurs when I want to stop the while loop prematurely.
I'm using a listener to detect any key press during the running of the program, set a stopnow variable and check for that variable in the while loop. But, the while loop doesn't detect the change in the variable until the loop finishes.
Also, I change a counter in the title bar of the window to display the count of clicks done, and that doesn't get updated either until the loop finishes.
I do get the NSLog message when I press a key.
I'm very confused.
My code is here :
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[[self myWindow] setLevel:NSFloatingWindowLevel];
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *event) {
keychar = (unichar) event.characters;
[NSApp activateIgnoringOtherApps:YES];
stopnow = 1;
NSLog(#"Key Pressed = x%x (%x) (%x)",keychar,(keychar&0x7f00),((keychar&0xff00)>>8));
}];
}
- (IBAction)setClickPoint:(NSButton *)sender {
sleep(5);
CGEventRef ourEvent = CGEventCreate(NULL);
cgPoint = CGEventGetLocation(ourEvent);
myPoint = [NSString stringWithFormat:#" (%5.0f,%5.0f)", cgPoint.x, cgPoint.y];
myNewTitle = [mytitle stringByAppendingString:myPoint];
[[self myWindow] setTitle:myNewTitle];
}
(IBAction)strtButton:(NSButton *)sender {
NSLog(#"Entered strButtn");
numClicks = [_nClicks intValue];
numWait = [_nWait floatValue];
i = 0;
while (i < numClicks || numClicks == 0) {
i++;
myTotal = [NSString stringWithFormat:#" %i of %i", i, numClicks];
myNewTitle = [mytitle stringByAppendingString:myPoint];
myNewTitle = [myNewTitle stringByAppendingString:myTotal];
[[self myWindow] setTitle:myNewTitle];
CGWarpMouseCursorPosition(cgPoint);
CGEventRef down = CGEventCreateMouseEvent(0, kCGEventLeftMouseDown,cgPoint, 0);
CGEventPost(kCGSessionEventTap, down);
CFRelease(down);
CGEventRef up = CGEventCreateMouseEvent(0, kCGEventLeftMouseUp,cgPoint, 0);
CGEventPost(kCGSessionEventTap, up);
CGRealease(up);
NSLog(#"stopnow = %i", stopnow);
if (stopnow == 1) {
stopnow = 0;
break;
}
usleep((unsigned int)(numWait * 1000000.0));
}
}
A Cocoa/Cocoa Touch app is an event-based environment, so you cannot have long running "loops" in the main thread, as you stop the handling and delivery of the events.
When your loop finishes, the UI is able to update the bits you are seeing, as it can now deliver the events.
You will need to do this work in the background thread, or some such.
Ok, here is what works - use dispatch_async(global type) for the main loop, use dispatch_async(main queue) for the code that updates the title.
I am new to the community, so let me know if my question is unclear. I am trying to make a choice reaction exercise on the iPAD. There are two images that should appear in random sequence on the left and right of the screen, and the user will respond by tapping a button that corresponds to the position of the appeared image. Here's the problem, I tried to get the two images to appear at random order using the following way:
- (void) viewDidAppear:(BOOL)animated
{
for(int n = 1; n <= 20; n = n + 1)
{
int r = arc4random() % 2;
NSLog(#"%i", r);
if(r==1)
{
[self greenCircleAppear:nil finished:nil context: nil];
}
else
{
[self redCircleAppear:nil finished:nil context: nil];
}
}
}
However, 20 random numbers get generated while only 1 set of animation is run. Is there a way to let the animation finish running in each loop before the next loop begins? Any help is appreciated, thanks in advance!
When you say "only one set of animation is run" I'm assuming that means greenCircleAppear and redCircleAppear begin the sequence of images appearing and the user pressing a button. If that's the case, I'd recommend not using a for loop in viewDidAppear but instead have viewDidAppear initialize the current state and call a method that presents the next animation. When the animation is finished, have it call the method that presents the next animation. Something along these lines:
Add this to the interface:
#interface ViewController ()
#property NSInteger currentIteration;
#end
This is in the implementation:
- (void)viewDidAppear:(BOOL)animated {
self.currentIteration = 0;
[self showNextAnimation];
}
- (void)greenCircleAppear:(id)arg1 finished:(id)arg2 context:(id)arg3 {
//perform animation
NSLog(#"green");
[self showNextAnimation];
}
- (void)redCircleAppear:(id)arg1 finished:(id)arg2 context:(id)arg3 {
//perform animation
NSLog(#"red");
[self showNextAnimation];
}
- (void)showNextAnimation {
self.currentIteration = self.currentIteration + 1;
if (self.currentIteration <= 20) { //you should replace '20' with a constant
int r = arc4random() % 2;
NSLog(#"%i", r);
if(r==1)
{
[self greenCircleAppear:nil finished:nil context: nil];
}
else
{
[self redCircleAppear:nil finished:nil context: nil];
}
}
else {
//do what needs to be done after the last animation
}
}
I'm trying to validate dynamically created text fields. The total number of textfields may vary.
The idea is to populate the empty fields with string like player 1, player 2 etc.. Here is what I try
-(IBAction)validateTextFields:sender
{
self.howManyPlayers = 3;
int emptyFieldCounter = 1;
NSMutableArray *playersNames = [NSMutableArray arrayWithCapacity:self.howManyPlayers];
while (self.howManyPlayers > 1)
{
self.howManyPlayers--;
UITextField *tmp = (UITextField *) [self.view viewWithTag:self.howManyPlayers];
if (tmp.text == nil)
{
[tmp setText:[NSString stringWithFormat:#"Player %d", emptyFieldCounter]];
emptyFieldCounter++;
}
[playersNames addObject:tmp.text];
}
}
The problems is that if I touch the button which invoke validateTextFields method. The first and the second textfield are populated with text Player 1 and Player 2, but the third field is not populated.
I notice also that if I type a text let's say in the second field touch the button then remove the text and again touch the button that field is not populated with text Player X.
How to make all that things to work correctly ?
change your code for two lines like this:
while (self.howManyPlayers >= 1) //edited line
{
UITextField *tmp = (UITextField *) [self.view viewWithTag:self.howManyPlayers];
if (tmp.text == nil)
{
[tmp setText:[NSString stringWithFormat:#"Player %d", emptyFieldCounter]];
emptyFieldCounter++;
}
[playersNames addObject:tmp.text];
self.howManyPlayers--; // moved line
}
I forgot ur second question, so edited my answer.
For that try with this. Change if (tmp.text == nil) with if (tmp.text == nil || [tmp.txt isEqualToString:#""])
The reason only two fields are populated is that you are only going through the while loop twice. It should be
while (self.howManyPlayers >= 1)
You should also move the decrement to the end of your while loop
while (self.howManyPlayers >= 1)
{
// other code here
self.howManyPlayers--;
}
For the second part of your question, I think when you delete the text from the control, it stops being nil and now becomes an empty string. So you need to check for an empty string as well as nil in your code.
if (tmp.text == nil || [tmp.txt isEqualToString:#""])
Goodday,
I got the following problem with this code:
-(void)textpopup:(UISegmentedControl *)sender {
int nummer = sender.tag;
if (sender.tag) {
if(sender.selectedSegmentIndex == 0 || sender.selectedSegmentIndex == 1){
beoordeling = [[UITextField alloc] init];
beoordeling.frame = CGRectMake(50 , nummer * 117 + 275 , scrollView.frame.size.width - 100 , 35);
beoordeling.autoresizingMask = (UIViewAutoresizingFlexibleWidth);
beoordeling.borderStyle = UITextBorderStyleLine;
beoordeling.tag = nummer;
[scrollView addSubview:beoordeling];
}
if(sender.selectedSegmentIndex == 2 || sender.selectedSegmentIndex == 3){
if (beoordeling.tag == sender.tag) {
[beoordeling removeFromSuperview];
}
}
}
}
i shall explain the scenario. I got some dynamic UISegmentedControls. At the moment there are 12 of them. At the first 2 segments chosen, a textfield needs to popup. This goes well. But after choosing the first 2 segments for a while and when i go to segments 2 and 3, sometimes the textfields won't remove.
I expected that the textfields which are written when i push segment 0 and 1 are removed when segment 2 and 3 are pushed.
Am i missing something?
EDIT:
At first i want to say, that i never know in advance how many UITextFields i got. When segments 0 and 1 are chosen, a UITextField needs to popup to that corresponding UISegmentedControl. And when segments 2 and 3 are chosen, the UITextField needs to stay away. But i got that fixed now in the following way.
-(void)textpopup:(UISegmentedControl *)sender {
int nummer = sender.tag;
if(sender.selectedSegmentIndex == 0 || sender.selectedSegmentIndex == 1){
// Before i add a new UITextField, the old one has to be removed.
UITextField *text = (UITextField *)[beoordeling viewWithTag:sender.tag];
[text removeFromSuperView];
beoordeling = [[UITextField alloc] init];
beoordeling.frame = CGRectMake(50 , nummer * 117 + 275 , scrollView.frame.size.width - 100 , 35);
beoordeling.autoresizingMask = (UIViewAutoresizingFlexibleWidth);
beoordeling.borderStyle = UITextBorderStyleLine;
beoordeling.tag = nummer;
[scrollView addSubview:beoordeling];
}
else if(sender.selectedSegmentIndex == 2 || sender.selectedSegmentIndex == 3) {
UITextField *tf = (UITextField *)[beoordeling viewWithTag:sender.tag];
tf.text = nil;
[tf removeFromSuperview];
}
}
The second if should be an else if, because it should execute only if the first one doesn't (the selected index can't be both 0 and 2).
What you're trying to do depends on how you've defined beoordeling. I suggest having it as an instance variable, possibly even an IBOutlet. Release in your class's dealloc. Then, in the first if clause, write
if (!beoordeling) {
beoordeling = [[UITextField alloc] init];
// Other setup
}
[scrollView addSubview:beoordeling];
I don't quite understand why you're checking for the tag, but to remove in the second if, just call removeFromSubview.
Do all your segmented controls invoke the same behavior? If you have only one beorrdeling that you're setting the tag on, then you don't need to bother checking for the tag. Just add/remove it, using removeFromSubview. If you have as many of text fields as segmented controls, maybe KVC would be what you want. If your segmented controls' tags go from 0–11, you might have beoordeling0, beoordeling1, beoordeling2, and so on. Then, to get the one you want, use something like this:
beoordeling = (UITextField *)[self valueForKey:[NSString stringWithFormat:#"beoordeling%d", sender.tag]];
Not really sure what you are trying to achieve here, but you do need to release your UITextField object beoordeling or you will leak. AddSubview adds one to the retain count, so should be safe to release straight away:
[scrollView addSubview:beoordeling];
[beoordeling release];
I am trying to loop through and programmatically make a grid of 256 NSButtons (16x16). The code I have so far is below. This is in Objective-C for my Mac app. So I am logging to see which tag I get when I click a button, but it keep returning the same tag every time.
I want it so that every button goes 1-256 from left to right, top to bottom. This code successfully makes them load into my view, but the tags are wrong.
#define N_ROWS 16
#define N_COLS 16
int btnSpaceDifference = 1;
int btnSpacing = N_ROWS + btnSpaceDifference;
for (int j = 0; j < N_ROWS; j++) {
for (int i = 0; i < N_COLS; i++) {
paintPixel = [[[NSButton alloc] initWithFrame:NSMakeRect(10 + (i * btnSpacing), 10 + (j * btnSpacing), 16, 16)] autorelease];
[paintPixel setTitle:#""];
[paintPixel setBezelStyle:NSBorderlessWindowMask];
[paintPixel setTag:j + i * N_ROWS + 1];
[paintPixel setAction:#selector(btnPaintAction:)];
[[[box.tabViewItems objectAtIndex:0]view] addSubview:paintPixel];
}
}
-(void)btnPaintAction:(id)sender{
NSLog(#"%ld", paintPixel.tag);
}
Instead of making all of these buttons yourself, why not use an NSMatrix? This is the sort of thing it's perfect for.
Not sure how its compiling, you might have paintPixel defined elsewhere. But you need to change your btnPaintAction from:
-(void)btnPaintAction:(id)sender {
NSLog(#"%ld", paintPixel.tag);
}
To something like this:
-(void)btnPaintAction:(id)sender {
NSButton * myButton = (NSButton *) sender;
NSLog(#"%ld", myButton.tag);
}
call setTag with an increment variable
int TagVal = 1;
for (int j = 0; j < N_ROWS; j++) {
....
[paintPixel setTag:TagVal++];
....
}
Then modify your btnPaintAction:
UIButton *button = (UIButton *)sender;
NSLog(#"%ld", button.tag);
It's returning the same tag every time because your action is referring to your (apparently) member variable paintPixel. Use the sender parameter to the action instead.
NSLog(#"%ld", ((NSButton *)sender).tag);
This is old post but I see no correct answer, thus my addition.
Q. " I want it so that every button goes 1-256 from left to right, top to bottom."
Kevin was on the good track, however one more alteration is required:
paintPixel = [[[NSButton alloc] initWithFrame:NSMakeRect(10 + (i * btnSpacing), 10 - (j * btnSpacing), 16, 16)] autorelease];
Thus minus(-) instead of plus(+) results in numbering top to bottom.