# Accessing NSTextView String value



## cryingraven (Oct 27, 2007)

Hi all

My problem is using the textview's string as an NSString...

What I want to do, is to trim leading and trailing whitespace from the text entered into 3 or 4 TextView elements then combine them using a separator character and place it into yet another TextView.

I have created my interface and created my classes and setup the button to call a function, but once in the function I have tried everything I could think of to access the string value and when I DO finally get to access the string value, I immediately print it to the other textview, just to see if it works.

If I send the value @"myString", then the TextView displays the word myString as expected. This means my code works. So I then replace it with myString (which is an NSMutableString or an NSString. Like I said, I tried every combination I could think of) I get weird characters like hearts and § sybols.

E.g. an example output: The value of myString is ±§&$ and is what I wanted

How do I trim/ add a character/ attach 4 TextView's String value to assign it back to another TextView?

Do I simply trim 1, trim 2, trim 3, trim 4, add " " to 1, add " " t0 2, add " " to 3, copy 1 to 5, append 2 to 5, append 3 to 5, append 4 to 5?
Do I have to send values from textview to textview only?
This would leave me with the problem of textviews 1, 2 and 3 having a space behind what the user entered so if the user wants to change the value, he first needs to delete my space before changing his text.
Not acceptable.

Is there no way of actually using the string values into a separately defined string and sending that value to the textview after all formatting has been completed?

Please say yes...
Then please tell me how...
I ask of you, oh so nicely... Please!
Been struggling with this for ages now!

My lack of string manipulation skills is what has put me off of using XCODE altogether. I love coding and have written a grand total of 0 apps since I got my first Mac 4 years ago...

You have to power to remedy this for me...


----------



## Captain Code (Oct 28, 2007)

So you have all the IBOutlets for all your NSTextField correct?

To access the text from an individual text field:

```
NSString* value1 = [textField1 stringValue];
//...etc
```

Then, to do all the concatenation to a new string:

```
//this is to make it easier to follow, you could do it all in one line but it'd be messy

NSString* out1 = [NSString stringWithString:[value1 stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];

NSString* out2 = [NSString stringWithString:[value2 stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];

NSString* out3 = [NSString stringWithString:[value3 stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];

NSString* concatedString = [NSString stringWithFormat:@"%@ %@ %@", out1, out2, out3];

[textField1 setStringValue:concatedString];
```

In the stringWithFormat message, the Cocoa frameworks replace each %@ with the corresponding NSString you pass in as arguments.  This works like C's sprintf function.


----------



## cryingraven (Oct 31, 2007)

Hia.

Thanks for the help.

I will attempt to do it this way and see if it works. I can't see you  doing anything different from me so if yours works it would be quite puzzling indeed.

I did notice that you got the value from the textfield using stringValue and then extracted the value from the NSString using another stringValue call...Perhaps this would help...(?)

I do have the outlets setup as you showed above, but then when I pass the NSStrings back to the TextField object I do it via a:
stringWithFormat: @"%@", value1 or via
stringWithFormat: @"%s", [value1 stringValue] or just
stringWithFormat: @"%s", [value1 string]

I then either get a crash or garbage characters...


----------



## cryingraven (Oct 31, 2007)

I tried it your way

It works perfectly... 

Thanks so much for that


----------



## Captain Code (Oct 31, 2007)

Hi, yeah you're right I think that was an error actually.  NSString doesn't have a stringValue function, NSTextField does.  So stringWithString takes an NSString pointer which you can get from the NSTextField with [textField1 stringValue].

If you wanted to do it without creating all the NSString objects, a better way would be:

```
NSString* out1 = [NSString stringWithString:[[textField1 stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
...
```


----------



## cryingraven (Nov 1, 2007)

Actually, you had a spelling mistake in your original example. There was an opening [missing so I first had to figure out what you were doing exactly so I could find the error. Once I noticed how you were doing things I then had to chance your code to use my variable names and I ended up changing the code to what you suggested just now. Worked perfectly 

So I finally felt confident in my ability to manipulate stings and thought I'd get started with that project I was thinking about. Oi... I sure wish there were something other than XCODE to work with on OSX...

Allow me to explain... I learnt programming using Pascal. Then I found Delphi and I experienced heaven. RAD truly meant RAD!!! This current project I am working on I did in Delphi in only 5 hours, error checking and all.

It took me around 7 hours yesterday to create the interface in InterfaceBuilder. I have to admit, the program is rather simple, but the interface allows for multiple methods of doing the same thing so it is a rather complex interface.

Once I finished it and did a test on the final interface I realized that all my NSTableView and NSTextView components are VIEW-able objects only. Now I have to scrounge around the net trying to figure out how to add editable tables and text areas...

It seems coding in XCODE is anything but RAD... Le sigh...


----------



## Captain Code (Nov 1, 2007)

Sorry for the typo, I didn't write it in XCode which would have made a beep at me for forgetting the [.

Are you using XCode 3 on Leopard?  It's link to IB is a little better than in 2.4.x but I find it's still a little cumbersome.  Not quite as integrated between the GUI development and the code, but it's better now.  

If you're using XCode 3, then there's one tip that will save you lots of time.  Don't define any outlets or actions in IB.  Do it all in XCode and IB will automatically read the source files and notice they are there when the source file is saved in XCode. This eliminates the huge annoyance of manually having to read/write/merge the source files from IB.

I thought that NSTableView was editable by default.  I've just started with XCode for a real application myself only a few weeks ago, but I remember that the table was editable and I turned that off because I don't want people to edit mine.  There's a checkbox in the properties.  There's a little bit of a trick though, which is, you select the NSTableColumn and make it editable, not the table itself.  

In IB 3, when you first click the table view you are actually selecting an NSScrollView.  Click it again and you get the NSTableView inside it.  Click a column now and you get the NSTableColumn.

I'm sure it's probably the same with the previous version of IB though.

You can tie a table view into an array controller through bindings if you want and that eliminates a lot of glue code for changing and adding values from the array associated with the table.  However, It's a little confusing for me and I haven't gotten it to work quite yet.  The most frustrating part about bindings is even if you don't get it right, there's nothing that tells you what's wrong.  No runtime errors or compiler errors.  It just doesn't do anything and you're stuck looking over every little detail to try and get it to work.

I imaging it's easy once you know how to do it but I find like a lot of things in Cocoa, it's hard to figure it out initially but once you get it working, you realize it's not actually that hard to do.

Last week for example, I wanted to be able to drag folders and only folders into my table and have the path show up in the table for every folder.  That took me hours and hours to get working even though it's only a few functions to make it work.


----------



## cryingraven (Nov 1, 2007)

I will give (some of) what you said a go although reading this post has made me realize that I actually know very little of XCODE and it seems I really HAVE to know a lot in order to use it...

For example, I hit the build and go button and then double clicked the tables. No editing option presented itself. I will now try to make a colom editable and see if that solves this little problem.

The other thing that confused me was this: (I think my main problem comes from expecting XCODE to be as simple as Delphi) I wanted to let people choose a file and then use the combined filepath and filename as a String. I remember in Delphi I just said

```
if openDialog1.open then filename = openDialog1.filename;
```
Or something like that. I then wanted to call the file open command in XCODE and realized I had no idea how to do that. So then I hit the jackpot in seeing that there is a NSBrowser object available.. Whoo Hoo!!! Except for three little problems...
1. I cannot find a setting to make it actually browse anything from any where.
2. I can only (oooh, bindings/ outlet... confused again) drag FROM it to my class, not from my class to it. Hense it behaves more like a class method than a class member...
3. At runtime it just sits there, doing nothing...

So how in the world do I actually get it working? How to extract an NSString from an object that does nothing? Does XCODE have a feature like

```
[NSString [openFileDialog openFrom:HardDrive0 location:root] selectedFile]
```

That would be awesome if it could only be that simple...


----------



## cryingraven (Nov 1, 2007)

Yeah, tried it... didn't do anything...
Had a look and it seems all my colums are already marked as editable.

As for the NSBrowser, I found that if I cut it from the NSTab and put it on the window directly, I can then link to it from myObject, but when I put it back into the NSTab, it is no longer selectable... Go figure...

Not sure if this helps, but here goes:


----------



## Captain Code (Nov 1, 2007)

You need to use NSOpenPanel.  There's also a corresponding NSSavePanel for getting the save location.  It's important to note NSOpenPanel doesn't actually open anything itself, it just allows you to get paths to files to open.


```
NSOpenPanel *open = [NSOpenPanel openPanel];
        //you can set some properties of the open dialog
	[open setCanChooseFiles:NO];
	[open setCanChooseDirectories:YES];
	[open setAllowsMultipleSelection:YES];
	
        // you can run it as a modal window or
        //use beginSheetForDirectory to run it as a sheet instead
	int result = [open runModal];
	
	if(result == NSOKButton)
	{
          	NSArray *files = [open filenames];
                //do something with the file array...
        }
```

Check here for how to use beginSheetForDirectory
http://developer.apple.com/document...dow:modalDelegate:didEndSelector:contextInfo:


----------



## Captain Code (Nov 1, 2007)

For IBActions, I like to think of it as where does the message go when you click the button?  It goes from the button(or whatever you're clicking on) to the controller class.  So you drag from the button to the controller.  

IBOutlet is like an outlet from your controller(you're always accessing the outlet item FROM the controller) to the object so drag from the controller to the object.


----------



## cryingraven (Nov 6, 2007)

Me again

I have found what my problem was initially. Coming from a Delphi environment I was used to creating objects by simply defining them or creating pointer objects using the NEW key word. Once the item was created, I did everything I wanted to do with the object USING the object.

Therein lay the difference. I never called the class for anything, only the instantiated object. I noticed that I often call [NSString stringWithFormat...] to do stuff. Once I realized that little fact, I started reading the API when I got stuck and it all made a whole lot more sense when I did so.

I have now sucsessfully managed to create my own OBJ-C classes to use as array members and gotten VERY far indeed with the project I was doing, thanks to you 

So, now onto one thing I was curious about and then I have need of assistance again...

Firstly, structs.
I wanted to create a struct simply to emulate a record so I can create a new instance of the struct, populate it with values and then insert into my array. I then got to this problem: How do I instantiate a struct? I used the NEW but got an error saying it was the first time it encountered the NEW method.
I thought I had to import foundation/foundation.h but that also didn't work...

So I changed all my structs into obj-c classes, set getters and setters for everything and instantiate it using

```
[[crKey alloc] init];
```
This seems to work fine, but I cannot help feeling that it would be easier/faster/more efficient to use a struct. Erm... how do I do that?

Secondly, I have been able to extract, modify, merge and update string values from textFields like nobody's business. When i discovered that NSTableViews require a data source I finally realized that I will not be able to use it like an excell-spreadheet-like grid view input device like I intended. So instead, I have decided to let the users of my app enter the values as space separated strings, instead of grid separated boxes. For this I used a NSTextView and was able to write to it using the [textBlock insertText:bla] method. This is not ideal since the user might move the insertion point between clicks, but it cannot be helped.

Now I find myself at the place where I want to extract the complete written text from the textview and find that i cannot find a function to do so...


```
[[textBlock textStorage] characters];
[[textBlock textStorage] words];
```

These two functions give me an array of characters and an array of words respectively, but what i need is an array of lines.

To explain:
the user may enter from 1 to x lines of text. Each line of text will be saved on it's own and displayed on it's own in turn.
I want to create a separate NSString for each line up to the "\n" or end of string.

It would seem that I will have to extract the characters, make it a string and then split the string into an array. Is this the only way?

Thanks in advance...


----------



## Captain Code (Nov 6, 2007)

For the string parsing, check out this
http://developer.apple.com/document...String/getLineStart:end:contentsEnd:forRange:

I haven't used structs in Objective-C but since it's a super set of C you can type in any C or C++ code and it intermixes.  I'm not an expert on C structs but I don't see why you can't just do it as you would in C.

I think maybe the easiest way is to use NSData dataWithBytes:length, put the bytes from the struct into that, make an array from all the NSData objects.


----------



## Captain Code (Nov 6, 2007)

It just so happens I'm working on string parsing data delimited by tabs(\t).  I found the best method to do it built into NSString componentsSeparatedByString: (NSString*)

Gives you an NSArray without doing any work


----------



## cryingraven (Nov 6, 2007)

The main concern now, is how to actually get an NSString from NSTextView...
How does one turn an NSArray of characters into an NSString? Is there such a function?

As for the structs, I guess I'll just stick to the obj-c classes. I just noticed something interesting though. After defining IBOutlets in IB, I then go back into XCODE and create some NSArray class members. Those members seem to not get initialized automatically... Now, please have a look at this:


```
[actors addObject:newActor];
	[actorsList insertText: [NSString stringWithFormat:@"%@\t%@\n",[newActor getName], [newActor getAvatar] ] ];
```

Now, this code executes and the last line is executed and the TextView is updated. Problem is, [actors count] returns 0 even if I make multiple calls to [actors addObject:newActor]

So this led me to believe that the actors member needs to be created first, but since this is a class created by IB, I never call the INIT on it so how do I initialize the variable?

I then tried this:

```
if ([actors count] == 0)
		actors = [NSMutableArray arrayWithObject:newActor];
	else [actors addObject:newActor];
	
	[actorsList insertText: [NSString stringWithFormat:@"%@\t%@\n",[newActor getName], [newActor getAvatar] ] ];
```

This causes the code to stop executing somewhere mid-function since the actorsList is never updated. I then tried something else. Just a small change:


```
if (!actors)
	 actors = [NSMutableArray arrayWithObject:newActor];
	else [actors addObject:newActor];
	
	[actorsList insertText: [NSString stringWithFormat:@"%@\t%@\n",[newActor getName], [newActor getAvatar] ] ];
```

This actually causes the program to crash. The funny thing is:
1. I can send the addObject message to an object that has not been created yet and it gives me no error, it just doesn't add the object.
2. When I call a method inside the class that has not been initialized yet, execution stops.
3. When I test to see if an object exists, the app crashes... Shouldn't this be the other way around?

This is the class I created to be used as the object to pass into the array:

```
//the .h file
@interface crActor : NSObject
{
	NSString	*name;
	NSString	*avatar;
}

-(void) setName:(NSString*)nm;
-(void) setAvatar:(NSString*)nm;
-(NSString*) getName;
-(NSString*) getAvatar;
@end

//The .m file
@implementation crActor
-(void) setName:(NSString*)nm
{
	name = nm;
}

-(void) setAvatar:(NSString*)nm
{
	avatar = nm;
}

-(NSString*) getName
{
return name;
}

-(NSString*) getAvatar
{
return avatar;
}
@end
```

As you can see, there is nothing funny about the class so the problem has to be with the init. So what is the best way of initializing a class member that has to be globally available for the duration of the app?

The variables are declared inside the class I created in IB as:

```
NSMutableArray *actors;
	NSMutableArray *keys;
	NSMutableArray *reqs;
	NSMutableArray *options;
	NSMutableArray *dialogueLines;
```

To summarize again:
Question 1: What am I doing wrong here?
Question 2: How do I extract an NSString from a NSTextView?


----------



## cryingraven (Nov 15, 2007)

well, I figured out one of the two problems...

It seems that the problem was with instantiating (what I thought to be a) global member. I called an alloc on a pointer that resides outside the function, but within the class. It seems that when the function ended, the member was deallocated but not NULLed so the next call of the function resulted in a garbage filled pointer being used.

All I had to do was add a RETAIN to the end of the alloc and the problem was solved...

```
if (!actors)
	 actors = [[NSMutableArray arrayWithObject:newActor] retain];
	else [actors addObject:newActor];
```

Now I just have to figure out question 2...


----------

