I am creating a custom delegate UIAlertView's alert:buttonClickedAtIndex: method, but it is not working properly. I am subclassing a UIView, and I have two buttons that are tagged as 0 and 1. This still does not work when I go to check the delegate for my custom view. Here the code that I did.
Custom View
- (void) buttonTouchedWithIdentifier:(NSInteger)identifier
{
if (identifier == 0) {
[self.delegate alert:self didClickButtonWithTagIdentifier:0];
}
if (identifier == 1) {
[self.delegate alert:self didClickButtonWithTagIdentifier:1];
}
}
* in my showInViewMethod *
[self.dismissButton addTarget:self action:#selector(buttonTouchedWithIdentifier:) forControlEvents:UIControlEventTouchDown];
[self.continueButton addTarget:self action:#selector(buttonTouchedWithIdentifier:) forControlEvents:UIControlEventTouchDown];
self.dismissButton.tag = 0;
self.continueButton.tag = 1;
* in my view controller *
nextLevelAlert = [[ARAlert alloc] init];
nextLevelAlert.delegate = self;
[nextLevelAlert showInView:self.view
withMessage:[NSString stringWithFormat:#"Congratulations, you have completed level %i.\nWould you like to continue?", levelNumber]
dismissButtonTitle:#"Menu"
continueButtonTitle:#"Next Level"];
- (void)alert:(ARAlert *)alert didClickButtonWithTagIdentifier:(NSInteger)tagId
{
if (alert == nextLevelAlert) {
if (tagId == 0) {
NSLog(#"User does not want to continue.");
}
}
}
Now, nextLevelAlert has the delegate set to self, and I do have the delegate declared in my view controller's class. Also, when i do the showInView... for nextLevelAlert, it DOES appear, it is recognizing what button is being pressed.
My guess is your param is not a NSInteger but the button, you should change buttonTouchedWithIdentifier like this :
- (void) buttonTouchedWithIdentifier:(id)sender
{
UIButton *button = (UIButton*)sender;
NSLog(#"buttonTouchedWithIdentifier %#",#(button.tag));
if (button.tag == 0) {
[self.delegate alert:self didClickButtonWithTagIdentifier:0];
}
if (button.tag == 1) {
[self.delegate alert:self didClickButtonWithTagIdentifier:1];
}
}
Also when comparing two objects use isEqual: instead of ==
- (void)alert:(ARAlert *)alert didClickButtonWithTagIdentifier:(NSInteger)tagId
{
if ([alert isEqual:nextLevelAlert]) {
if (tagId == 0) {
NSLog(#"User does not want to continue.");
}
}
}
Related
I have an array of buttons:
pokemon_cards = [[NSMutableArray alloc] init];
[pokemon_cards addObject:self.cardButton1];
[pokemon_cards addObject:self.cardButton2];
[pokemon_cards addObject:self.cardButton3];
later in some method I want to do a BOOL check to see if ALL of them are NOT selected simultaneously. In other words, if ALL of them are not selected notify user, otherwise proceed, even if just one of them is selected.
Here is what i've done but its not working and I can't figure out how to do this without adding the buttons in the loop to a temporary array:
-(BOOL)userCanProceedToTurn {
for (UIButton *button in pokemon_cards) {
if (![button isSelected]) {
// This only works for OR condition I want it to work for &&
return NO;
} else {
return YES;
}
}
}
So this is what I want it to do pretty much but the function above doesn't work for &&:
if (![self.cardButton1 isSelected] && ![self.cardButton2 isSelected] && ![self.cardButton3 isSelected]) {
//notify users they must selected at least one card
} else {
}
But i don't know which cards will be added to the array, that depends on the user, so I don't know how to check for that in the for loop
EDIT
I have implemented the code as suggested below. and as mentioned before this does not the && check that I was concerned with.
For example i need to make sure ALL cards are not currently in the 'not selected' state. but if one of those 3 cards are then they can proceed, even if the other two aren't. but with the check below, it will not proceed because the else statement is in the loop as well so everytime the loop is ran the buttons that aren't selected cause it to not proceed because the loop is ran 3 times.
here is my complete bool method, everything else works fine except the button one:
-(BOOL)userCanProceedToTurn {
if (self.energyAmount == 0) {
UIAlertView *view .. tell users they need energy before proceeding
return NO;
}
if (self.usernameLabel.text.length == 0) {
//Tell user they are not signed in
return NO;
}
NSLog(#"button is %lu", (unsigned long)pokemon_cards.count);
for (UIButton *button in pokemon_cards) {
if ([button isSelected]) {
NSLog(#"button.tag == %lu",button.tag);
return YES;
} else {
UIAlertView *view .. tell users they need to select at least one card
//this gets called because the loop is ran as many times there are buttons so inevitably i'll get an error. Which is why this works for the first button only, because it stops looping after it found that one since it was the first once selected
return NO;
}
}
return YES;
}
Do you actually need to know the state of each button?
Why not take the opposite approach:
- (BOOL)userCanProceedToTurn {
for (UIButton *button in pokemon_cards) {
if ([button isSelected]) {
return YES;
}
}
return NO;
}
EDIT
As a rule of thumb, methods that return a BOOL value should start with a flag set to YES or NO and only invert that flag, never setting it back to its original value. So basically, start with with BOOL result = YES and only flip it to NO, never ever flip it back to YES. This will have the pseudo-security of preventing something bad to happen.
Here's your method rewritten with this concept:
- (BOOL)userCanProceedToTurn {
BOOL isEverythingOK = NO;
NSString *message = nil;
if (self.energyAmount != 0) {
isEverythingOK = YES;
} else {
message = #"You need energy before proceeding.";
}
if (self.usernameLabel.text.length != 0) {
isEverythingOK = YES;
} else {
message = #"You are not signed in.";
}
for (UIButton *button in pokemon_cards) {
if ([button isSelected]) {
isEverythingOK = YES;
} else {
message = #"You need to select at least one card"
}
}
if (!isEverythingOK) {
UIAlertView *alert = [[UIAlertView alloc] initWith use the message here]
}
return isEverythingOK
}
We can sum up the questions, answers and comments. You can use these methods to do what you want :)
- (BOOL)userCanProceedToTurn
{
// Check Username
if (self.usernameLabel.text.length == 0)
{
[self showMessage: #"You are not signed in."];
return false;
}
// Check Energy
if (!(energyAmount > 0))
{
[self showMessage: #"You need energy before proceeding."];
return false;
}
// Check Cards
for (UIButton *button in pokemon_cards)
{
if ([button isSelected])
{
return true
}
}
[self showMessage: #"You need to select at least one card"];
return false;
}
- (void)showMessage:(NSString *)title
{
[[[UIAlertView alloc] initWithTitle: title message: nil delegate: nil cancelButtonTitle: #"OK" otherButtonTitles: nil] show];
}
If you need to know which buttons are selected then try
-(BOOL)userCanProceedToTurn
{
NSMutableArray *pokemon_cards_temp = [[NSMutableArray alloc] init];
for (UIButton *button in pokemon_cards)
{
if ([button isSelected])
{
[pokemon_cards_temp addObject:button];
}
}
// Do what you want with the selected buttons
if ([pokemon_cards_temp count] > 0)
{
return true;
}
else
{
return false;
}
}
I have aUITextField and below that 3 button. User can enter the amount inUITextField. And When user clicked the button belowUITextField it will add the amount inUITextField with amount written in button.
First button will add 100, Second will add 500 ad 3rd will add 1000. What is the best method to achieve it.
I try to implement following but got button.tag=0
/*Amount increment*/
- (IBAction)Btn_Incr100:(id)sender {
[self addTextFieldValue:nil];
}
- (IBAction)Btn_Incr500:(id)sender {
[self addTextFieldValue:nil];
}
- (IBAction)Btn_Incr1000:(id)sender {
[self addTextFieldValue:nil];
}
-(void)tagNumber{
Btn_Incr100.tag=1;
Btn_Incr500.tag=2;
Btn_Incr1000.tag=3;
}
-(void) addTextFieldValue:(UIButton*) button
{
int amount=[Txt_Amount.text intValue];
int AddAmount;
NSLog(#"button.tag%d",button.tag);
if (button.tag==1) {
AddAmount=100;
}
else if (button.tag==2) {
AddAmount=500;
}
else if(button.tag==3) {
AddAmount=1000;
}else{
AddAmount=0;
}
amount=amount+AddAmount;
Txt_Amount.text=[NSString stringWithFormat:#"%d",amount];
NSLog(#"%d",amount);
NSLog(#"%#",Txt_Amount.text);
}
Try using it by implementing tag
- (void)viewDidLoad {
[super viewDidLoad];
[self tagNumber];
}
-(void)tagNumber{
Btn_Incr100.tag=1;
Btn_Incr500.tag=2;
Btn_Incr1000.tag=3;
}
-(void) addTextFieldValue:(UIButton*) button
{
int amount=[Txt_Amount.text intValue];
int AddAmount;
int buttonTagIndex = button.tag;
switch (buttonTagIndex) {
case 1:
AddAmount=100;
break;
case 2:
AddAmount=500;
break;
case 3:
AddAmount=1000;
break;
default:
AddAmount=0;
break;
}
amount=amount+AddAmount;
Txt_Amount.text=[NSString stringWithFormat:#"%d",amount];
}
- (IBAction)btnAmountClick:(id)sender{
UIButton *button = sender;
[self addTextFieldValue:button];
}
With the help of this you can achieve:
-(void)displayvalue:(id)sender {
UIButton *resultButton = (UIButton *)sender;
NSLog(#" The button's title is %#." resultButton.currentTitle);
}
and now you can set this text inside your textfield.
Add this method as every button action.
-(void) addTextFieldValue:(UIButton*) button
{
NSString *string;
if ([button.text hasPrefix:#"+"] && [button.text length] > 1)
{
string = [string substringFromIndex:1];
}
int textFieldValue =[textField.text integerValue] + string.integerValue;
textField setText:[NSString stringWithFormat:#"%i",textFieldValue];
}
NSString *string;
if ([button.text hasPrefix:#"+"] && [button.text length] > 1)
{
string = [string substringFromIndex:1];
}
int textFieldValue =[textField.text integerValue] + string.integerValue;
textField setText:[NSString stringWithFormat:#"%i",textFieldValue];
or
connect separate actions
-(IBAction) onClick1: (id) sender]
{
NSLog(#"User clicked %#", sender);
textField.text = [textField.text integerValue] + 100
}
You can do this way -
The three buttons below your textfield, link them all to a common method say,
-(IBAction)addBtnClicked:(UIButton *)btn
Given tags to three buttons like for button 100,
btn1.tag=100..
then for button 500,
btn2.tag = 500..
then for button 1000, btn2.tag = 1000.
then in
-(IBAction)addBtnClicked:(UIButton *)btn
{
int value = -1;
if(btn.tag == 100)
{
value =[txtFldText.text intValue] + 100;
}
else if(btn.tag == 500)
{
value =[txtFldText.text intValue] + 500;
}
else if(btn.tag == 1000)
{
value =[txtFldText.text intValue] + 1000;
}
txtFldText.text = [NSString stringWithFormat:#"%d",value];
}
Simply you can do like this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_txtAmount.text=#"0";
_btn100.tag=100;
_btn500.tag=500;
_btn1000.tag=1000;
}
- (IBAction)btnAmountClick:(id)sender
{
UIButton *btn=sender;
if (btn.tag==100)
{
[self addAmount:#"100"];
}
if (btn.tag==500)
{
[self addAmount:#"500"];
}
if (btn.tag==1000)
{
[self addAmount:#"1000"];
}
}
-(void)addAmount:(NSString*)strAmount
{
int amount=[_txtAmount.text intValue]+[strAmount intValue];
_txtAmount.text=[NSString stringWithFormat:#"%d",amount];
}
First set tag to every button like 0,1,2.
then connect IBOutlet of all button to single IBAction and
implement the IBAction as below.
- (IBAction)btnClicked:(id)sender
{
switch ([sender tag]) {
case 0:
{
NSString *s=self.txtField.text;
int add=[s intValue]+ 100;
self.txtField.text=[NSString stringWithFormat:#"%d",add];
break;
}
case 1:
{
NSString *s=self.txtField.text;
int add=[s intValue]+ 200;
self.txtField.text=[NSString stringWithFormat:#"%d",add];
}
break;
case 2:
{
NSString *s=self.txtField.text;
int add=[s intValue]+ 500;
self.txtField.text=[NSString stringWithFormat:#"%d",add];
}
break;
default:
break;
}
}
There's a better solution for this.
You can use tags smth differently, as they are.
Just tie your three buttons as actions to this method and set the tags to 100/500/1000
- (void)buttonAction:(id)sender {
textField.text = [NSString stringWithFormat:#"%li", (long)(textfield.text.intValue + sender.tag)];
}
In my app I have a view with 3 textfields. Until they are ALL filled out, I want my button to be disabled. In the storyboard I left it enabled, but then in my ViewDidLoad I added this:
if (amtYouOwe.text.length == 0 && personYouOweMoney.text.length == 0 && self.cellNum.text.length == 0)
{
saveButton.enabled = NO;
}
Even if all the fields were filled out my button was disabled..So then I tried to put it in my IBAction save button. This time I got the opposite result. No matter what my button was enabled. Even if all the textfields were blank. I believe my code is correct, I'm just trying to find the perfect place to put it....
All help is appreciated, thanks in advance.
Write a function to enable/disable saveButton as
-(void)enableDisableSave{
if (amtYouOwe.text.length == 0 && personYouOweMoney.text.length == 0 && self.cellNum.text.length == 0){
saveButton.enabled = NO;
} else {
saveButton.enabled = YES;
}
}
Write a delegate method of textField, assign your view controller as delegate to all textfield, write following delegate method and call that method in that and bingo..
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self enableDisableSave];
}
You should add button enable/disable control in UITextField delegate methods. Every time a text field being edited, check all the text fields' length and change button status.
https://developer.apple.com/library/ios/documentation/uikit/reference/UITextFieldDelegate_Protocol/UITextFieldDelegate/UITextFieldDelegate.html
If you want real time checking, use this:
- (void)viewDidLoad {
[super viewDidLoad];
// ..
// your code
// ..
[self addTextChangedEvent];
[self toggleSaveButtonStatus];
}
- (void)addTextChangedEvent {
[amtYouOwe addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[personYouOweMoney addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[self.cellNum addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
- (void)textFieldDidChange:(UITextField *)textField {
[self toggleSaveButtonStatus];
}
- (void)toggleSaveButtonStatus {
int youOweLength = amtYouOwe.text.length;
int personYouOweLength = personYouOweMoney.text.length;
int cellNumLength = self.cellNum.text.length;
saveButton.enabled = youOweLength > 0 && personYouOweLength > 0 && cellNumLength > 0;
}
Write in .pch file
This macro does not allow white space..
#define allTrim( object ) [object stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet] ]
then write delegate method
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self enableDisableSave];
}
Write a function to enable/disable saveButton as
-(void)enableDisableSave{
if ([allTrim(amtYouOwe.text) length] == 0 && [allTrim(personYouOweMoney.text) length] == 0 && [allTrim(self.cellNum.text) length] == 0){
saveButton.enabled = NO;
} else {
saveButton.enabled = YES;
}
}
Happy Coding!!!
This happens only on device and not on simulator..
I have two custom views and both have a UITextField in them(Child1 and Child2).
Both these views are placed on another UIView (Say viewA).
Now my requirement is such that when text is entered in one of the textfield I need to clear the other text fields content, so in the textFieldDidChange: method I inform viewA and than it iterates over its subview finds Child1 and sets its properties. But as soon as I access the textField of this Child1 to enable its userInteraction or and set its text to nil. This textfield now becomes the first responder.
I am not really sure why it does that. You can look at the below code to get more info.
Method inside viewA:
for (UIView *view in [self subviews])
{
if ([view isKindOfClass:[ChildView class]])
{
ChildView *concernedCustomerView = (ChildView *)view;
if (concernedCustomerView.typeOfCompartment == CompartmentTypeNone)
{
[concernedCustomerView.checkBoxButton setSelected:NO];
[concernedCustomerView.countSwitch setOn:NO animated:YES];
concernedCustomerView.countTextField.userInteractionEnabled = YES;
concernedCustomerView.countTextField.alpha = 1.0f;
concernedCustomerView.countTextField.text = nil;
}
}
}
Method inside custom Child View
-(void)textFieldDidChange:(id)sender
{
NSString *note = _countTextField.text;
note = [note stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
//If we are checking note for nil it should be before calling trimming white space
if (note && note.length > 0)
{
[_checkBoxButton setSelected:YES];
if (note.length == 3 && note.integerValue == 999)
{
[_countSwitch setOn:YES animated:YES];
_countTextField.userInteractionEnabled = NO;
_countTextField.alpha = 0.5f;
_countTextField.text = nil;
// [_countSwitch sendActionsForControlEvents:UIControlEventValueChanged];
}
}
else
{
[_checkBoxButton setSelected:NO];
}
if ([self.delegate conformsToProtocol:#protocol(ChildViewDelegate)] &&
[self.delegate respondsToSelector:#selector(adjustStateOfOtherControls:andCheckBoxStatus:)])
{
[self.delegate adjustStateOfOtherControls:_typeOfCompartment andCheckBoxStatus:_checkBoxButton.selected];
}
}
Do not set the view object to nil because they are alive and your controller is still active,
try to set _countTextField.text = #""; so that your textfield become empty.
Just a suggest:
1) Instead of manual iterate subviews you can assign tag to child views and uitextfields e.g:
child1View.tag = 100;
child2View.tag = 200;
...
textField1.tag = 10;
textField2.tag = 20;
then get child references from parent viewA by:
UIView *child1View = [viewA viewWithTag:100];
UIView *child2View = [viewA viewWithTag:200];
2) Set child views textfield delegate to a common viewcontroller
3) Handle one single
- (void)textFieldDidBeginEditing:(UITextField *)textField
4) Iniside this method check
if(textField.tag==10)
{
do stuff
}
else if(textfield.tag==20)
{
do other stuff
}
Hope it helps !
I made a UIAlertView subclass. It implements two UIAlertViewDelegate methods: alertView:clickedButtonAtIndex: is called when expected, however alertViewShouldEnableFirstOtherButton: is never called.
Had a look at related posts and added:
[textField sendActionsForControlEvents:UIControlEventEditingChanged];
but without results. I seem to be out of options. What am I missing here?
#implementation MyAlertView
+ (MyAlertView*)showAlertForJson:(NSDictionary*)json delegate:(id<F2WebAlertDelegate>)delegate
{
MyAlertView* view = [[MyAlertView alloc] initWithJson:json delegate:delegate];
return view;
}
- (instancetype)initWithJson:(NSDictionary*)json delegate:(id<MyWebAlertDelegate>)delegate
{
if (self = [super initWithTitle:json[#"title"]
message:json[#"message"]
delegate:self
cancelButtonTitle:json[#"cancelTitle"]
otherButtonTitles:nil])
{
_json = json;
self.webDelegate = delegate;
for (NSString* title in json[#"otherTitles"])
{
[self addButtonWithTitle:title];
}
[self initInput:json[#"input"]];
[self show];
}
return self;
}
- (void)initInput:(NSDictionary*)json
{
if (json == nil)
{
return;
}
[json[#"style"] isEqualToString:#"plain"] ? self.alertViewStyle = UIAlertViewStylePlainTextInput : 0;
...
[self initTextField:[self textFieldAtIndex:0] withJson:json[#"field0"]];
if (self.alertViewStyle == UIAlertViewStyleLoginAndPasswordInput)
{
[self initTextField:[self textFieldAtIndex:1] withJson:json[#"field1"]];
}
}
- (void)initTextField:(UITextField*)textField withJson:(NSDictionary*)json
{
[textField sendActionsForControlEvents:UIControlEventEditingChanged];
if (textField == nil || json == nil)
{
return;
}
[json[#"keyboard"] isEqualToString:#"ascii"] ? (textField.keyboardType = UIKeyboardTypeASCIICapable) : 0;
...
}
#pragma mark - Alert View Delegate
- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
...
}
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView*)alertView
{
return YES;
}
#end
Turns out that, as the delegate method's name suggest, you must specify a list otherButtonTitles to UIAlertView's initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:.
So, buttons added later with addButtonWithTitle: do not count.