2012-08-28 9 views
7

Ho una sottoclasse NSTextView che funge da delegato NSTextStorage. Sto cercando di fare 2 cose:La modifica di NSTextStorage fa spostare il punto di inserimento alla fine della riga

  1. evidenziare il testo in qualche modo
  2. valutare il testo e quindi aggiungere la risposta alla TextView.

Lo sto facendo in due modi diversi, entrambi invocati dal callback del delegato - (void)textStorageWillProcessEditing:(NSNotification *)notification.

Posso fare l'evidenziazione della sintassi bene, ma quando si tratta di aggiungere la mia risposta, il punto di inserimento salta alla fine della riga e non so davvero perché. Il mio metodo di valutazione è simile al seguente:

NSString *result = ..; 
NSRange lineRange = [[textStorage string] lineRangeForRange:[self selectedRange]]; 
NSString *line = [[textStorage string] substringWithRange:lineRange]; 
line = [self appendResult:result toLine:line]; // appends the answer 

[textStorage replaceCharactersInRange:lineRange withString:line]; 

Facendo che aggiungerà il mio risultato più che bene, ma il problema è, come detto, il punto di inserimento salta alla fine.

ho provato:

  1. Wrapping quelli di cui sopra si richiama in [textStorage beginEditing] e -endEditing.
  2. Salvare l'intervallo di selezione (cioè il punto di inserimento) prima di modificare l'archiviazione del testo in modo da poterlo reimpostare in seguito, ma senza dadi.

Sto facendo bene? Sto cercando di fare questo il modo meno ostico, e sono anche incerto se questo è il posto ideale per fare la mia analisi/evidenziazione. I documenti mi portano a crederci, ma forse è sbagliato.

+0

Che cos'è lineForRange, non trovo questo metodo nei documenti? Se intendi lineRangeForRange, questo potrebbe essere il tuo problema. Stai cercando di aggiungere alla fine della riga o alla fine della selezione? – rdelmar

+0

Sì, questo è un errore di battitura. – jbrennan

+0

E la seconda parte della mia domanda? Non è chiaro dove stai cercando di aggiungere il tuo testo. Quando dici che il punto di inserimento si sposta alla fine della linea, intendi dopo l'inserimento o prima? – rdelmar

risposta

0

È semplice! Ho finito per rompere questo problema in 2 parti. Faccio ancora l'evidenziazione della sintassi a seguito della richiamata dei delegati textStorage, ma ora eseguo la valutazione e aggiungo altro.

Ho finito per ignorare sia -insertText: e -deleteBackwards: (potrei anche voler fare lo stesso per -deleteForwards:, anche). Entrambe le sostituzioni simile al seguente:

- (void)insertText:(id)insertString { 
    [super insertText:insertString]; 
    NSRange selectedRange = [self selectedRange]; 
    [self doEvaluationAndAppendResult]; 
    [self setSelectedRange:selectedRange]; 
} 

ho finito per dover reimpostare il punto di inserimento manualmente qui. Mi piacerebbe ancora capire perché è necessario, ma almeno sembra meno di un trucco.

+0

Questa post-correzione, spostando il punto di inserimento, funzionerà solo quando l'intervallo interessato non ha causato lo scorrimento - o l'utente vedrà lo sfarfallio della barra di scorrimento brevemente e la linea del punto di inserimento scorrerà verso il centro della visualizzazione del testo. Ciò accade quando la roba 'doEvaluationAndAppendResult' scorre in modo che' setSelectedRange: 'sia impostato su un intervallo al di fuori del rect visibile. – ctietze

4

So che questa domanda è stata risposta da tempo, tuttavia avevo lo esattamente lo stesso problema. Nel mio NSTextStorage sottoclasse stavo facendo quanto segue:

- (void)processEditing { 
    //Process self.editedRange and apply styles first 
    [super processEditing]; 
} 

Tuttavia, la cosa giusta da fare è questa:

- (void)processEditing { 
    [super processEditing]; 
    //Process self.editedRange and apply styles after calling superclass method 
} 
+1

Grazie, questo ha risolto il mio problema. Se si utilizza NSTextStorageDelegate e si verifica questo problema, utilizzare textStorageDidProcessEditing: anziché textStorageWillProcessEditing :. Leggere gli avvertimenti della documentazione sull'abbandono di textStorage in uno stato incoerente. –

+0

Avviso: mentre funziona, devi stare attento a non fare troppi problemi con 'NSLayoutManager' nel processo, perché la chiamata al' processEditing' del genitore corregge gli attributi e segna la fine di un blocco begin/endEditing, quindi stai impostando gli attributi di evidenziazione al di fuori dei normali allegati di modifica. – ctietze

1

Motivo per il punto di inserimento per spostare

Sorprendentemente, non ho mai trovato una vera spiegazione del motivo per cui questi suggerimenti funzionano (o non funzionano).

Scavare in esso, il motivo per il punto di inserimento per spostare è: .editedCharacters (NSTextStorageEditedCharacters in objC) influisce sulla posizione del punto di inserimento da NSLayoutManager.processEditing(from:editedMask:...).

Se viene inviato solo .editedAttributes/NSTextStorageEditedAttributes, il punto di inserimento non verrà toccato. Questo è ciò che vorresti ottenere quando evidenzi: modifica solo degli attributi.

Perché evidenziazione colpisce il punto di inserimento

Il problema con evidenziazione ecco che NSTextStorage raccoglie tutti edited chiamate durante un singolo ciclo di elaborazione e combina gli intervalli, iniziando con il cambiamento user-modificato (ad esempio l'inserimento quando digitando), quindi formando un'unione di questo e tutti gli intervalli riportati da addAttributes(_:range:). Ciò si traduce in una singola chiamata NSLayoutManager.processEditing(from:editedMask:...) - con un editedMask di entrambi [.editedCharacters, .editedAttributes].

Così si desidera inviare .editedAttributes per le gamme evidenziati ma finiscono per formare un'unione con .editedCharacters invece. Quell'unione sposta il punto di inserimento waaaaaaaay oltre il punto in cui dovrebbe andare.

Cambiare l'ordine in processEditing per chiamare super prime opere perché al gestore del layout verrà notificata una modifica completata. Ma questo approccio si interromperà ancora per alcuni casi limite, risultando in un layout non valido o in una vista a scorrimento mentre si digitano paragrafi molto grandi.

This is true for hooking into NSTextStorageDelegate, a proposito.

gancio in callback dopo il layout è veramente finito per innescare evidenziando invece di processEditing

L'unica soluzione che funziona robusto basato su motivi inerenti il ​​quadro Cocoa è effettuare evidenziazione da textDidChange(_:) esclusivamente, cioè dopo l'elaborazione di layout è davvero finito. L'iscrizione allo NSTextDidChangeNotification funziona altrettanto bene.

Lato negativo: è necessario attivare i passaggi di evidenziazione per le modifiche programmatiche alla stringa sottostante poiché questi non richiameranno la richiamata textDidChange(_:).


nel caso si desideri sapere di più circa l'origine del problema, ho messo più la mia ricerca, approcci diversi, e dettagli della soluzione in un post molto più lungo per riferimento. Questo post è ancora una soluzione autonoma in sé: http://christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/

Problemi correlati