disabling rows on condition on xlform - objective-c

I have a section set up using xlform where there is a bool field and then a text field under it. I want to disable the text field if the bool is chosen. I tried with the following, but it did not work
if ([[self.form valueForKey:#"pay"] isEqualToValue:#(1)]){
row.disabled = YES;
} else {
row.required = YES;
}
[section addFormRow:row];
Any suggestions? Here is the documentation, spent a bunch of time search without being able to find the answer.
Edit: I'm starting to think that the values of the dictionary don't get updated dynamically. Which is odd because the dictionary can be accessed in other parts of the view controller at any time.
Edit
- (void)formRowDescriptorValueHasChangedTwo:(XLFormRowDescriptor *)formRow value:(id)value
{
[value isEqual:[self.formValues valueForKey:#"pay"]];
if([formRow.tag isEqualToString:#"pay"] && [value isEqual:[NSNumber numberWithBool:YES]])
{
self.price.disabled = NO;
self.price.required = YES;
[self.tableView reloadRowsAtIndexPaths:#[[self.form indexPathOfFormRow:self.price]]
withRowAnimation:UITableViewRowAnimationNone];
}
}
I got it working, but I have a new problem.
Here is the code
- (void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue
{
if([formRow.tag isEqualToString:#"now"] && [newValue isEqual:[NSNumber numberWithBool:YES]])
{
self.time.disabled = YES;
[self.tableView reloadRowsAtIndexPaths:#[[self.form indexPathOfFormRow:self.time]]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
if([formRow.tag isEqualToString:#"pay"] && [newValue isEqual:[NSNumber numberWithBool:YES]])
{
self.price.disabled = NO;
self.price.required = YES;
[self.tableView reloadRowsAtIndexPaths:#[[self.form indexPathOfFormRow:self.price]]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
When I click on the first row (now), it disables the corresponding row fine, but when I click on the second row (pay), it makes the row that now disables reappear AND it makes the row that I want to appear under pay to disappear.
Edit Got it to work by changing the animation to UITableViewRowAnimationNone

Here's how you can do it using XLFormDescriptorDelegate. This will change the state as the field is toggled on the form.
#interface SomeClass() <XLFormDescriptorDelegate>
#end
#implementation SomeClass
- (void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue
{
if([formRow.tag isEqualToString:#"boolFieldTag"] && [newValue boolValue])
{
self.otherRow.disabled = YES;
[self.tableView reloadRowsAtIndexPaths:#[[self.form indexPathOfFormRow:self.otherRow]]
withRowAnimation:UITableViewRowAnimationNone];
}
}
#end
In this example I set the row as a property when I originally added it to the form. Alternatively you can just use: [self.form formRowWithTag:#"someTag"]

Related

Enable button only after text fields are entered

Apple started rejecting my app (after yrs of approvals) - I have 2 text fields for a few char of last, first name, then press Go. They claim - I can't reproduce that the app crashes if you just press Go - when I run it it tries to send the request but kicks it back with an error 'No name - please reenter'. Whatever. To appease Apple I edited my text field entry method to now (see below) - but still not working. Please advise.
// Text Field methods
-(void)textFieldDidBeginEditing:(UITextField *)textField {
UIFont* boldFont = [UIFont fontWithName:#"Helvetica-Bold" size: 24];
if (textField == _lastName) {
[_lastName becomeFirstResponder];
[_lastName setFont:boldFont];
_lastName.autocorrectionType = UITextAutocorrectionTypeNo;;
_lastName.autocapitalizationType = UITextAutocapitalizationTypeNone;
_lastName.text = #"";
NSLog(#"user editing last...");
// Enable button once fields have values
if ([_lastName.text isEqual: #""] && [_firstName.text isEqual: #""])
{
_fwdButton.enabled = YES;
}
}
else {
[_firstName becomeFirstResponder];
[_firstName setFont:boldFont];
_firstName.autocorrectionType = UITextAutocorrectionTypeNo;;
_firstName.autocapitalizationType = UITextAutocapitalizationTypeNone;
_firstName.text = #"";
NSLog(#"user editing first");
// Enable button once fields have values
if ([_lastName.text isEqual: #""] && [_firstName.text isEqual: #""])
{
_fwdButton.enabled = YES;
}
}
}
I was able to add code similar to[https://stackoverflow.com/questions/2859821/disable-button-until-text-fields-have-been-entered][1]
to deal with the situation.

How to enable an UISwitch while disabling other two simultaneously

I have 3 UISwitches in my View Controller and each one identifies gender ("Woman, Male, No Gender") that user has to choose.
The behavior I would like to implement is that, when the user taps on a switch to select the gender, the others two have to be disabled simultaneously.
And once the switch is selected, the "Create Profile" button is activated. (screenshots attached)
I'm not able to implement the if condition (or alternatively a switch condition)to do that.
Please help.
This is the code I implemented:
- (IBAction)checkGenderSwitch:(id)sender {
if ([self.femaleGenderSwitch isOn] && (![self.maleGenderSwitch isOn] && ![self.noGenderSwitch isOn])) {
[self enableCreateProfileButton];
} else if ([self.maleGenderSwitch isOn] && (![self.femaleGenderSwitch isOn] && ![self.noGenderSwitch isOn])) {
[self enableCreateProfileButton];
} else if ([self.noGenderSwitch isOn] && (![self.femaleGenderSwitch isOn] && ![self.maleGenderSwitch isOn])) {
[self enableCreateProfileButton];
}else{
[self disableCreateProfileButton];
}
}
Thanks.
Let's reformulate your need:
Every time you change a switch value, you do (in pseudo code):
if (male isOn && female isOff && noGender isOff) { enableButton }
else if (male isOff && female isOn && noGender isOff) { enableButton}
else if (male isOff && female isOff && noGender isOn)) { enableButton }
else { disableButton }
Which could be translated in:
if (onlyMale isOn) { enableButton }
else if (onlyFemale isOn) { enableButton}
else if (onlyNoGender isOn) { enableButton }
else { disableButton }
only... isOn? What about putting them into a NSArray, and count the number of isOn? That should do the trick no? only... isOn is for count = 0 where isOn == true.
Which could be then coded:
NSArray *switches = #[self.femaleGenderSwitch, self.maleGenderSwitch, self.noGenderSwitch];
NSUInteger numberOfSwitchesOn = 0;
for (UISwitch *aSwitch in switches) {
if ([aSwitch isOn]) {
numberOfSwitchesOn += 1;
}
}
if (numberOfSwitchesOn == 1) {
[self enableCreateProfileButton];
} else {
[self disableCreateProfileButton];
}
Now, if you want that when you switchOn a Switch, you switchOff the other two (if needed), which in our case would only be one, it can be done with:
NSArray *switches = #[self.femaleGenderSwitch, self.maleGenderSwitch, self.noGenderSwitch];
BOOL hasASwitchOn = NO;
//We iterate over the switches
for (UISwitch *aSwitch in switches) {
//If the newly set is set to on, we need to disable the other (the one which aren't the same as the sender)
if ((UISwitch *)[sender isOn] && ![sender isEqual:aSwitch]) {
[aSwitch setOn:NO];
hasASwitchOn = YES;
} else if ([aSwitch isOn]) { //Else, we still count the number of isOn
hasASwitchOn = YES;
}
}
if (hasASwitchOn) {
[self enableCreateProfileButton];
} else {
[self disableCreateProfileButton];
}
As has been said in a comment, the disablement requirement is wrong. All three switches need to be enabled at all times. The rule should be simply that only one switch at a time can be on.
The key here is that when you tap a switch, and the IBAction method is called, the sender is that switch.
So just keep a list references to all three switches and if the sender is now on, cycle thru the list and turn off each switch that is not the sender.
(Or use a three part UISegmentedControl which does all this for you automatically; it lets the user choose exactly one of three.)
I would use the tag property of UIView for your three switches, giving each of the three its own, unique tag value: for instance, 0, 1, 2. (You can set that up in Interface Builder, or in code — whichever you prefer.) This makes it trivial to distinguish the three switches.
Then, create one extra property, genders, an array of your three switches:
#interface ViewController ()
#property (nonatomic, weak) IBOutlet UISwitch* femaleGenderSwitch;
#property (nonatomic, weak) IBOutlet UISwitch* maleGenderSwitch;
#property (nonatomic, weak) IBOutlet UISwitch* noGenderSwitch;
#property (nonatomic, strong) NSArray* genders;
#end
In -viewDidLoad set that up:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.genders =
#[[self femaleGenderSwitch], [self maleGenderSwitch], [self noGenderSwitch]];
}
Finally, you can simplify your logic in your action method like this:
- (IBAction)checkGenderSwitch:(id)sender
{
if ([sender isOn]) {
for (id aSwitch in [self genders]) {
if ([aSwitch tag] != [sender tag]) {
[aSwitch setOn:NO animated:YES];
}
}
[self enableCreateProfileButton];
}
else {
[self disableCreateProfileButton];
}
}

View based NSTableView text field loses focus

After selecting a row, pressing TAB will normally move focus to the next editable text field(s), or in my case, column. However, I have noticed that only sometimes after pressing TAB, the row will turn gray and focus is lost. I must then click on the text field to continue. After days of observations, I still cannot detect a repeatable pattern, and internet searches have not given me enough information. I wonder if my delegate might be the culprit. Some questions come to mind.
Can anyone shed light on why or how focus might be lost? (indirectly answered by Willeke's comment to remove some code)
Is there a proper or deliberate way to force focus on the next text field? (now irrelevant)
The unpredictable behavior prompting the first two questions seemed to be interference between two methods. Resolving one has at least led to predictable behavior and this related question. I included more code below with another method. One column contains a pop-up button. Ordinarily, choosing a value does not select the row (ie, highlighted). I would like the row to be selected and focused (ie, highlighted blue) so that I can immediately TAB to the next field and begin editing. In editPopUps:, selectRowIndexes: will select the row, but the row is gray instead of blue and hence TAB has no effect. How do I select and focus the row?
Update: Here are some pertinent methods. I setup each view based on column because only some columns are editable.
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSString *column = tableColumn.identifier;
NSTableCellView *value = [tableView makeViewWithIdentifier:column owner:self];
Foo *foo = self.foos[row];
[self setupView:value column:column foo:foo];
[self populateCellView:value column:column foo:foo object:[foo valueForKey:column]];
return value;
}
- (void)setupCellView:(NSTableCellView *)view column:(NSString *)column foo:(Foo *)foo {
view.textField.alignment = NSRightTextAlignment;
BOOL editable = YES;
if ([attribute isEqualToString:column] || ![foo.x isEqualToNumber:bar.x]) {
editable = NO;
} else {
view.textField.target = self;
view.textField.action = #selector(editTextField:);
}
[view.textField setEditable:editable];
}
- (void)editTextField:(NSTextField *)sender {
if ([self.fooTableView numberOfSelectedRows]) {
NSTableCellView *cellView = (NSTableCellView *)[sender superview];
NSString *column = cellView.identifier;
NSInteger row = [self.fooTableView rowForView:cellView];
Foo *foo = self.foos[row];
NSNumber *amountNumber = [NSNumber numberWithShort:[sender intValue]];
if ([attribute isEqualToString:column]) {
foo.y = amountNumber;
} else {
foo.z = amountNumber;
}
[self.fooTableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:row] columnIndexes:[NSIndexSet indexSetWithIndex:[self.fooTableView columnWithIdentifier:column]]];
[self refreshSelection];
}
}
- (void)editPopUps:(NSPopUpButton *)sender {
GlorpView *glorpView = (GlorpView *)[sender superview];
NSString *column = pokeCellView.identifier;
NSInteger row = [self.fooTableView rowForView:glorpView];
Foo *foo = self.foos[row];
NSNumber *indexValue = [NSNumber numberWithShort:[sender indexOfSelectedItem]];
NSMutableIndexSet *columnIndexes = [NSMutableIndexSet indexSetWithIndex:[self.fooTableView columnWithIdentifier:column]];
if ([attribute isEqualToString:column]) {
foo.Z = indexValue;
}
[self.fooTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
[self refreshSelection];
}

Saving data entered in textfield of custom cells

I have a custom cell.In that custom cell,there is a textfield and a label.I actually have made a form using custom cell.The user will enter its name,city,state,country,dob detail in that textfield.Now ,on a click of a button I want to save all this data together in a dictionary.But I am not able to understand that how can I save data for different keys using the same textfield as it is being reused.Please help with some code in objective c.Thanks in advance!
I am giving you idea, so that you can implement it
Just take a Macro, like this #define textFieldTag 1000
You are re-using the textfField right. So, in cellForRowAtIndexPath (if u r using table view) set the tag of the textField like this: cell.textType.tag = indexPath.row + textFieldTag;
then
access the textField using delegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
switch (textField.tag - textFieldTag) {
case 0:{
NSString *name = textField.text;
//Take a NSMutableDictionary and add the values over here
break;
}
case 1:
{
NSString *city = textField.text;
break;
}
case 2:
{
NSString *state = textField.text;
break;
}
case 3:
{
NSString *country = textField.text;
break;
}
case 4:
{
NSString *dob = textField.text;
break;
}
default:
break;
}
}
And one thing, when you click the save or what ever button u r using to submit the form remember to dismiss the keyboard [textField resignFirstResponder];, otherwise the last value can be nil.
Thank you

Anyway to make a (wrapping) NSTextField write a carriage return upon pressing return key?

I want to use a wrapping text field that can potentially contain carriage returns in my app. Is there any way to force the NSTextField object to write a carriage return into the text area instead of sending its action to its target when the Return key is pressed?
This is covered in Technical Q&A QA1454, which also enumerates reasons why one would use NSTextField instead of NSTextView in this case.
You can implement the following method in the text field delegate:
- (BOOL)control:(NSControl*)control
textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector
{
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver
// to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
return result;
}
Okay, I figured out one way to do it, but this very well may not be the best (or even a good) way. I subclassed NSTextField, and overrode -textShouldEndEditing: like so:
-(BOOL)textShouldEndEditing:(NSText *)textObject {
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36) {
[self setStringValue:[[self stringValue] stringByAppendingString:#"\n"]];
return NO;
}
else {
return [super textShouldEndEditing:textObject];
}
}
I found a combination of Sean and Bevarious worked best for me. Sean's answer assumes that the new line is always wanted to be added to the end (instead of for instance where the user's cursor is placed).
-(BOOL)textShouldEndEditing:(NSText *)textObject
{
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36)
{
[textObject insertNewlineIgnoringFieldEditor:nil];
return NO;
}
else
{
return [super textShouldEndEditing:textObject];
}
}
Swift version:
override func textShouldEndEditing(textObject: NSText) -> Bool {
let event = NSApplication.sharedApplication().currentEvent
if event?.type == NSEventType.KeyDown && event?.keyCode == 36 {
self.stringValue = self.stringValue.stringByAppendingString("\n")
return false
} else {
return super.textShouldEndEditing(textObject)
}
}