Is there a way to automatically bind a UITextField to a variable in my data model?

I create a view controller where I add a bunch of UITextField to my view programmatically. I want to prefill the text fields with the text that I download from CoreData, but allow the user to enter and change this text if he wants. Then I need to go back and save this new text back to CoreData, and also perform calculations using the new value. Saving and loading on their own is not a problem, but I'm looking for a good way to track user changes.

In the past, I did this using the .tag property of the delegate method UITextField and textFieldDidEndEditing: The problem is that in this view I will have many text fields - enough so that I canโ€™t remember that .tag belongs to which variable in my model. To go back to find out which tag I assigned when I created it, it will be a pain, and it all seems really error prone. In addition, I am going to create text fields in several different loops, so getting a unique tag for each text field when creating it will be difficult (perhaps impossible?).

What I'm looking for is a way to bind a UITextField to a variable in my model, when I create it and just know from that point, when the user updates this text box, the value specified in my model will be instantly updated for use in future calculations and save back to CoreData when the user leaves the screen. Is there any way to do this?

EDIT 1: Another thing that should be noted is that my model itself is complex. I have several different custom subclasses of NSObject that contain different parts of my data. Some objects have properties that are themselves other custom objects. I also have dictionaries where the values โ€‹โ€‹will be instances of some user objects.

As an example, I can have the following objects:

  • MySmallObject1 , which has the properties name , size and value (all NSStrings )
  • MySmallObject2 , which has date and cost properties (an NSDate and a NSNumber )
  • MyBigObject , which has box1 and box2 properties (which are instances of MySmallObject1 and MySmallObject2 respectively)
  • theDict , which is an instance of NSDictionary , which has MyBigObject as values

Therefore, when I create my text fields, I could go down the tree, which would look something like this:

 for (NSString *key in [theDict allKeys]) { UITextField *txt = [[UITextField alloc] initWithFrame:...]; txt.text = [[(MyBigObject *)[theDict objectForKey:key] box1] name]; [self.view addSubview:txt]; } 

In this case, I need to know when the user changes the text in any of the txt and updates the corresponding value through the tree.

EDIT 2: When trying to implement @ In addition to the comments, I started reading the guide on pointers in C. It seems like this is the way I need to go, but I try my best to get & and * from the C world to play well with the NSObject subclasses that I mention in my last EDIT from the Objective-C world To get @ Think about how to work, I think I need to somehow save the memory cell of my value in this complex tree of objects. So my block of code from the last EDIT would look like this:

 .h @property (nonatomic, strong) NSMutableDictionary *tagDict; .m for (NSString *key in [theDict allKeys]) { UITextField *txt = [[UITextField alloc] initWithFrame:...]; txt.text = [[(MyBigObject *)[theDict objectForKey:key] box1] name]; txt.tag = globalCounter; [tagDict addObject:[[(MyBigObject *)[theDict objectForKey:key] box1] name] forKey:[NSString stringWithFormat:@"%d",globalCounter]]; globalCounter ++; [self.view addSubview:txt]; } 

Where globalCounter is an int , which I increment every time I put something in a tagDict , so I can keep it unique. I think in this case I could just use NSArray and it will work just as well, and probably looks a little cleaner. My plan is to use tagDict in my testFieldDidEndEditing like:

 - (void) textFieldDidEndEditing:(UITextField *)textField { *[tagDict objectForKey:[NSString stringWithFormat:@"%d",textField.tag]] = textField.txt; } 

This causes an error for Assigning to 'id' from incompatible type 'NSString *' . I'm not quite sure what that means. Is there a way that I can use the "dereference operator" to change the value of the element that I point to in my dictionary?

throws an error Address expression must be an lvalue or a function designator . I'm still a little vague in how this pointer material will work in the Objective-C world, and I think there is something I donโ€™t quite understand.

+4
source share
3 answers

I ended up doing basically what I think @Till was trying to suggest. I added NSMutableArray *tieArray and int tieCounter as @properties to my view controller. My code to create UITextFields from the first block of code in my OP is now:

 for (NSString *key in [theDict allKeys]) { UITextField *txt = [[UITextField alloc] initWithFrame:...]; txt.text = [[(MyBigObject *)[theDict objectForKey:key] box1] name]; [self.view addSubview:txt]; txt.tag = self.tieCounter; self.tieCounter ++; [self.tieArray addObject:[[(MyBigObject *)[theDict objectForKey:key] box1] name]]; } 

Then in my textFieldDidEndEditing

 - (void) textFieldDidEndEditing:(UITextField *)textField { if ([tieArray objectAtIndex:textField.tag] isKindOfClass:[NSMutableString class]]) { [(NSMutableString *)[tieArray objectAtIndex:textField.tag] setString:textField.text]; } else if (//...More conditions to set other variable types with the syntax as needed) } 

Note that for this, all properties of MySmallObject1 and MySmallObject2 should be covered by the isKindOfClass: check, and the actual syntax will be slightly different depending on which class this property is.

I have not been able to verify this yet (and my application will probably not be in a test state for some time), but it makes sense to me and it does not throw any errors or warnings. If I have problems with this when I run it, I will update here.

0
source

You can use UITextField + blocks , it can be less confusing. And it will be much simpler if the objects have a similar interface or implement one protocol using a method like setText:

0
source

You can use your original approach with .tag, but use typedefs.

 typedef enum TFTypes { TFModalType1, TFModalType2, TFModalType3, TFModalType4, TFModalType5, ... } TFType; 
0
source

Source: https://habr.com/ru/post/1495561/


All Articles