PDA

View Full Version : Continuously check if...




RD92
Jul 17, 2007, 01:01 AM
Hi, I created an app that counts the lines in a text view. I got it to work like this:

- (void)awakeFromNib
{
string = [textView string];
unsigned numberOfLines;
unsigned index;
unsigned stringLength = [string length];
for (index = 0, numberOfLines = 0; index < stringLength; numberOfLines++)
{
index = NSMaxRange([string lineRangeForRange:NSMakeRange(index, 0)]);
[textField setIntValue:numberOfLines + 1];
}
}

I put '+ 1' because, I don't know why, it starts from zero. It works perfectly except It just counts them once. If you modify the text view, the text field doesn't change. How can I make it to coninuously check for changes in the text view?



robbieduncan
Jul 17, 2007, 01:55 AM
You don't want to continuously check for changes: this will use a large amount of CPU (this solution is called polling and, in general, is something we try and avoid).

The better solution would be to have the text view tell you when there is a change and re-count then.

To do this set your instance as the delegate of the view. You then have a number of methods you could implement.

The textDidChange: (http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSText_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/textDidChange:) method of NSText would work, although from memory this would only get called when editing ends (i.e. you select another control) so is probably not what you want.

A better method might be to implement - (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString (textView:shouldChangeTextInRange:replacementString:
). Remember to return YES from this! This should get called on all changes to the text.

RD92
Jul 17, 2007, 03:31 AM
Ok thanks.

Nutter
Jul 17, 2007, 04:46 AM
Also, a for loop isn't particularly suitable here. Try this:


unsigned numberOfLines = 0;
unsigned index = 0;
while (index < [[textView string] length])
{
index = NSMaxRange([[textView string] lineRangeForRange:NSMakeRange(index, 0)]);
++numberOfLines;
}
[textField setIntValue:numberOfLines];


(I know this wasn't part of your question, so sorry if this is unwelcome advice.)

Eraserhead
Jul 17, 2007, 05:09 AM
@RD I think you are wrong, I think textDidChange does get called every time you type a character, at least certainly one of the delegates of NSText or NSTextField does. I think textDidEndEditing only gets called when you end editing ;).


I'd love to help find which method but I changed my code to use bindings instead.

robbieduncan
Jul 17, 2007, 05:26 AM
@RD I think you are wrong, I think textDidChange does get called every time you type a character, at least certainly one of the delegates of NSText or NSTextField does. I think textDidEndEditing only gets called when you end editing ;).

In that case that would be great. I have distinct memories of it not being called on very character change in an NSTextField though. I could be thinking of another delegate method though.

The simplest thing would be to try it!

Another reason for suggesting the other method was that it tells you the range being effected and the new characters. So you could use this data to update the count without a full recount which could save a lot of CPU time on a very long document...

Eraserhead
Jul 17, 2007, 07:39 AM
Another reason for suggesting the other method was that it tells you the range being effected and the new characters. So you could use this data to update the count without a full recount which could save a lot of CPU time on a very long document...

True, it did seem to be very inefficient to do it my way.

RD92
Jul 17, 2007, 08:32 AM
Ok, I switched to textDidChange:NSTextDidEndEditingNotification but I have no idea how to count only the characters that have been added. Here's my code:

- (void)textDidChange:NSTextDidEndEditingNotification
{
linesString = [textView string];
unsigned numberOfLines;
unsigned index;
unsigned stringLength = [linesString length];
for (index = 0, numberOfLines = 0; index < stringLength; numberOfLines++)
{
index = NSMaxRange([linesString lineRangeForRange:NSMakeRange(index, 0)]);
[linesField setIntValue:numberOfLines + 1];
}
}

kainjow
Jul 17, 2007, 08:41 AM
There's no need to update your text field every single time. Just do it once at the end. Also, isn't componentsSeparatedByString: a more simpler way to count the # of lines?

Edit: Use Nutter's code. Much easier to read and more efficient.

RD92
Jul 17, 2007, 09:25 AM
Ok, thanks it worked perfectly. Just one more thing, how can I get the current line that the insertion point is in?