2014-04-02 11 views
10

Per supportare il protocollo UIAccessibilityReadingContent, ho bisogno del mio UITextView per rispondere alle domande sulle sue linee. Questi sono i metodi del protocollo che ho bisogno di implementare:Ottieni informazioni sulla linea da UITextView e NSLayoutManager

  • accessibilityLineNumberForPoint: < - ha fornito una coordinata, restituisce un numero di riga
  • accessibilityContentForLineNumber: < - Restituisce il testo di una data linea
  • accessibilityFrameForLineNumber: < - Dato un numero di riga, restituisce il suo frame
  • accessibilityPageContent < - L'intero contenuto del testo. Che ho. :)

Immagino che NSLayoutManager possa aiutarmi, ma non ne ho esperienza. Ho capito un po '(penso), ma ho ancora bisogno di aiuto.

Apple ha alcuni esempi di codice (here) che mi può ottenere il numero di righe nella visualizzazione di testo:

NSLayoutManager *layoutManager = [textView layoutManager]; 
unsigned numberOfLines, index, numberOfGlyphs = 
     [layoutManager numberOfGlyphs]; 
NSRange lineRange; 
for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){ 
    (void) [layoutManager lineFragmentRectForGlyphAtIndex:index 
      effectiveRange:&lineRange]; 
    index = NSMaxRange(lineRange); 
} 

Ho capito che, con lineRange sopra, posso calcolare la linea rettangoli di utilizzare questo metodo su NSLayoutManager :

- (NSRect)boundingRectForGlyphRange:(NSRange)glyphRange inTextContainer:(NSTextContainer *)container 

E dato lineRanges dovrei essere in grado di calcolare il numero di riga per un punto utilizzando (trovando la lineRange che contiene l'indice di glifo:

- (NSUInteger)glyphIndexForPoint:(CGPoint)point inTextContainer:(NSTextContainer *)container fractionOfDistanceThroughGlyph:(CGFloat *)partialFraction 

Quindi, ciò che resta è, come faccio ad avere il contenuto di una linea (come NSString), dato un numero di riga?

+0

Sei sicuro di aver bisogno di quel protocollo, nel caso comune puoi semplicemente impostare NSTextStorage, NSLayoutManager e NSTextContainer e tutto andrà bene. Ho letto un grande argomento al riguardo: http://www.raywenderlich.com/50151/text-kit-tutorial. – kaspartus

+0

Purtroppo si. UITextView non sembra fornire una lettura riga per riga quando VoiceOver è abilitato. Almeno non nella mia implementazione (UITextViews incorporato in UITableViewCells) –

+0

'NSLayoutManager' offre metodi che funzionano con intervalli di glifi, oltre a metodi per intervalli di caratteri. Infine ci sono due metodi che ti permettono di convertire tra entrambi gli intervalli: '- [NSLayoutManager glyphRangeForCharacterRange: actualCharacterRange:]' e '- [NSLayoutManager characterRangeForGlyphRange: actualGlyphRange:]'. Per recuperare la stringa, converti semplicemente il glyphRange che ti interessa in un characterRange, quindi puoi recuperare una sottostringa dal 'textStorage' di' UITextView' usando 'attributeSubstringFromRange:' o sia 'string' che' substringWithRange: '. – Dalzhim

risposta

4

pensare in una soluzione facile, ecco un piccolo codice che riproducono quello che ti serve

- (void)analyse:(UITextView *)textView 
{ 
    NSLayoutManager *layoutManager = [textView layoutManager]; 
    NSString *string = textView.text; 
    unsigned numberOfLines, index, stringLength = [string length]; 

    NSMutableArray *ranges = [NSMutableArray new]; 
    NSMutableArray *frames = [NSMutableArray new]; 
    for (index = 0, numberOfLines = 0; index < stringLength; numberOfLines++) 
    { 
     NSRange tmprange; 
     NSRange range = [string lineRangeForRange:NSMakeRange(index, 0)]; 
     CGRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:index 
              effectiveRange:&tmprange]; 

     [ranges addObject:[NSValue valueWithRange:range]]; 

     [frames addObject:[NSValue valueWithCGRect:rect]]; 
     index = NSMaxRange(range); 
    } 
    self.ranges = ranges; 
    self.frames = frames; 
    self.numberOfLines = numberOfLines; 
} 

prega di dare un'occhiata delle proprietà:

self.ranges = ranges; 
self.frames = frames; 
self.numberOfLines = numberOfLines; 

Si può avere il seguente nella classe per creare queste proprietà:

@property (nonatomic) NSInteger numberOfLines; 
@property (strong, nonatomic) NSArray *ranges; 
@property (strong, nonatomic) NSArray *frames; 

Ti suggerisco di aggiungere la chiamata di analisi all'interno del followi ng delegato:

- (void)textViewDidChange:(UITextView *)textView 

Non ci si può, ad esempio, dopo l'analisi ottenere la cornice del 2 ° linea solo facendo: self.frames [1]

o di ottenere il testo della seconda linea fare: [textView.text substringWithRange: [self.ranges [1] rangeValue]]

Ad esempio come questo:

if (self.numberOfLines > 1) 
{ 
    NSRange range = [self.ranges[1] rangeValue]; 
    NSLog(@"2nd line = %@", [textView.text substringWithRange:range]); 
    NSLog(@"2nd line frame = %@", self.frames[1]); 
} 

Avere tutti i frame in self.frames Penso che si possa facilmente fare l'altra cosa, indovinare il numero di linea usando una coordinata.

+0

Ho trovato un piccolo errore nella tua bellissima soluzione, dovresti usare 'index = NSMaxRange (tmpRange);' invece di 'index = NSMaxRange (intervallo);' per ottenere il numero corretto di righe. Funzionando su 9.3. Altrimenti, la soluzione è perfetta - più uno per te! :-) –