cocoa - NSTextFinder + programmatically changing the text in NSTextView -


i have nstextview want use find bar. text selectable, not editable. change text in text view programatically.

this setup can crash when nstextfinder tries select next match after text changed. seems nstextfinder hold on outdated ranges incremental matches.

i tried several methods of changing text:

[textview setstring:@""]; 

or

nstextstorage *newstorage = [[nstextstorage alloc] initwithstring:@""];  [textview.layoutmanager replacetextstorage:newstorage]; 

or

[textview.textstorage beginediting]; [textview.textstorage setattributedstring:[[nsattributedstring alloc] initwithstring:@""]]; [textview.textstorage endediting]; 

only replacetextstorage: calls -[nstextfinder noteclientstringwillchange]. none of above invokes -[nstextfinder cancelfindindicator].

even nstextfinder notified text change can crash on find next (command-g).

i have tried creating own nstextfinder instance suggested in post. though nstextview not implement nstextfinderclient protocol works , fails same without nstextfinder instance.

what correct way use nstextfinder nstextview?

i had same problem text view in app, , makes more annoying "solutions" find on internet either incorrect or @ least incomplete. here contribution.

when set textview.usefindbar = yes in nstextview, text view creates nstextfinder internally, , forwards search/replace commands it. unfortunately, nstextview not seem handle correctly changes make programmatically associated nstextstorage, causes crashes mention.

if want change behavior, creating private nstextfinder not enough: need avoid use text view of default text finder, otherwise conflicts occur , new text finder won't of use.

to this, have subclass nstextview:

@interface mytextview : nstextview  - (void) resettextfinder; // method reset view's text finder when change text storage  @end 

and in text view, have override responder methods used controlling text finder:

@interface mytextview ()  <nstextfinderclient> {     nstextfinder* _textfinder; // define own text finder }  @property (readonly) nstextfinder* textfinder;  @end  @implementation mytextview  // text finder command validation (could done in method validateuserinterfaceitem: if prefer)   - (bool) validatemenuitem:(nsmenuitem *)menuitem {     bool isvaliditem = no;      if (menuitem.action == @selector(performtextfinderaction:)) {         isvaliditem = [self.textfinder validateaction:menuitem.tag];     }     // validate other menu items if needed     // ...     // , don't forget call superclass     else {         isvaliditem = [super validatemenuitem:menuitem];     }      return isvaliditem; }  // text finder  - (nstextfinder*) textfinder {     // create text finder on demand     if (_textfinder == nil) {         _textfinder = [[nstextfinder alloc] init];         _textfinder.client = self;         _textfinder.findbarcontainer = [self enclosingscrollview];         _textfinder.incrementalsearchingenabled = yes;         _textfinder.incrementalsearchingshoulddimcontentview = yes;     }      return _textfinder; }  - (void) resettextfinder {     if  (_textfinder != nil) {         // hide text finder          [_textfinder cancelfindindicator];         [_textfinder performaction:nstextfinderactionhidefindinterface];          // clear client , container properties         _textfinder.client = nil;         _textfinder.findbarcontainer = nil;          // , delete         _textfinder = nil;     } }  // commands sent text finder - (void) performtextfinderaction:(id<nsvalidateduserinterfaceitem>)sender {     [self.textfinder performaction:sender.tag]; }  @end 

in text view, still need set properties usesfindbar , incrementalsearchingenabled yes.

and before changing view's text storage (or text storage contents) need call [mytextview resettextfinder]; recreate brand new text finder new content next time search.

if want more information nstextfinder, best doc have seen in appkit release notes os x 10.7


Comments

Popular posts from this blog

javascript - jQuery: Add class depending on URL in the best way -

caching - How to check if a url path exists in the service worker cache -

Redirect to a HTTPS version using .htaccess -