7

Sto leggendo le stringhe da un Localizable.strings che contiene qualcosa di simile a quello che è in pratica quello che hai in un string.xml di un'app Androidcrea una stringa attribuita con testo semplice (Android formattato) in swift per iOS

"testShort" = "A <b>short</b>\ntest with another<b>bold text</b>"; 

Il grassetto e il line feed sono gli unici due attributi di formattazione che ho nei miei testi. Sto cercando di sviluppare un metodo come questo per giorni ora senza successo:

func ConvertText(inputText: String) -> NSAttributedString { 
    // here comes the conversion to a representation with Helvetica 14pt and Helvetica-Bold 14pt including line feeds. 
} 

Il mio obiettivo finale è quello di visualizzare il testo in attributedText di un UITextView.

Essendo un po 'nuovo per Swift e iOS senza conoscere Objective-C ho trovato la sua molto difficile da fare manipolazioni di stringhe in quanto sono molto diverse e complesse e tutti gli esempi sono in Objective-C. Ciò che rende ancora più difficile è che la maggior parte dei metodi API non sono disponibili o diversi in Swift rispetto a Objective-C ...

Ecco cosa ho provato finora per il metodo body con l'aiuto di molti altri post qui :

var test = inputText.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)! 
attrStr = NSAttributedString(
     data: test, 
     options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], 
     documentAttributes: nil, 
     error: nil)! 
return attrStr 

I principali problemi qui sono che \n non viene convertito e il tipo di carattere è molto piccolo e diverso.

Successivamente ho provato a mettere in grassetto manualmente una parte di un testo. E sembra funzionare così:

var newText = NSMutableAttributedString(string: inputText) 
newText.addAttribute(NSFontAttributeName, value: UIFont(name: "Helvetica-Bold", size: 14.0)!, range: NSRange(location:2,length:4)) 

Ora ho provato a cercare per gli attributi nel testo, eliminarli e utilizzare l'AddAttribute un po 'come quella

// Start of bold text 
var range = newText.rangeOfString("<b>")! 
// End of bold text 
var range2 = newText.rangeOfString("</b>")! 

// replacing attributes 
newText = newText.stringByReplacingCharactersInRange(range, withString: "") 
newText = newText.stringByReplacingCharactersInRange(range2, withString: "") 

// creating a range for bold => error "'String.Index' is not convertible to 'int'" 
// how to do such a thing 
var boldRange = NSMakeRange(range.startIndex, range2.endIndex -3) 

// setting bold 
newText.addAttribute(NSFontAttributeName, value: UIFont(name: "Helvetica-Bold", size: 14.0)!, range: boldRange) 

Questa intera gamma cosa è il mio problema principale al momento è piuttosto diverso da una semplice posizione nella stringa.

Questo problema è un grande esempio per la mancanza di (o ben nascosto) documentazione:

Il addAttribute vuole un NSRange, il rangeOfString sembra offrire una gamma generica in base a un messaggio di errore ottengo - ma c'è nessuna informazione a riguardo Il pulsante Cerca nella documentazione in Xcode su rangeOfString() porta a NSString. La ricerca in questo campo per rangeOfString() dice che restituisce NSRange. Facendo clic su questo si accede alle informazioni di un alias di tipo per _NSRange che a sua volta ha due proprietà NSUInteger denominate location e length. Dov'è la proprietà startIndex e endIndex che vedo in XCode? Molto confuso ...

Sarebbe grandioso se tu potessi darmi alcuni frammenti o suggerimenti in cui mi trovo in errore qui o anche il metodo del corpo come sto ancora sperando che non sia troppo difficile se conosci bene iOS e Swift. Sto puntando al supporto per iOS 7.1, ma se la sua strada più semplice con iOS 8 è valida anche per lui.

+0

Effetto Bold range: 'NSMakeRange (range.location, range2.location-range.location)'. Manca forse il fatto che hai "cancellato" nel frattempo "" e "", il che significa che potresti avere 3 o 3 + 4 differenze. – Larme

+0

Larne, ci ho provato. Xcode mi lancia l'errore _'Range 'non ha un membro chiamato' location'_ che è dovuto alla spiegazione Martin R postato sotto – Soko

risposta

7

quanto riguarda il tuo primo metodo con NSAttributedString:

  • Il personaggio \n in HTML è spazio bianco semplicemente ordinario. Per ottenere un'interruzione di linea, è necessario sostituire prima con <br />.
  • Gli attributi del carattere possono essere controllati da un codice HTML <span>, vedere Parsing HTML into NSAttributedText - how to set font?.

Questo dà (ora aggiornato per Swift 2):

func convertText(inputText: String) -> NSAttributedString { 

    var html = inputText 

    // Replace newline character by HTML line break 
    while let range = html.rangeOfString("\n") { 
     html.replaceRange(range, with: "<br />") 
    } 

    // Embed in a <span> for font attributes: 
    html = "<span style=\"font-family: Helvetica; font-size:14pt;\">" + html + "</span>" 

    let data = html.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)! 
    let attrStr = try? NSAttributedString(
     data: data, 
     options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], 
     documentAttributes: nil) 
    return attrStr! 
} 

quanto riguarda il tuo secondo metodo:

Ci sono due diversi rangeOfString() metodi, uno per (Swift) String e uno per (Fondazione) NSString. Il metodo String restituisce un Range<String.Index> e il metodo NSString restituisce un NSRange.

La conversione tra questi due è possibile ma complicata. Il motivo è che in a String ogni "grafo grafo esteso" conta come un carattere, mentre in NSString ogni unità UTF-16 viene conteggiata. Un cluster grafo esteso può essere una o più unità UTF-16 ("" è due unità UTF-16, "" è quattro).

Il metodo addAttribute() accetta solo un NSRange. Il metodo più semplice per risolvere il problema consiste nel convertire la stringa Swift in NSString e lavorare solo con NSRange . Quindi il tuo metodo potrebbe essere il seguente:

func convertText(inputText: String) -> NSAttributedString { 

    let attrString = NSMutableAttributedString(string: inputText) 
    let boldFont = UIFont(name: "Helvetica-Bold", size: 14.0)! 

    var r1 = (attrString.string as NSString).rangeOfString("<b>") 
    while r1.location != NSNotFound { 
     let r2 = (attrString.string as NSString).rangeOfString("</b>") 
     if r2.location != NSNotFound && r2.location > r1.location { 
      let r3 = NSMakeRange(r1.location + r1.length, r2.location - r1.location - r1.length) 
      attrString.addAttribute(NSFontAttributeName, value: boldFont, range: r3) 
      attrString.replaceCharactersInRange(r2, withString: "") 
      attrString.replaceCharactersInRange(r1, withString: "") 
     } else { 
      break 
     } 
     r1 = (attrString.string as NSString).rangeOfString("<b>") 
    } 

    return attrString 
} 
+0

Grazie mille Martin. I tuoi metodi funzionano alla grande, anche per iOS 7.1. Quale preferiresti (quale è più veloce)? Ohh e non c'è davvero alcuna documentazione sul metodo 'rangeOfString()' o sul suo tipo restituito 'Range ' o è solo ben nascosto? Spero ci sia una buona ragione per rendere Strings così complicato in Swift ... – Soko

+1

@Soko: Non ho usato nessuno di questi, quindi non posso dire nulla sulla velocità. Probabilmente preferirei # 1 perché è più generale e può gestire altri attributi HTML. # 2 è in realtà abbastanza soggetto a errori (cosa succede se il tag è scritto come '' con uno spazio?). 'Range ' è facile una volta che ne hai avuto la sensazione :) Non ho un buon riferimento, ma ci sono già molte domande e risposte qui su SO. –