I'm building up a view controller where I'm adding a bunch of UITextField
s to my view programmatically. I want to pre-populate the textfields with some text that I'm loading from CoreData, but allow the user to go in and change that text if they want. I then need to go back and save that new text back to CoreData again, as well as perform calculations using the new value. The saving and loading itself isn't a problem, but I'm looking for a good way to track what the user has changed.
In the past, I've done this with the .tag
property of the UITextField
and the textFieldDidEndEditing:
delegate method. The problem is I'm going to have dozens of textfields in this view - enough so that I can't really remember what .tag
belongs to what variable in my model. Having to go back to look up what tag I assigned when I created it is going to be a pain, and the whole thing feels really error prone. Additionally, I'm going to be creating the textfields in multiple different loops, so getting a unique tag for each textfield when I create it will be difficult (maybe impossible?).
What I'm looking for is a way to "tie" the UITextField to a variable in my model when I create it and just know from that point on, whenever the user updates that textfield, the value I specify in my model will be instantly updated to be used in future calculations, and saving back to CoreData when the user leaves the screen. Is there a way to do this?
EDIT 1: Another thing to note is that my model itself is kinda complex. I have several different custom NSObject
subclasses that are all holding different pieces of my data. Some of the objects have properties that are themselves other custom objects. I also have dictionaries where the values will be instances of some of the custom objects.
As an example, I might have these objects:
MySmallObject1
, which has propertiesname
,size
, andvalue
(allNSStrings
)MySmallObject2
, which has propertiesdate
andcost
(anNSDate
and anNSNumber
)MyBigObject
, which has propertiesbox1
andbox2
(which areMySmallObject1
andMySmallObject2
instances, respectively)theDict
, which is anNSDictionary
instance that hasMyBigObject
s as values
So when I am building my textfields, I might actually be going down a tree that would look something like:
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 update the appropriate value through the tree.
EDIT 2: While trying to implement @Till's suggestion in the comments, I started reading a tutorial on pointers in C. It seems like this is the way I need to go, but I'm struggling to get the &
and *
from the C world to play nicely with the NSObject subclasses I mention in my last EDIT from the Objective-C world. To get @Till's suggestion to work, I think I need to somehow store the memory location of my value up that complicated object tree. So my code block from the last EDIT would become:
.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
that I'm incrementing every time I put something into tagDict
so I can keep it unique. I guess in this case I could just simply be using an NSArray
and it would work just as well, and probably look 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 throws an error for Assigning to 'id' from incompatible type 'NSString *'
. I'm not really sure what that means. Is there a way I can use a "dereferencing operator" to change the value of the item I am pointing to in my dictionary?
throws an error Address expression must be an lvalue or a function designator
. I'm still a little fuzzy on how this pointer stuff would work in the Objective C world, and I think there's something I'm not fully understanding.
You could use your original approach with the .tag , but use typedefs.
You can use UITextField+blocks it may be less entangled. And it will be much easier if objects have similar interface or implement one protocol with method like
setText:
.I ended up doing largely what I think @Till was trying to suggest. I added an
NSMutableArray *tieArray
and anint tieCounter
as@properties
to my view controller. My code for building theUITextFields
from the first code block in my OP is now:Then in my
textFieldDidEndEditing
Note that for this to work, all the properties of
MySmallObject1
andMySmallObject2
need to be covered by aisKindOfClass:
check, and the actual syntax will vary a little depending on what class that property is.I haven't been able to test this yet (and my app probably won't be in a testable state for quite some time), but it makes sense to me, and it's not throwing any errors or warnings. If I have problems with it when I actually run it I'll update here.