NSTableView edits not changing NSMutableArray - objective-c

I have a Class that stores some strings along with a BOOL flag that is set to YES if one of the strings has been updated.
I have a NSTableView that displays the strings in my class. The view is controlled via separate controller class and the view is fed by an NSMutableArray.
The GUI stuff seems to work fine in terms of displaying data and allowing me to edit the cells in the table view. The problem I am having is the edits don't change the objects stored in the NSMutableArray. I have some debug code to print out the strings when I close the app, and none of the changes made in the GUI show up in the objects at this point. Setting a break point where those values are changed shows me the objects are indeed changed, but those changes seem to get lost. How can I get any changes I make in the view to persist in the object stored in the NSMutableArray backing the NSTableView?
Here is how I am coding:
// my class .h file
#interface Snip : NSObject <NSMutableCopying>
#property (assign) int64_t id_num;
#property (assign) BOOL changed;
#property NSMutableString *name;
#property NSMutableString *text;
#property (copy) NSString *language;
// my class .m file
import "Snip.h"
#implementation Snip
#synthesize id_num;
#synthesize name;
#synthesize text;
#synthesize changed;
#synthesize language;
method from my controller class
// edit table values
-(void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn(NSTableColumn *)tableColumn row:(NSInteger)row
{
Snip *sn = [snippet_list objectAtIndex:row];
[sn setChanged:YES];
[sn setValue:object forKey:[tableColumn identifier]];
NSLog(#"Change: %#",sn.name);
}
That last NSLog statement displays the change I made in the GUI. Also, the setChanged:YES is lost as well when I print the NSMutableArray contents when closing the app.

This behaviour is often due to the data being initialised again at some point after being changed by the user.
You can catch this problem by setting a breakpoint (or NSLog statement) in your initialisation code.

Related

Problems with an NSMutableArray

I'm trying to write an app that has two scenes in it. The first page is a UITableView that will contain a list of note entries. The second scene has 2 text fields (note summary and note description). I'm entering details on the second scene and then clicking a "Save" button which saves the data:
NoteListViewController.m
- (IBAction)saveAndGoBack:(id)sender {
NSLog(#"NoteDetailViewController.m: %#", NSStringFromSelector(_cmd));
NSString * desc = [[NSString alloc]init];
NSString * detail = [[NSString alloc]init];
desc = _noteTitle.text;
detail = _noteDesc.text;
[[NoteStore sharedStore]createNoteWithDesc:desc AndDetail:detail];
[self.navigationController popViewControllerAnimated:YES];
}
NoteStore is a static sharedStore that I am saving the data into.
NoteStore.m
-(Notes *)createNoteWithDesc:(NSString *)desc AndDetail:(NSString *)detail {
NSLog(#"NoteStore.m: %#", NSStringFromSelector(_cmd));
Notes * newNote = [[Notes alloc]initNoteWithDesc:desc AndDetail:detail];
[self.privateItems addObject:newNote];
return newNote;
}
So, the note is added to an NSMutableArray called "privateItems". I confirmed that the Note object gets added properly.
*****The problem happens when I try to retrieve the Note object (desc and detail) from the privateItems array later on using an accessor method which has a public property in the NoteStore.h file called allItems (it's an NSArray readonly, nonatomic and a copy):
NoteStore.m
-(NSArray *)allItems{
NSLog(#"NoteStore.m: %#", NSStringFromSelector(_cmd));
return [self.privateItems copy];
}
Everytime I try to retrieve it, the first property (desc) comes up as nil while the second property (detail) has the data I saved in the second text field of the second scene. Why is the first field constantly coming up as nil???
Just for clarity, a Note object is declared as follows
#interface Notes : NSObject
// What are the properties of a note?
#property (nonatomic, weak) NSString * noteDesc;
#property (nonatomic, weak) NSString * noteDetail;
#property (nonatomic, weak) NSString * test;
// Designated Initializer
-(instancetype)initNoteWithDesc:(NSString *)desc AndDetail:(NSString *)detail;
#end
Any help would be greatly appreciated.
When you call the designated initialiser you pass in two NSString objects. At this point they are owned by the method where they are created.
When they are assigned to the properties they only have a weak reference and therefor the retain count is not bumped up. Weak references are good for things like delegate objects. In this case you want your objects to stick around, so by declaring them as strong you're saying I want these properties to stick around in memory and take ownership of them.

Dynamic UITableView with multiple custom UITableViewCells

I've been working on this for a while, doing a lot of research, but haven't found a solution I was particularly happy with.
Here's the situation:
The tableview is a settings page of sorts, with dynamic content. For example, rows need to be added and removed when a switch changes state in one of the cells. To accomplish this, I chose the delegate pattern to notify of the cell's changes
The problem:
1) I'm not sure what object should be the proper "owner," and therefore delegate, of the custom cells. It seems to me that the uitableview should delegate for the cells, and in turn it delegates to the view controller.
2) Whichever object delegates for the custom cells, it has to "figure out" which property would need to be updated based on the call. This is a problem because of multiple cells of the same type being applied to different properties.
For example, say there are 2 sections, each with 1 switch cell. When one of the cells fires its delegate call to notify of the change in state, the view controller has to determine which part of the model to update. In this example, you could easily check which section the cell was in to update the model, however it wouldn't really solve the problem, because if you were to add a second switch cell to one of the sections in the future, it would break.
Note:
As you'll see in the code below, it is conceivable to utilize the indexPath to check the property being edited. However, it would result in either a ever growing if/elseif or switch statement checking which property corresponds to which indexPath.
Reason being: at least some properties are not pointers, so storing them in an array and editing them directly wouldn't affect the data, and would eventually need to be translated using literals to the actual data object.
Here's some of what I have to better illustrate:
#protocol CustomUITextFieldCellDelegate <NSObject>
- (void)cellDidBeginEditingTextField:(CustomUITextFieldCell *)cell;
- (void)cellDidEndEditingTextField:(CustomUITextFieldCell *)cell;
#end
#interface CustomUITextFieldCell : UITableViewCell <UITextFieldDelegate>
#property (nonatomic, strong) NSString *title;
#property (nonatomic, assign) id <CustomUITextFieldCellDelegate> delegate;
#end
'
#protocol CustomTableViewDelegate <UITableViewDelegate>
- (void)textFieldCell:(CustomUITextFieldCell *)cell didBeginEditingIndexPath:(NSIndexPath *)indexPath;
- (void)textFieldCell:(CustomUITextFieldCell *)cell didEndEditingIndexPath:(NSIndexPath *)indexPath;
#end
#interface CustomTableView : UITableView <CustomUITextFieldCellDelegate>
#property (nonatomic, assign) id <CustomTableViewDelegate> delegate;
#end
In the ViewController, delegate for the CustomUITableView:
- (void)textFieldCell:(TTD_UITextFieldCell *)cell didBeginEditingIndexPath:(NSIndexPath *)indexPath {
// determine which property is being edited
// update model
}
Thanks for any help in advance! I'm curious to see how you would tackle this problem.
If you know which property the value is editing when creating the cell, you can use blocks instead of the delegate pattern, like this:
// for text editing
typedef void (^TextCellSetValueBlock)(NSString *);
typedef NSString *(^TextCellGetValueBlock)();
#interface CustomUITextFieldCell: UITableViewCell {
// ...
#property (nonatomic, copy) TextCellSetValueBlock onSetValue;
#property (nonatomic, copy) TextCellGetValueBlock onGetValue;
// ...
}
When creating the cell, assign onSetValue/onGetValue to blocks that read/write the appropriate property from your model, and call onGetValue()/onSetValue() from your cell when you want to get/set the property.
For the boolean switch that turns on/off parts of your UI, you can have your onSetValue blocks update your model and also add/remove cells as a side effect.

textView not getting set from another view controller (using Singleton class)

I have a textViewController class. I want to set(basically update) the corresponding textView's content from another view controller which I am pushing over the present textViewController. The way I thought I could do this was to have a shared singleton class and have a string property there to contain the text. I have been using the singleton for storing some other content as well and it worked fine till now.
But the text variable in singleton class doesn't hold the content I pass to it from the second view controller and hence the textViewController, after popping the second view controller, displays the old text even after reappearing. I am updating the textView in its viewWillAppear method which is getting called but shows old text content on NSLogging.
What am I missing here? Please suggest a solution, stuck at it for a long time now.
Declaration
Firstly, declare the NSString in your app delegate .h file. It should look something like this:
//YourAppDelegate.h
#import <UIKit/UIKit.h>
#interface YourAppDelegate : NSObject < UIApplicationDelegate >
{
NSString *sharedString; //WE ADDED THIS
}
#property (nonatomic, retain) NSString *sharedString; //AND THIS
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet SomeViewController *viewController;
#end
Now you want to synthesize the object so that the accessor and mutator methods are made for you. This means you don't have to write the setSharedString: and getSharedString methods - we can simply access and mutate the object by making a reference to it. This is how we synthesize it in the .m file:
//YourAppDelegate.m
#synthesize sharedString;
Assigning a Value
When you want to assign a value to sharedString from another class, you must first retrieve the active instance of your application delegate:
YourAppDelegate *appDelegate = (YourAppDelegate*)[[UIApplication sharedApplication] delegate];
The appDelegate object here is your running app delegate instance. To access the sharedString NSString object in the delegate and assign a value, we'd do this:
appDelegate.sharedString = #"some string we want to assign";
For the duration of our application's runtime, "some string we want to assign" is now stored in sharedString in our app delegate.
Retrieving the Value
You'll want to retrieve the value at some point. To do this, again we'll have to get the running instance of our application delegate:
YourAppDelegate *appDelegate = (YourAppDelegate*)[[UIApplication sharedApplication] delegate];
Once we have the running instance, we can then access the sharedString object stored inside it:
NSString *retrievedString = appDelegate.sharedString;
From this point, retrievedString now holds the value "some string we want to assign".
Note:
Everywhere I mention YourAppDelegate, I am referring to your app delegate - the app delegate you have in your project. It'll be related to your project name (look for the file).
How your NSString property for your Text-Variable is defined? Is it a "weak"?

Objective C Adding custom objects into NSMutableArray

I want to store a list of data records in a NSMutableArray for use in a UITableView. In other languages I would have used a simple 'type' structure to define the record structure but I understand the way to do this in Obj-C is to define a new class. I've done this as follows :
#interface CustSuppListItem : NSObject
#property (nonatomic, copy, readwrite) NSString *acCode;
#property (nonatomic, copy, readwrite) NSString *acCompany;
#property (nonatomic, copy, readwrite) NSString *acContact;
#property (nonatomic, assign, readwrite) double osBalBase;
#property (nonatomic, assign, readwrite) unsigned int acAccStatus;
#end
#implementation CustSuppListItem
#synthesize acCode, acCompany, acContact, osBalBase, acAccStatus;
#end
In the viewDidLoad of my UITableViewController I instantiate the array :
tableListDataArray = [[NSMutableArray alloc] init];
Once I have retrieved my data, I add it to the array as follows :
CustSuppListItem *custSuppItem = [[CustSuppListItem alloc] init];
[custSuppItem setAcCode:[jsonCustSuppRecord getStringForKey:#"acCode"]];
[custSuppItem setAcCompany:[jsonCustSuppRecord getStringForKey:#"acCompany"]];
[custSuppItem setAcContact:[jsonCustSuppRecord getStringForKey:#"acContact"]];
[custSuppItem setOsBalBase:[jsonCustSuppRecord getDoubleForKey:#"osBalBase"]];
[custSuppItem setAcAccStatus:[jsonCustSuppRecord getIntForKey:#"acAccStatus"]];
[tableListDataArray addObject:custSuppItem];
[custSuppItem release];
In my table cellForRowAtIndexPath method, I retrieve the data for the current cell as follows:
CustSuppListItem *listDataRecord = [tableListDataArray objectAtIndex:indexPath.row];
[cell.lblCompanyName setText:listDataRecord.acCompany]; // EXC_BAD_ACCESS here
[cell.lblAcCodeContact setText:[NSString stringWithFormat:#"%#, %#",
listDataRecord.acCode, listDataRecord.acContact]];
[cell.lblBalance setText:[Utils fmtNumber:listDataRecord.osBalBase withDecPlaces:2]];
[cell.lblStatus setText:[Utils exchAccStatusDesc:listDataRecord.acAccStatus]];
return cell;
In the dealloc method for the view controller I release the NSMutableArray :
[tableListDataArray release];
I'm very new to Obj-C so it would be great if somebody could confirm everything I've done so far makes sense and is in order. I am getting an intermittent EXC_BAD_ACCESS error when trying to read the acCompany property (see comment next to line) so something must not be right.
Any help appreciated,
Jonathan
All your code looks reasonable and correct to me at first glance.
A few things that I would look at are:
Confirm that cell definitely has a property lblCompanyName. If you're trying to assign to a property that doesn't exist then you will get this type of error. Have you defined a custom cell object type?
Confirm that it is always the acCompany property that is causing the EXC_BAD_ACCESS, and not just any property on the object. One way to do this would be to change the ordering of the lines in the cellForRowAtIndexPath method.
Confirm that the listDataRecord that's causing the crash is getting populated correctly in the first place. In other words, confirm that your jsonCustSuppRecord is always valid. What does jsonCustSuppRecord getStringForKey: return if the key doesn't exist in the jsonCustSuppRecord?
Set a breakpoint at this line: [tableListDataArray addObject:custSuppItem]; and examine the contents of the custSuppItem each time (this is an extension of point 3. above)

Using Scanf with ObjC and iPhone

trying to make an iPhone app and going through the tutorials and books recommended by those before me :) I'm trying to find information on the scanf/storing user input data from a text field into a variable which I can use later in my program. The text field is actually a number field, so I am trying to save the integers they input and not the text since there won't be any in my case. Am I on the wrong path here? Any help would be greatly appreciated.
what about if i am essentially trying
to save the input that is a number to
begin with
You want NSNumberFormatter. The data formatters ( Apple Guide) handle conversions to and from strings as well as formatting for output.
I think rather than scanf, you're just looking to get the value from the text field as an NSString pointer.
If you use a UITextField in your interface, you can connect the UITextField to a member variable in your class by declaring the variable as an IBOutlet and connecting it in Interface Builder.
You can then access the text value as an NSString pointer by using [UITextField variable name].text.
There are many useful functions to work with NSStrings or convert the string to other datatypes such as integers.
Hope this helps!
Here's an example of how to get an integer out of a text field.
In your .h file:
#include <UIKit/UIKit.h>
#interface MyViewController : UIViewController {
UITextField *myTextField;
}
#property (nonatomic, retain) IBOutlet UITextField *myTextField;
- (IBAction)buttonPressed1:(id)sender;
#end
In your .m file:
#include "MyViewController.h"
#implementation MyViewController
#synthesize myTextField;
- (IBAction)buttonPressed1:(id)sender {
NSString *textInMyTextField = myTextField.text;
// textInMyTextField now contains the text in myTextField.
NSInteger *numberInMyTextField = [textInMyTextField integerValue];
// numberInMyTextField now contains an NSInteger based on the contents of myTextField
// Do some stuff with numberInMyTextField...
}
- (void)dealloc {
// Because we are retaining myTextField we need to make sure we release it when we're done with it.
[myTextField release];
[super dealloc];
}
#end
In interface builder, connect the myTextField outlet of your view controller to the text field you want to get the value from. Connect the buttonPressed1 action to the button.
Hope this helps!