NSMutableArray of nsstrings (in one of the UISearchBar delegates) possible with persistent storage? - objective-c

I would like to make a history list in my app. I have surfed and looked at many questions here, but so far I haven´t found the answer to my question.
My codes:
-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
NSLog (#"search bar text did end editing!");
NSString *bookmarked = mySearchBar.text;
NSMutableArray *bookmarkl = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < [bookmarkl count]; i ++)
{
[bookmarkl addObject:bookmarked];
}
for (NSString *sa in bookmarkl);
NSLog (#"whole array: %#", bookmarkl);
}
But all I get is one string at a time. What is wrong?
I want to add a new string to the mutable array, how should I do this?

You are creating a new list every time the function is called. You should add bookmarkl to your class as a property and only create it once during initialization. If you want the history to persist when your app is closed you will also want to write the data to a file. You can use [NSMutableArray arrayWithContentsOfFile] to load the history and writeToFile to save it.

Related

NSMutableArray Resetting Itself?

I am having an issue with NSMutableArray wiping its contents.
Consider my code: (int i; is in my file's .h as is NSMutableArray* newFileControllerArray)
-(void)awakeFromNib{
i = 0;
newFileWindowControllerArray = [[NSMutableArray alloc]init];
}
-(IBAction)newFileMenubar:(id)sender{
[newFileWindowControllerArray addObject:[[NewFileWindowController alloc]initWithWindowNibName:#"NewFileWindowController"]];
NSUInteger elementsInArray = [newFileWindowControllerArray count];
NSLog(#"%lu",(unsigned long)elementsInArray);
[[newFileWindowControllerArray objectAtIndex:i] showWindow:nil];
}
-(IBAction)OKButtonClicked:(id)sender{
NSUInteger elementsInArray = [newFileWindowControllerArray count];
NSLog(#"THERE ARE %lu ELEMENTS IN THE ARRAY",(unsigned long)elementsInArray);
}
The first method called (other than awakeFromNib:) is newFileMenubar: This will add one element to the array. I can confirm that this works because 1 is printed in the console. However, once OKbutton is called and I print out the number of elements in my array it says that no elements are in the array. Why is that?
Am I missing something very obvious here? Why does my array reset itself?
EDIT:
The comments have gotten long and unwieldy so here is the code w/NSLogs and outputs:
-(void)awakeFromNib{
i = 0;
newFileWindowControllerArray = [NSMutableArray array];
NSLog(#"self=%p, array=%p", self, newFileWindowControllerArray);
}
-(IBAction)newFileMenubar:(id)sender{
[newFileWindowControllerArray addObject:[[NewFileWindowController alloc]initWithWindowNibName:#"NewFileWindowController"]];
[[newFileWindowControllerArray objectAtIndex:i] showWindow:nil];
i++;
NSLog(#"self=%p, array=%p", self, newFileWindowControllerArray);
}
-(IBAction)OKButtonClicked:(id)sender{
NSUInteger elementsInArray = [newFileWindowControllerArray count];
NSLog(#"self=%p, array=%p", self, newFileWindowControllerArray);
[documentController newDocument:sender];
[[newFileWindowControllerArray objectAtIndex:i]close];
}
When the program launches, this is the output: self=0x100141480, array=0x100140f30
This should be coming from awakeFromNib:
The next method called is newFileMenubar:
The output from this is
self=0x1001ac990, array=0x1005228a0 and immediately after self=0x100141480, array=0x100140f30
The last method called is OKButtonClicked:
The output from the last method (OKButtonClicked:) is self=0x1001ac990, array=0x1005228a0
As you can see from the code, the name of the array doesn't change, but my outputs beg to differ? What could cause this?
There are good clues in your log output. There are multiple instances of the view controller (see the different values for 'self'?). They each have their own array. See this code...
-(IBAction)newFileMenubar:(id)sender{
[newFileWindowControllerArray addObject:[[NewFileWindowController alloc]initWithWindowNibName:#"NewFileWindowController"]];
When you press the button associated with that action, your app builds another view controller and places it in the array. That view controller gets the awake from nib message and allocates another array, and so on.
To confirm this, change the code as follows:
-(IBAction)newFileMenubar:(id)sender{
[newFileWindowControllerArray addObject:#"Hello world"];
// and comment this out, for now:
// [[newFileWindowControllerArray objectAtIndex:i] showWindow:nil];
In the other methods, comment out your expectations that the array has anything other than strings in it, and see what you get. e.g. ...
- (IBAction)OKButtonClicked:(id)sender {
NSUInteger elementsInArray = [newFileWindowControllerArray count];
NSLog(#"self=%p, array=%p", self, newFileWindowControllerArray);
[documentController newDocument:sender];
// and comment this out, for now:
// [[newFileWindowControllerArray objectAtIndex:i]close];
// instead...
NSLog(#"danh thinks my array will be ok: %#", newFileWindowControllerArray);
}
You probably do not mean to create another view controller on every button press, but I'm not sure what function you do want. Maybe you want an array of views? (To create many view controllers under the control of another, you'll want to read up on container view controllers, here).

How do I concatenate strings together in Objective-C?

So I am trying to concatenate a bunch of input strings together as one string so I can save that to a text file.
So far I am trying to write something like this
NSString *tempString = [[NSString alloc]initWithFormat:#"%#%#%#", text1, text2, text3];
The only problem with this is that I need a total of 30 strings stored this way. I need a way to do this without typing out each string name. Is there a way to use a for loop or something to accomplish this? Type the strings like this perhaps?
text(i)
So that the variable name would change each time it went through the for loop. I've tried doing something like this and I can't get it to work. If you can help me with this method or another way that you know to do it I would be very thankful.
Okay, so all of the answers here take the wrong approach (sorry guys).
The fundamental problem is that you are using your "text boxes" as a data source, when they should simply be views. When someone changes the text, you should immediately store them in your model (which could be a simple array) and then reference that model later. (This is part of MVC. Look it up if you aren't familiar, as you should be if you are programming for iOS!)
Here is what I would do. (I'm assuming that your "text boxes" are UITextField's.)
Set the delegate for each text field to your view controller.
Set the tag for each text field to a number which represents the order that you want the strings joined in. (ie 1-30)
If you don't have a separate class for your data model, then setup a declared property in your view controller which stores a reference to a NSMutableArray which can contain all of the strings in order. Let's call it dataSource. In viewDidLoad: set this to an actual mutable array filled with empty values (or previously stored values if you are saving them). The reason that we store empty values is so that we can replace them with the user entered strings for any index, even if they are entered out of order:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = [[NSMutableArray alloc] initWithCapacity:20];
for(int i = 0; i < 30; i++)
[self.dataSource addObject:#""];
}
Then, use the following text field delegate method which stores the strings into the array as they are entered:
// This is called every time that a text field finishes editing.
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField.tag > 0)
[self.dataSource replaceObjectAtIndex:textField.tag-1 withObject:textField.text];
}
Congratulations! All of your strings are now stored in one array. Now we just have to combine them all:
NSMutableString *theString = [self.dataSource componentsJoinedByString:#""];
Note that I have NOT tested all of this so there may be typos. This should get you pointed in the right direction though!
If you set up your text boxes in Interface Builder with an IBOutletCollection(UITextField) you would have an array of text boxes that you could access the text value using KVC and join them.
//interface
...
#property (nonatomic, strong) IBOutletCollection(UITextField) NSArray *textBoxes;
//implementation
...
NSString *tempString = [[textBoxes valueForKey:#"text"]
componentsJoinedByString:#""];
Using iOS 4's IBOutletCollection
If you programmatically create your text boxes then add them to an array as you create them.
NSMutableString's appendString: method is your friend.
NSArray *strings = [NSArray arrayWithObjects: #"Hi", #" there", #" dude", nil];
NSMutableString *result = [[NSMutableString alloc] init];
for (NSString *string in strings) {
[result appendString:string];
}
NSLog(#"result: %#", result); // result: Hi there dude

How to add imagepath inside array one by one in iphone

I am new to iPhone programming. How do I add an NSString to an NSArray. I have thumbnail images. If I click on any one of the images I want to store imagepath in the NSArray. Same if I click on the same image I want to remove the imagepath from NSArray. (like select and deselect) Can anybody tell me how I can do this, what is the logic for that? Till now I have been doing
- (void)click
{
NSLog(#"%#",_fullsizeUrl);
array =[[NSMutableArray alloc]init];
[array addObject:_fullsizeUrl];
NSLog(#"%#",array);
}
In above code _fullsizeUrl is imagepath I am storing this in an NSMutableArray. The problem is if select any one image means Ii getting image path that is storing in NSArray. But if I select any other images it's storing the latest one previous one is removing. Can any body tell me how to do this
Thanks
Your "previous one is removing" because you are reallocating your NSMutableArray everytime you tap the thumbnail. Allocate the array outside the click method.
-(void)click
{
if ([yourAry containsObject:yourObject])
{
[yourAry removeObject:yourObject];
}
else{
[yourAry addObject:yourObject];
}
}
array =[[NSMutableArray alloc]init];
[array addObject:_fullsizeUrl];
You are creating a new array every time click is called. At the end of the method, the array has no strong references (assuming ARC, otherwise you are leaking memory) and gets deallocated.
Make your NSMutableArray a property on your class and then click should look like this:
- (void)click
{
if ([self.myArray containsObject:self.fullSizeUrl]) {
[self.myArray removeObject:self.fullSizeUrl];
} else {
[self.myArray addObject:self.fullSizeUrl];
}
}
If you are only storing unique strings in your array you could consider using NSMutableSet instead.
You can use this method and use one int variable i and increase ever time +1.
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;

How to manage memory/usage of a NSMutableArray of NSMutableArrays

I am currently trying to write a class to create faux grid system to keep track of a NSMutableArray of game entities using a NSMutableArray of NSMutableArrays. Given my limited experience with Objective-C programming, I am unsure of how certain things work.
Here is the init method:
#define MAX_BALL_ROWCOUNT 6
#define MAX_BALL_COLCOUNT 4
- (id) initWithMutableArray:(NSMutableArray *)aList {
self = [super init];
if (self != nil) {
ballList = [[NSMutableArray alloc] initWithCapacity: MAX_BALL_ROWCOUNT];
for (int i=0; i<MAX_BALL_ROWCOUNT; i++) {
NSMutableArray *balls = [[NSMutableArray alloc] initWithCapacity:MAX_BALL_COLCOUNT];
[ballList addObject:balls];
[balls release];
}
int x = 0;
for (NSMutableArray *array in ballList) {
for (int i = 0; i<MAX_BALL_COLCOUNT; i++) {
[array addObject:[aList objectAtIndex:x]];
x++;
}
}
}
return self;
}
ballList is the class's NSMutableArray that will store NSMutableArrays.
aList is the NSMutableArray containing the GameEntities I wish to keep track of that is passed into this class.
All the sizes and amount of entities to store are fixed, which is why there is no checks on the sizes of the arrays nor the number of entities to store.
So the first question I have involves freeing memory. This is the dealloc function I currently have:
- (void) dealloc {
[ballList release];
[super dealloc];
}
Does calling a release on ballList cause the release to be called on the NSMutableArrays that it contains (which will subsequently call the release on the objects those NSMutableArrays contain) or do I have to write something like:
for (NSMutableArray *array in ballList) {
[array release];
}
[ballList release];
My second question involves the usage of this array of arrays. Is this the proper way to traverse through ballList?
- (void) update {
for (NSMutableArray *array in ballList) {
for (GameEntity *balls in array) {
(CGPoint) location = [balls getLocation];
[balls setLocation: CGPointMake(location.x+1, location.y+1)];
}
}
}
Lastly, in the code above where it sets the balls location, does it only affect the contents within ballList or does the original aList that is passed into ballList change as well? If the contents in the original aList do not change, how would I write it so that they do?
If people have suggestions for a better way to keep track of the entities in a grid system, I'd be open to those too. Thanks in advance.
First : One release is enough for the NSMutableArray instance to release all it's object.
[ballList release];
Second : Your code for updating GameEntity instance is fine and will also effect to the original aList (which you called) .
When the dealloc of a NSArray or NSMutableArray is called, all its contents gets a release message. So when you release ballList, if there there is no other owner (I guess in this case there is none) then its dealloc is called and you don't need to release the other arrays here.
Your loop traversal is fine. Though for 2D arrays instead of NSArray of NSArray I personally prefer pure C 2D array, at least in most of the cases.
When you are adding object in this way you are adding a reference in the array. So any change via the array's reference will be reflected in all references of the object. If you don't want that then add a copy of the object in the array.

why the tableview doesn't show binding data?

Here's my code of generating data
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[array initWithCapacity:20];
}
- (IBAction) readlog:(id)sender {
for (int i = 0; i < 20; i++) {
NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingFormat:#"/%d.log",i]];
[array addObject:d];
}
}
- (IBAction) writelog:(id)sender {
for (int i = 0; i < 20; i++) {
NSMutableDictionary *d = [NSMutableDictionary dictionary];
NSString *name = [NSString stringWithFormat:#"testfile%d", i];
[d setObject:[NSDate date] forKey:#"date"];
[d setObject:[path stringByAppendingFormat:#"/%d.log", i] forKey:#"path"];
[d setObject:name forKey:#"name"];
[d writeToFile:[path stringByAppendingFormat:#"/%d.log", i] atomically:YES];
}
and I bind my tableview column with appdelegate.array with keypath name/path/date
but it doesn't show any data in the array.. is there anything wrong here?
Thanks!
You haven't created an array.
init methods, including NSMutableArray's initWithCapacity:, initialize an existing (freshly-created) instance. You haven't created one, so you're sending that initWithCapacity: message to nil, which means it has no effect.
You need to create the array, then initialize it, then assign it to your array variable, preferably all in the same line.
There's also the issue that your table view will have already asked for the array by the time you receive the applicationDidFinishLaunching: message. You don't have one yet, so it gets nothing; by the time you create one, it has already asked you for it and gotten its answer, and does not know that it should ask again.
Create your array in init or initWithCoder: (I believe you will need the latter if your app delegate is in a nib), and implement and use Key-Value-Coding-compatible accessor methods to fill the array with values. When you send yourself accessor messages, you'll cause KVO notifications that will tip off the table view that it needs to ask for the array again. Assigning directly to the instance variable will not cause this effect.
A couple of other things…
You have three [path stringByAppendingFormat:#"/%d.log", i] expressions in two different methods. Don't repeat yourself. Move that to a method named something like logFileNameWithLogFileNumber: and send yourself that message to generate the filename. This will make the code both clearer and easier to maintain.
Finally, as a matter of style, you should not use stringByAppendingFormat: or stringWithFormat: to construct paths. Use stringByAppendingPathComponent: (in this case, together with stringWithFormat: to generate the filename). Clarity and pathname-separator-independence are virtues.