attributed ios swift nsattributedstring

attributed - Swift: cambia la fuente en una cadena HTML que tiene sus propios estilos



nsattributedstring swift 3 (6)

Dinámicamente obtengo una cadena HTML de una API de Wordpress y la analizo en una cadena atribuida para mostrarla en mi aplicación. Como la cadena tiene sus propios estilos, muestra diferentes fuentes y tamaños, algo que está afectando nuestras elecciones de diseño.

Lo que quiero hacer es cambiar la fuente y su tamaño en toda la cadena atribuida.

Intenté hacerlo en las opciones de la cadena atribuida, pero no hace nada:

let attributedT = try! NSAttributedString( data: nContent!.decodeHTML().data(using: String.Encoding.unicode, allowLossyConversion: true)!, options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSFontAttributeName: UIFont(name: "Helvetica", size: 16.0)!], documentAttributes: nil) contentLbl.attributedText = attributedT

¿Alguien tiene alguna idea sobre cómo lograr esto?

PD: Sé que podría agregar una etiqueta CSS al principio o al final de la cadena, pero ¿esto anularía otros estilos en ella? Además, si esta es una solución válida, ¿podría proporcionar una muestra de cómo hacerlo?


Lo que quiere hacer, básicamente, es convertir la NSAttributedString en una NSMutableAttributedString.

let attributedT = // ... attributed string let mutableT = NSMutableAttributedString(attributedString:attributedT)

Ahora puede llamar a addAttributes para aplicar atributos, como una fuente diferente, en cualquier rango deseado, como todo.

Desafortunadamente, sin embargo, una fuente sin un rasgo simbólico como cursiva es una fuente diferente de una fuente con ese rasgo simbólico. Por lo tanto, necesitará una utilidad que copie los rasgos simbólicos existentes de una fuente y los aplique a otra fuente:

func applyTraitsFromFont(_ f1: UIFont, to f2: UIFont) -> UIFont? { let t = f1.fontDescriptor.symbolicTraits if let fd = f2.fontDescriptor.withSymbolicTraits(t) { return UIFont.init(descriptor: fd, size: 0) } return nil }

Bien, armado con esa utilidad, probémoslo. Comenzaré con un HTML simple y lo convertiré en una cadena atribuida, tal como lo estás haciendo:

let html = "<p>Hello <i>world</i>, hello</p>" let data = html.data(using: .utf8)! let att = try! NSAttributedString.init( data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) let matt = NSMutableAttributedString(attributedString:att)

Como puede ver, he convertido a un NSMutableAttributedString, como le aconsejé. Ahora voy a pasar por el estilo en términos de fuente, alterando a una fuente diferente mientras uso mi utilidad para aplicar los rasgos existentes:

matt.enumerateAttribute( NSFontAttributeName, in:NSMakeRange(0,matt.length), options:.longestEffectiveRangeNotRequired) { value, range, stop in let f1 = value as! UIFont let f2 = UIFont(name:"Georgia", size:20)! if let f3 = applyTraitsFromFont(f1, to:f2) { matt.addAttribute( NSFontAttributeName, value:f3, range:range) } }

Aquí está el resultado:

Obviamente, podría ajustar este procedimiento para que sea aún más sofisticado, dependiendo de sus necesidades de diseño.


SetAttributes restablecerá todos los atributos de HTML. Escribí un método de extensión para evitar esto:

Swift 4

public convenience init?(HTMLString html: String, font: UIFont? = nil) throws { let options : [NSAttributedString.DocumentReadingOptionKey : Any] = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue] guard let data = html.data(using: .utf8, allowLossyConversion: true) else { throw NSError(domain: "Parse Error", code: 0, userInfo: nil) } if let font = font { guard let attr = try? NSMutableAttributedString(data: data, options: options, documentAttributes: nil) else { throw NSError(domain: "Parse Error", code: 0, userInfo: nil) } var attrs = attr.attributes(at: 0, effectiveRange: nil) attrs[NSAttributedStringKey.font] = font attr.setAttributes(attrs, range: NSRange(location: 0, length: attr.length)) self.init(attributedString: attr) } else { try? self.init(data: data, options: options, documentAttributes: nil) } }

Muestra de prueba:

let html = "<html><body><h1 style=/"color:red;/">html text here</h1></body></html>" let font = UIFont.systemFont(ofSize: 16) var attr = try NSMutableAttributedString(HTMLString: html, font: nil) var attrs = attr?.attributes(at: 0, effectiveRange: nil) attrs?[NSAttributedStringKey.font] as? UIFont // print: <UICTFont: 0x7ff19fd0a530> font-family: "TimesNewRomanPS-BoldMT"; font-weight: bold; font-style: normal; font-size: 24.00pt attr = try NSMutableAttributedString(HTMLString: html, font: font) attrs = attr?.attributes(at: 0, effectiveRange: nil) attrs?[NSAttributedStringKey.font] as? UIFont // print: <UICTFont: 0x7f8c0cc04620> font-family: ".SFUIText"; font-weight: normal; font-style: normal; font-size: 16.00pt


Solución Swift 4

  • Extensión NSAttributedString con inicializador de conveniencia
  • Enumera a través de los atributos de fuente de la cadena atribuida (documento HTML) y reemplaza con el UIFont proporcionado
  • Conserva los tamaños de fuente HTML originales o usa el tamaño de fuente del UIFont proporcionado, useDocumentFontSize parámetro useDocumentFontSize
  • Este método simplemente puede convertir HTML a NSAttributedString, sin la sobrecarga de manipular con fuentes, simplemente omita el parámetro de fuente, @ vea la declaración de guard

extension NSAttributedString { convenience init(htmlString html: String, font: UIFont? = nil, useDocumentFontSize: Bool = true) throws { let options: [NSAttributedString.DocumentReadingOptionKey : Any] = [ .documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue ] let data = html.data(using: .utf8, allowLossyConversion: true) guard (data != nil), let fontFamily = font?.familyName, let attr = try? NSMutableAttributedString(data: data!, options: options, documentAttributes: nil) else { try self.init(data: data ?? Data(html.utf8), options: options, documentAttributes: nil) return } let fontSize: CGFloat? = useDocumentFontSize ? nil : font!.pointSize let range = NSRange(location: 0, length: attr.length) attr.enumerateAttribute(.font, in: range, options: .longestEffectiveRangeNotRequired) { attrib, range, _ in if let htmlFont = attrib as? UIFont { let traits = htmlFont.fontDescriptor.symbolicTraits var descrip = htmlFont.fontDescriptor.withFamily(fontFamily) if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitBold.rawValue) != 0 { descrip = descrip.withSymbolicTraits(.traitBold)! } if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitItalic.rawValue) != 0 { descrip = descrip.withSymbolicTraits(.traitItalic)! } attr.addAttribute(.font, value: UIFont(descriptor: descrip, size: fontSize ?? htmlFont.pointSize), range: range) } } self.init(attributedString: attr) } }

Uso-1 (Reemplazar fuente)

let attr = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!", font: UIFont.systemFont(ofSize: 34, weight: .thin))

Usage-2 (ejemplo NSMutableAttributedString )

let attr = try! NSMutableAttributedString(htmlString: "<strong>Hello</strong> World!", font: UIFont.systemFont(ofSize: 34, weight: .thin)) attr.append(NSAttributedString(string: " MINIMIZE", attributes: [.link: "@m"]))

Uso-3 (solo convierte HTML a NSAttributedString)

let attr = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")


Versión Swift 3 de mi solución anterior (Swift 4)

extension NSAttributedString { convenience init(htmlString html: String, font: UIFont? = nil, useDocumentFontSize: Bool = true) throws { let options: [String : Any] = [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue ] let data = html.data(using: .utf8, allowLossyConversion: true) guard (data != nil), let fontFamily = font?.familyName, let attr = try? NSMutableAttributedString(data: data!, options: options, documentAttributes: nil) else { try self.init(data: data ?? Data(html.utf8), options: options, documentAttributes: nil) return } let fontSize: CGFloat? = useDocumentFontSize ? nil : font!.pointSize let range = NSRange(location: 0, length: attr.length) attr.enumerateAttribute(NSFontAttributeName, in: range, options: .longestEffectiveRangeNotRequired) { attrib, range, _ in if let htmlFont = attrib as? UIFont { let traits = htmlFont.fontDescriptor.symbolicTraits var descrip = htmlFont.fontDescriptor.withFamily(fontFamily) if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitBold.rawValue) != 0 { descrip = descrip.withSymbolicTraits(.traitBold)! } if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitItalic.rawValue) != 0 { descrip = descrip.withSymbolicTraits(.traitItalic)! } attr.addAttribute(NSFontAttributeName, value: UIFont(descriptor: descrip, size: fontSize ?? htmlFont.pointSize), range: range) } } self.init(attributedString: attr) } }


let font = "<font face=''Montserrat-Regular'' size=''13'' color= ''black''>%@" let html = String(format: font, yourhtmlstring) webView.loadHTMLString(html, baseURL: nil)


let font = UIFont(name: fontName, size: fontSize) textAttributes[NSFontAttributeName] = font self.attributedText = NSAttributedString(string: self.text, attributes: textAttributes)