how to find which objects are selected:TRUE from 64 buttons labelled b1, b2 etc - iphone

I have a project where there are 64 buttons, you have to click certain ones, then click 'done'. If you clicked the right ones, you get some points, and those then need to disappear.
I have let the user keep track of which buttons are pressed by doing sender setSelected:TRUE.
The first bit is all working fine, but I'd like to be able to then hide the buttons that were selected when the user clicked 'done'.
My current thoughts are:
- what I used in Actionscript to do the same thing was
for (i=1;i++;i<65) {
if(getProperty ("b"+ i, _visible) == true) {do blah blah blah} }
I'm really really really hoping there is an obvious equivalent in objective C that does the same thing?
I definitely do not want to have to go through all 64 buttons and type if ([b1 isSelected == TRUE]etc...
I can't just use sender, as it may be several buttons that have previously been selected that I need to access.
EDIT -
This is now the code that is called when user presses one of the 64 buttons.
-(IBAction) pressed:(id)sender {
UIButton *button = (UIButton *)sender;
if ([sender isSelected] ==FALSE) {
[sender setSelected:TRUE];
}
else {
[sender setSelected:FALSE];
}
if ([myArray containsObject:sender])
{
[myArray removeObject:sender];
}
else {
[myArray addObject:sender];
}
}
This is called when they press the 'done' button.
-(IBAction) checkTotal:(id)sender {
if (total == [(totaltxt.text) intValue]) {
score += 1;
scoretxt.text = [NSString stringWithFormat:#"%i", score];
for (UIButton *b in myArray)
{
[b setHidden:TRUE];
}
[myArray removeAllObjects];
}
else {
// some indication that they pressed the button but were wrong.
}
}
It unfortunately still won't hide the button.
It works if I change it to [n1 setHidden:TRUE] to hide the matching textbox above the button, but won't hide even a specific button -eg- [b1 setHidden:TRUE], let alone all the buttons in my array.
AAAAAAAARGH!!!!
Any ideas?

You can iterate on your view's subviews, and check whether the subview is a button:
for (UIView *view in self.view.subviews)
{
if ([view isKindOfClass: [UIButton class]])
{
// Do processing here
// For instance:
if ((UIButton *)view).isSelected)
view.hidden = YES;
}
}

If you dont want to iterate through all the buttons then how about store a reference to your button in an array then just iterate through that array and clear it when the use clicks done?
-(void)click:(id)sender
{
if([myArray containsObject:sender])
[myArray removeObject:sender];
else
[myArray addObject:sender];
}
-(void)doneClicked:(id)sender
{
for(UIButton *b in myArray)
{
[b setHidden:TRUE];
}
[myArray removeAllObjects]; //whateverr the method is i dont remember it off the top of my head
}

Try [b setAlpha:0.0];

Set the tag when creating the buttons and store the tag index in an array.
-(IBAction) pressed:(id)sender {
}
by sender.tag
then finally run the loop hide all object
and again run loop to unhide the object if it exists in array.

Related

Check if any textfield, textview or label changed for a UIViewController..?

I have a view controller and it contains n number of UITextFields and UItextViews and UILabels, is there anyhow I can get notified if any of them changes..?
I can do it by manually looking at each of them in their respective TextDidChangeNotification and similar but I am looking for a more optimized way of doing this, where I don't have to worry about each of them .
// Assumes you don't use tag values now - if you do small change to create
// and index set, add the ones you use, so all new ones assigned are unique.
// assumes ARC
1) New ivar:
{
NSMutableDictionary *savedValues;
}
1) When you want to baseline the values:
savedValues = [self snapshot];
2) call this to baseline current values initially then at any later point:
- (NSMutableDictionary *)snapshot
{
NSInteger tag = 1;
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:[self.view.subviews count]];
for(UIView *v in self.view.subviews) {
if([v isKindOfClass:[UILabel class]] || [v isKindOfClass:[UITextField class]] || [v isKindOfClass:[UITextView class]]) {
if(v.tag == 0) v.tag = tag++; // will happen once
// EDIT below
    [dict setObject:[NSString stringWithString:[(id)v text]] forKey:[NSNumber numberWithInteger:tag]];
}
}
return dict;
}
4) When you want to see if anything changed:
- (BOOL)anyChanges
{
NSDictionary *currentDict = [self snapshot];
return [currentDict isEqualToDictionary:savedValues];
}
You should use one method for all textFields, textView and Labels. and give a unique tag to textFields and textViews and labels. which help you for define it's textField ,textView or Label.
if(sender.tag == 1000)//UILabel
{
UILabel *label=(UILabel *)sender
//write own code for label what do you want.
}
else if(sender.tag == 2000)//UITextField
{
UITextField *textField=(UITextField *)sender
//write own code for textField what do you want.
}
else if(sender.tag == 3000)// UITextView
{
UITextView *textView=(UITextView *)sender
//write own code for textView what do you want.
}
Make your class to handle UIControlEventValueChanged event in your textfield, textview or label's value is changed. Add this line ViewDidLoad method:
[youLabel addTarget:self action:#selector(valueChanged:)
forControlEvents:UIControlEventValueChanged];
// add for textfield, textview also if needed
[youTextField addTarget:self action:#selector(valueChanged:)
forControlEvents:UIControlEventValueChanged];
[youTextView addTarget:self action:#selector(valueChanged:)
forControlEvents:UIControlEventValueChanged];
Now selector would be called when ever value is changed
- (void) valueChanged:(id)sender{
if(sender isKindofClass[UILabel class])
{
//label value changed here to differntiate used tag
if([sender tag] == 0)
....
....
}
else if(sender isKindofClass[UITextField class])
{
// textField value changed to differntiate used tag
if([sender tag] == 0)
....
....
}
else if(sender isKindofClass[UITextView class])
{
// textview value changed to differntiate used tag
if([sender tag] == 0)
....
....
}
}

Can't read the segmentedcontroll index

I have implemented several of UISegmentedControl objects before but for one or other reason I am unable to implement it within a tableview cell. I want the user to choose his gender. if the value of the segmentedControl changed a method gets called to save it for further processing.
self.seg = [[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Man",#"Female", nil]];
[self.seg addTarget:self action:#selector(toggleGender) forControlEvents:UIControlEventValueChanged];
I created the method:
-(void)toggleGender{
NSLog(#"%d",self.seg.selectedSegmentIndex)
if (self.seg.selectedSegmentIndex == 0)
{
NSLog(#"User is a man");
} else if (self.seg.selectedSegmentIndex == 1)
{ NSLog(#"User is a female");
}
}
The first NSLog prints -1 as value if the value has changed.
I declared seg in the header file, and created a property and synthesized it!
I am currently NOT dealllocing this object. (I know this is a memory leak)
Why am I unable to read-out the selected segment, and why does it give back -1 as selected item?
Try this :
self.seg = [[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Man",#"Female", nil]];
[self.seg addTarget:self action:#selector(toggleGender:) forControlEvents:UIControlEventValueChanged];
(NOTE THE ':' in the #selector..). And change your function by this :
-(void)toggleGender:(id)sender
{
if ([sender isKindOfClass:[UISegmentedControl class]])
{
NSLog(#"%d",[(UISegmentedControl *)sender selectedSegmentIndex]);
if ([(UISegmentedControl *)sender selectedSegmentIndex] == 0)
{
NSLog(#"User is a man");
}
else if ([(UISegmentedControl *)sender selectedSegmentIndex] == 1)
{
NSLog(#"User is a female");
}
}
}

issue with UITapGestureRecognizer for a button

I've done the following thing:
buttonPlaylistView = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width *(urlList.count+1), 0, self.view.frame.size.width, self.view.frame.size.height)];
buttonPlaylistView.tag = 0;
UITapGestureRecognizer *doubleTap3 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap3 setNumberOfTapsRequired:2];
[buttonPlaylistView addGestureRecognizer:doubleTap3];
[doubleTap3 release];
-(void) handleDoubleTap:(UITapGestureRecognizer *)sender{
if(sender.state == UIGestureRecognizerStateEnded)
int x = [sender tag];
return;
}
But I get SIGAGRT at this line: int x = [sender tag]; saying:
[UITapGestureRecognizer tag]: unrecognized selector sent to instance 0x61280b0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITapGestureRecognizer tag]: unrecognized selector sent to instance 0x61280b0'
NOW:What's the problem and what's the solution for this?Thanks
-(void) handleDoubleTap:(UITapGestureRecognizer *)sender
{
if(sender.state == UIGestureRecognizerStateEnded)
{
int x = [sender.view tag];
}
}
Will fix the issue.
An UITapGestureRecognizer does not have a property named tag - as you see, the sender you get is NOT the button. You have to access the buttonPlayListView directly, like
int x = [buttonPlayListView tag];
or otherwise remember which button you want to access.
Even though I'm quite sure that you're going about this the wrong way, adding a double tap gesture recognizer to a UIButton, there is a way you can still perform the task you require that shouldn't be too much work for you.
You've made the comment
and how could I remember if I create let say 100 buttons
to one of the answers, the one which highlights what the issue is that's causing your SIGBART. UIGestureRecognizer does not have a tag property.
Here's what you could do, is to iterate through all the subviews of your [self view] and find the one that has the same UIGestureRecognizer, it's not the prettiest solution, and the more subview's you have the longer the loop will take. But it'll do what you seem to be looking for, so if you're adding .
In your handleDoubleTap function you could do the following
-(void) handleDoubleTap:(UITapGestureRecognizer *)sender
{
if(sender.state == UIGestureRecognizerStateEnded)
{
int iButtonTag = -1 //This is important later to escape the below for loop as we don't need to needlessly go around in circles
for(UIView* psubView in [[self view] subviews])
{
if( [psubView isKindOfClass:[UIButton class]] )
{
UIButton* pButton = (UIButton*)psubView;
for(UIGestureRecognizer* pGesture in [pButton gestureRecognizers] )
{
if( pGesture == sender )//this is the button we're after
{
iButtonTag = [pButton tag];
break;
}
}
if( iButton != -1 )//found what we came for
{
break;
}
}
}
//do what ever it was you needed to do now that you have the views tag, or you could have kept a reference to the button etc.
}
}
That should solve your problem. Alternatively if you're going to be adding buttons to subviews of subviews it would be better to keep track of your UIButtons in an NSMutableArray , you would do this by creating a class property (or member variable) and adding the buttons to this using the 'addObject:' function of NSMutableArray. Then instead of the line
for(UIView* psubView in [[self view] subviews])
above you could exchange that for
for( UIButton* pButton in m_pMutableButtonArray )
where "m_pMutableButtonArray" is the variable name you gave to your NSMutableArray you were storing the UIButtons in. This also means you would do away with the following if isKindOfClass test on the following line.
That should fix your problem.
Why are you putting a UITapGestureRecognizer in a button? The button already handles that for you and will send you a callback, you can add a target to a button using this UIControl method
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents

Hiding or moving SegmentContoller

Hello I've tried for 3 weeks to solve this issue and it stumps me. What i am trying to do is create a 3 part segment from an array, display it in a view in a certain position, then remove it from view when the "OFF" flag is set. Every thing works except the removal of the segment. It will even commuticate with (pickOne) and display the segment letters in a label. What i can't get to work is either of the two: setHidden:YES, or removeAllSegments. Any help would be appreciated. Here is my code.
- (void) showSegment {
int x = 192;
int y = 212;
int w = 125;
int h = 25;
SegUnit1 = #"A";
SegUnit2 = #"B";
SegUnit3 = #"C";
threeSegs = [NSArray arrayWithObjects: SegUnit1, SegUnit2, SegUnit3, nil];
segSize = [NSArray arrayWithArray:threeSegs];
UISegmentedControl *heightSC = [[UISegmentedControl alloc] initWithItems:segSize];
if ([segmentState_height isEqualToString:#"ON"]) {
NSLog(#"segmentState_height = %#",segmentState_height);
heightSC.frame = CGRectMake(x, y, w, h);
heightSC.segmentedControlStyle = UISegmentedControlStyleBar;
heightSC.selectedSegmentIndex = -1;
[heightSC addTarget:self
action:#selector(pickOne:)
forControlEvents:UIControlEventValueChanged];
[self.view addSubview:heightSC];
[heightSC release];
} else if ([segmentState_height isEqualToString:#"OFF"]) {
NSLog(#"segmentState_height = %#",segmentState_height);
[heightSC setHidden:YES]; // NSLog showing "OFF" but segment will not hide.
[heightSC removeAllSegments]; // NSLog showing "OFF" and segment is suppose to dismantle and does not.
}
}
I know now that i have to "not" create and remove in the same function, and was given a tip on correcting this but I don't know how to use the tip.
here is what was suggested.
Well, your method is a little confused, since you are trying to both create and hide at the same time. So you might consider splitting that up into separate methods.
In general, it will be along these lines:
Code:
if ([self theControlProperty] == nil)
{
UISeg... *theControl = [[UISeg alloc] ....];
[self setTheControlProperty:theControl];
...
}
if (shouldHideTheControl)
{
[[self theControlProperty] setHidden:YES];
}
Any help would be appreciated.
The problem you have is that you're creating a new UISegmentedControl instance every time that method is called. The first time through, you create an instance and add it as a subview to your view. This apparently works fine, as it should. Then the method returns, and you no longer have any easy way to refer to that instance that you created. When you re-enter -showSegment, you create a different instance, and then hide and/or destroy it. This different instance has no effect whatsoever on the instance that you gave to the view.
What you need to do is make heightSC an instance variable. Add it to the interface declaration in the header file, then initialize it only once, and hide or modify it as needed subsequently. The key point is that you need to have a reference to the instance of the UISegmentedControl which is being drawn on the screen, a reference that lives outside the method itself that you can use the second, third, fourth, etc time you call that method.
Try using the remove segments in your button choice method pickOne. This takes it outside the showSegment method and matches the users desired action to make the change and clear off the buttons.
- (void) pickOne:(id)sender {
UISegmentedControl* userChose = sender;
if( [userChose selectedSegmentIndex] == 0 ){
your first button operation;
[heightSC removeAllSegments];
}
if( [userChose selectedSegmentIndex] == 1 ){
your second button operation;
[heightSC removeAllSegments];
}
if( [userChose selectedSegmentIndex] == 2 ){
your third button operation;
[heightSC removeAllSegments];
}
}
I tried this and got the results I was looking for. Thanks goes to Mythogen and BrianSlick I just need to check and make sure there are no leaks. Now that will be a task.
Does anyone know if I need the second [heightSC release]; ?
// .h
# interface ------ {
UISegmentedControl *segmentPicked;
}
|
#property (nonatomic, retain) UISegmentedControl *segmentPicked;
// .m
|
#synthesize segmentPicked;
|
if ([self segmentPicked] == nil) {
UISegmentedControl *heightSC = [[UISegmentedControl alloc] initWithItems:segSize];
[self setSegmentPicked:heightSC];
[heightSC release];
heightSC.frame = CGRectMake(x, y, w, h);
heightSC.segmentedControlStyle = UISegmentedControlStyleBar;
heightSC.selectedSegmentIndex = -1;
[heightSC addTarget:self
action:#selector(pickOne:)
forControlEvents:UIControlEventValueChanged];
[self.view addSubview:heightSC];
[heightSC release];
}
if ([segmentState_height isEqualToString:#"OFF"])
{
[[self segmentPicked] setHidden:YES];
} else {
[[self segmentPicked] setHidden:NO];
}
[yourSegment removeFromSuperview];
?

Creating a custom UIKeyBoard for iPhone

If anyone has the app GymBuddy, then they will know what I am talking about. They seem to use the stock Number Pad keyboard but have added a "." button in the lower left as well as a bar across the top to switch to alpha characters. Does anyone know how to do this? Do I make a new view like the keyboard and pull it up and have the buttons correspond to the textField for input? I can't seem to find any information on customizing a keyboard or creating your own. Thanks
I have done this. Basically you add your own button as a subview of the UIKeyboard like this:
// This function is called each time the keyboard is going to be shown
- (void)keyboardWillShow:(NSNotification *)note {
// Just used to reference windows of our application while we iterate though them
UIWindow* tempWindow;
// Because we cant get access to the UIKeyboard throught the SDK we will just use UIView.
// UIKeyboard is a subclass of UIView anyways
UIView* keyboard;
// Check each window in our application
for(int c = 0; c < [[[UIApplication sharedApplication] windows] count]; c ++)
{
// Get a reference of the current window
tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:c];
// Loop through all views in the current window
for(int i = 0; i < [tempWindow.subviews count]; i++)
{
// Get a reference to the current view
keyboard = [tempWindow.subviews objectAtIndex:i];
// From all the apps i have made, they keyboard view description always starts with <UIKeyboard so I did the following
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES)
{
// Only add the Decimal Button if the Keyboard showing is a number pad. (Set Manually through a BOOL)
if (numberPadShowing && [keyboard viewWithTag:123] == nil) {
// Set the Button Type.
dot = [UIButton buttonWithType:UIButtonTypeCustom];
// Position the button - I found these numbers align fine (0, 0 = top left of keyboard)
dot.frame = CGRectMake(0, 163, 106, 53);
dot.tag = 123;
// Add images to our button so that it looks just like a native UI Element.
[dot setImage:[UIImage imageNamed:#"dotNormal.png"] forState:UIControlStateNormal];
[dot setImage:[UIImage imageNamed:#"dotHighlighted.png"] forState:UIControlStateHighlighted];
//Add the button to the keyboard
[keyboard addSubview:dot];
// When the decimal button is pressed, we send a message to ourself (the AppDelegate) which will then post a notification that will then append a decimal in the UITextField in the Appropriate View Controller.
[dot addTarget:self action:#selector(sendDecimal:) forControlEvents:UIControlEventTouchUpInside];
return;
}
else if (numberPadShowing && [keyboard viewWithTag:123])
{
[keyboard bringSubviewToFront:dot];
}
else if (!numberPadShowing)
{
for (UIView *v in [keyboard subviews]){
if ([v tag]==123)
[v removeFromSuperview];
}
}
}
}
}
}
- (void)sendDecimal:(id)sender {
// The decimal was pressed
}
Hope that's clear.
-Oscar
Check this post, this could be your answer:
UIKeyboardTypeNumberPad and the missing "return" key