Build an array with input from tableviewcell UITextfield - objective-c

I have a tableview with dynamical amount of cells, based on an array who is build up by a JSON.
tableData = [NSJSONSerialization JSONObjectWithData:dataWebService options:kNilOptions error:&error];
This little line gets all the data I need to build my tableview.
In the tableview I have a textfield in every cell with predefined text from my JSON. The user can change this value before he proceed to the next segue (storyboard).
The question is how I can store this usertyped values in an array an replace the excisting values in my tableData array before I send this arraobject to the next segue.
And keep in mind that the user input must not change when the user scroll in the tableview.
Maybe this is basic, but I cannot figure a nice way to do this.
This is an example of the JSON.
(
{
defaultval = 8;
dtype = long;
id = "#franr";
label = "Faktura nr.";
},
{
defaultval = 8;
dtype = long;
id = "#tilnr";
label = "Til faktura";
}
The tableview look like this:
(I cannot post images)
Fra faktura 8
Til faktura 8
The user can change the values (number 8) to any numbers. So I need to replace the numbers of defaultVal in my JSON array.
Thanks.

Since its a UITEXTFIELD, you can use OnTextChange Event
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
or
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
, or lose focus
- (BOOL)textFieldShouldReturn:(UITextField *)textField
please keep in mind you need to set the delegate of the textfield to self & add the respective protocals.

In order to keep the data after scrolling, you should modify the data source (the array) after each input from the user. You can do dis in the UITextFieldDelegate's textFieldDidEndEditing: method (of course, you have to be the delegate for each text field: textField.delegate = self;).
You should also implement a UIScrollViewDelegate method, for dismissing the keyboard when the table begins to scroll (e.g. scrollViewWillBeginDragging:).
EDIT:
Your array must be containing NSDictionaries, which are not mutable, so I recommend, you create your own class having those attributes (defaultval, dtype ...), so you can edit the data source. I don't think you can modify your array the way it is now.
The code with custom object would be something like:
- (void)textFieldDidEndEditing:(UITextField *)textField {
UITableViewCell *cell = textField.superview.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
MyObject *currentObject = [tableData objectAtIndex:indexPath.row];
currentObject.defaultVal = textField.text.intValue;
}

Related

Cocoa Bindings - NSTableView - Swapping Values

Is an NSValueTransform subclass a good choice for displaying Core Data attributes into UI views displaying:
A number string like (0,1,2,3,etc) into a string such as (Pending, Completed, Frozen, In progress, etc)
A number string like (0,1) into a app-based image (red.png if 0, green.png if 1)
Here's what Core Data displays for the two attributes, timer and status:
Here is what I want to be displayed instead, without changing the values in Core Data:
If not to use NSValueTransformer, in what other way is this possible?
I do not want to see the data permanently converted, only for the benefit of less data stored in Core Data and better UI view items.
I have also tried to modify the attributes in the managed object class (with out KVO notification) with no luck.
Yes, NSValueTransformer subclasses work just fine for this purpose.
You can also add read-only computed properties to your managed object class, and that should work, too. Those properties can even be added by a category in the controller code, if they don't make sense as part of the model code.
For example:
+ (NSSet*) keyPathsForValuesAffectingStatusDisplayName
{
return [NSSet setWithObject:#"status"];
}
- (NSString*) statusDisplayName
{
NSString* status = self.status;
if ([status isEqualToString:#"0"])
return #"Pending";
else if ([status isEqualToString:#"1"])
return #"Completed";
// ...
}
The +keyPathsForValuesAffectingStatusDisplayName method lets KVO and bindings know that when status changes, so does this new statusDisplayName property. See the docs for +keyPathsForValuesAffectingValueForKey: to learn how that works.
I ended up using what at first appeared to be blocking the display of different info in those cells, using:
#pragma mark - Table View Delegate
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
/* tableColumn = (string) #"AutomaticTableColumnIdentifier.0"
row = (int) 0 */
NSString *identifier = [tableColumn identifier];
NSTableCellView *cellView = [tableView makeViewWithIdentifier:identifier owner:self];
NSManagedObject *item = [self.itemArrayController.arrangedObjects objectAtIndex:row];
if ([identifier isEqualToString:#"AutomaticTableColumnIdentifier.0"]) {
/* subviews returns array with 0 = Image View &
1 = Text Field */
/* First, set the correct timer image */
... logic ...
NSImageView *theImage = (NSImageView *)[[cellView subviews] objectAtIndex:0];
theImage.image = [NSImage imageNamed:#"green.gif"];
/* Second, display the desired status */
NSTextField *theTextField = (NSTextField *)[[result subviews] objectAtIndex:1];
... logic ...
theTextField.stringValue = #"Pending";
}
return cellView;
}
Apple's documentation states (somewhere) that bindings with an Array Controller can work in combination with manually populating the table view cells. It seems best and easiest to start with bindings and then refine display values manually.

Populate NSTableView with unknown number of columns

I have a NSTableview and a button. NSTableview has a unknown number of columns.
The first column has a image well and 2 text boxes, the others (again, unknown number) are normal textbox columns.
The button opens up a file open dialogue. Once I choose the files (.jpg) I would like to process them.
So far everything is made (chose files, columns, etc..) what is missing is the populating of the table:
I have the loop that goes through all the selected files. What is the best way to do this:
display the image in the image well of the first cell,
type the filename in the first textbox of the first cell,
type the filepath in the second cell of the textbox,
type "YES" in all other columns.
My difficulty is that I have no idea how many columns will be there since it depends from the user. The number of columns will not change during Runtime. they are set up at startup based on the configuration. if the configuration is changed then the app should be reloaded.
I am a beginner in Objective-C/Cocoa programming.
EDIT:
additional info as requested:
It is a view based NSTableView
each column represents an action that has to be taken in a later moment on an image. the program user can decide what and how many actions to take, thats the reason for a unknown amount of columns in the table view.
You can add columns programmatically using addTableColumn:. This method takes an NSTableColumn instance that you can create in your code.
The rest of your architecture (displaying images, etc.) does not particularly change from "normal" code just because the columns have been added dynamically.
Here is a snippet that should get you started:
NSTableColumn* tc = [[NSTableColumn alloc] init];
NSString *columnIdentifier = #"NewCol"; // Make a distinct one for each column
NSString *columnHeader = #"New Column"; // Or whatever you want to show the user
[[tc headerCell ] setStringValue: columnHeader];
tc.identifier = columnIdentifier;
// You may need this one, too, to get it to show.
self.dataTableview.headerView.needsDisplay = YES;
When populating the table, and assuming that the model is an array (in self.model) of NSDictionary objects, it could go something like this;
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSString *columnIdentifier = tableColumn.identifier;
NSDictionary *rowDict = [self.model objectAtIndex: row];
NSString *value = [rowDict valueForKey: columnIdentifier]; // Presuming the value is stored as a string
// Show the value in the view
}
More in the docs.
When user adds a column or row, you should reflect it in your model (by binding or by code), so you know the size of your table, when you need to populating it.
set tableView.delegate (in code or in Interface Builder), reference here
implement:
- (NSView*) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row
{
Item* itemView = [tableView makeViewWithIdentifier:#"rowItem" owner:self];
/*Here you populate your cell view*/
id entryObject = [self.entries objectAtIndex:row];
[itemView setEntry:entryObject];
return itemView;
}
and then invoke [tableView reloadData];
maybe for you better to use this method
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
Just see the NSTableViewDataSource and NSTableViewDelegate

Accessing UI Input elements as I would in HTML

I am dumping multiple inputs into a view. They consist of UITextFields UIPickerViews and UIDatePickers.
Each of them have an ID and a Key that need to be saved when the input value is saved. So when the 'Save' button is clicked, I need to loop through and store something like:
{
ID: 'inputid',
Key: 'yearly',
Value: (UITextField value)
}
In HTML, I would just add these values to the input (<input type="text" id="inputid" name="yearly" />) and then loop through each one using $(input).attr('id') etc.
In Objective-C, the only way I can think to do this is to keep a hashtable of this information when I draw the inputs, and then store some kind of identifier against the 'tag' field of the UITextField, then read that by getting all of the inputs from a view and comparing them to the hashtable.
Is this the right way to go about it?? Am I missing something simple here? How would you go about it?
EDIT
To better frame the situation, the number of UITextFields on the page is being pulled from an XML file, therefore I don't know how many UITextFields there will be (so can't assign them to the controller necessarily)
I need something along the lines of:
foreach(var question in ArrayOfQuestions) {
UITextField *textField = [[UITextField alloc] initWithFrame:];
textField.attributes["id"] = question.Id;
textField.attributes["key"] = question.Key;
}
and in the save method
foreach(var textField in UIView) {
textField = (UITextField)textField;
NSString *id = textField.attributes["id"];
NSString *key = textField.attributes["key"];
}
This maybe something I could find in google but can't think of the right search terms and keep coming up empty handed. On the same level, if you can better describe my request please update the title of my question
I think you are actually at the best solution, in regards to the hash table (NSDictionary) of attribute data. It is really a bad design decision to have too much semantic data in the view object itself, as it has nothing to do with the view.
What you need to do concretely in code is the following:
To set up your views & attribute data:
UIView *containerView; // The view that contains your UITextViews.
NSMutableDictionary *attributes; // A dictionary mapping tags to questions.
NSMutableArray *arrayOfQuestions; // The questions that you've parsed from a file or whatever.
// ...
// Each "question" would be of the form #{ #"id" : ____, #"key" : ____ }
for (NSDictionary *question in arrayOfQuestions) {
UITextField *textField = [[[UITextField alloc] initWithFrame:aFrame] autorelease];
[containerView addSubview:textField];
textField.tag = getATag(); // However you want to tag them.
// Fancy new objective-C container/object-literal syntax :)
attributes[#(textField.tag)] = question;
}
Then for your "save" method:
for (UIView *childView in containerView.subviews) {
if ([childView isKindOfClass:[UITextView class]]) {
// We know the class and can thus safely typecast the UIView.
UITextField *textField = (UITextField *)childView;
NSDictionary *aQuestion = attributes[#(textView.tag)];
// Now you can access the id and key properties of the question.
// ... Whatever else you want to do.
}
}
The enumerated loop over the subviews is I think the big thing you were looking for here. It is very similar to the way that you would do it in jQuery with selectors.
If you make each of the elements a property of the view controller, you can access them directly from anywhere and get the current value.
So in the method attached to the save button, you can get the current string value of a UITextField like this, for example:
NSString *currentTextFieldString = self.someTextField.text;

Obtain a stringValue from NSTableView

I have a simple NSTableView which I have loaded with data from a NSMutableArray. When I select a row (entry) in the tableView and modify it, I can find the row index, what I cannot get is the edited entry out as a string so I can modify the array. I can find lots of information on selecting rows, etc., but not on how to get the actual modified string. I keep thinking this should be real simple. Help please.
Part of my code:
- (IBAction)toDoEdit:(id)sender // Accept the edited data
{
NSString *toDoItem = [[toDoTableCell:toDoTableView dataCellFoTableColumn:0 row:rowToBeEdited] stringValue];
// I get the error "dataCellForTableColumn' method cannot be found.
[toDoArray replaceObjectAtIndex:rowToBeDeleted withObject:toDoItem];
[toDoTableView reloadData];
[toDoTableView deselectRow:rowToBeDeleted];
}
~~~~~~~~~~~
// This method should return the cell value of the selected row
- toDoTableCell:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
return toDoTableCell; // No errors here but toDoTableCell is nil.
}
The 'Add' data to tableView works, 'Delete' data from tableView works, I just cannot get the edited data out of tableView so I can reload the data with the corrections.
What you are looking for is an NSTableView Delegate method:
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
This will return the NSCell that is in the row and column that you specify. From the NSCell you should be able to extract the value that you need. Depending on how you are using your NSCell you would either call [cell stringValue] or [cell objectValue].
Try this:
– tableView:setObjectValue:forTableColumn:row:
in - NSTableViewDataSource Protocol Reference
--- Edited based on comment ---
Above method is called whenever user tries to edit a table row, it also provides user with changed value as parameter. If you are trying to edit the row in table itself then it should serve your purpose. You can simply check the objectValue obtained as parameter and verify if it is correct or not. In case it is incorrect you can modify the obtained value and set it in todoArray.
Briefly:
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
// below case is an example, you can add your own
if([anObject isEqualToString:#"Incorrect"])
{
anObject = #"Correct";
}
// Considering todoArray is array of dictionary items containing keys as table-column identifiers
NSMutableDictionary *originalData = [todoArray objectAtIndex:rowIndex];
[originalData setValue:anObject forKey:[aTableColumn identifier]];
[toDoTableView reloadData];
}
To get the value being edited you can simply use this code in above method, before setting the new value:
NSString *editedValue = [[todoArray objectAtIndex:rowIndex] valueForKey:[aTableColumn identifier]];
Hope this helps :)
It is simple. Read up on Cocoa Bindings and NSArrayController.
Take a look at the NSTableView methods selectedColumn, selectedColumnIndexes, selectedRow and selectedRowIndexes. I guess they should provide you with the needed information.
Now you can query the model, i.e. the array, for the data you need.

NSTableView with Dropdown Menu and having Image inside Menu

Yes, is it possible to have:
A table having two column ( Should be easy)
One of Cell should have image and that should be selectable from Drop - down menu
By googling what i came to know it has to be of type NSPopupButtonCell type, but i want only image inside it, no text,
How can i do that ?
The another column would be editable, user should be able to type in that.
it would be great if i can get any reference code to implement the same.
i did it with following way,
In Coloumn 1 select the DataCell and assign it of type NSPopupButtonCell, by default it wouldn't come, you need to select it explicitly.
In the Code add following lines of Code...
NSTableColumn *option = [pTableColumns objectAtIndex:[pTableView columnWithIdentifier:OPTION_COLUMN_NAME]];
NSTableColumn *shortCutItem = [pTableColumns objectAtIndex:[pTableView columnWithIdentifier:SHORTCUT_COLUMN_NAME]];
// we want first cell to have the Image & Menu
//Data type column drop down
NSPopUpButtonCell *dataTypeDropDownCell = [option dataCell];//[[NSPopUpButtonCell alloc] initTextCell:#"" pullsDown:YES];
[dataTypeDropDownCell setBordered:NO];
[dataTypeDropDownCell setEditable:YES];
NSArray *dataTypeNames = [NSArray arrayWithObjects:#"NULLOrignal", #"String", #"Money", #"Date", #"Int", nil];
[dataTypeDropDownCell addItemsWithTitles:dataTypeNames];
Add following code to set the correct MenuItem
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex{
if([[aTableColumn identifier] isEqualToString:OPTION_COLUMN_NAME]){
NSPopUpButtonCell *dataTypeDropDownCell = [aTableColumn dataCell];
[dataTypeDropDownCell selectItem:[ dataTypeDropDownCell itemAtIndex:3]];
}
}
Now only pending this is to append Image inside MenuItem which is not a big deal at all,
Again thanks for Looking at this, let me know if any other approach is there to do so....