¿Cómo hago una cadena atribuida usando Swift?

Estoy tratando de hacer una simple calculadora de café. Necesito mostrar la cantidad de café en gramos. El símbolo “g” para gramos debe adjuntarse a mi UILabel que estoy usando para mostrar la cantidad. Los números en el UILabel están cambiando dinámicamente con la entrada del usuario muy bien, pero tengo que agregar una “g” minúscula al final de la cadena que tiene un formato diferente al de los números de actualización. La “g” debe adjuntarse a los números para que a medida que el tamaño y la posición del número cambien, la “g” “se mueva” con los números. Estoy seguro de que este problema se ha resuelto antes, por lo que un enlace en la dirección correcta sería útil ya que he buscado mi pequeño corazón en Google.

He buscado en la documentación una cadena atribuida e incluso descargué un “Creador de cadena atribuido” de la tienda de aplicaciones, pero el código resultante está en Objective-C y estoy usando Swift. Lo que sería increíble, y probablemente útil para otros desarrolladores que aprenden este idioma, es un claro ejemplo de crear una fuente personalizada con atributos personalizados usando una cadena atribuida en Swift. La documentación para esto es muy confusa ya que no hay un camino muy claro sobre cómo hacerlo. Mi plan es crear la cadena atribuida y agregarla al final de mi cadena coffeeAmount.

var coffeeAmount: String = calculatedCoffee + attributedText 

Donde calculateCoffee es un Int convertido a una cadena y “attribuText” es la “g” minúscula con letra personalizada que estoy tratando de crear. Tal vez estoy haciendo esto de la manera incorrecta. ¡Cualquier ayuda es apreciada!

enter image description here

Esta respuesta se ha actualizado para Swift 4.0.

Referencia rápida

La forma general para crear y establecer una cadena atribuida es así. Puede encontrar otras opciones comunes a continuación.

 // create attributed string let myString = "Swift Attributed String" let myAttribute = [ NSAttributedStringKey.foregroundColor: UIColor.blue ] let myAttrString = NSAttributedString(string: myString, attributes: myAttribute) // set attributed text on a UILabel myLabel.attributedText = myAttrString 

Color de texto

 let myAttribute = [ NSAttributedStringKey.foregroundColor: UIColor.blue ] 

Color de fondo

 let myAttribute = [ NSAttributedStringKey.backgroundColor: UIColor.yellow ] 

Fuente

 let myAttribute = [ NSAttributedStringKey.font: UIFont(name: "Chalkduster", size: 18.0)! ] 

enter image description here

 let myAttribute = [ NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue ] 

enter image description here

 let myShadow = NSShadow() myShadow.shadowBlurRadius = 3 myShadow.shadowOffset = CGSize(width: 3, height: 3) myShadow.shadowColor = UIColor.gray let myAttribute = [ NSAttributedStringKey.shadow: myShadow ] 

El rest de esta publicación brinda más detalles para quienes estén interesados.


Atributos

Los atributos de cadena son solo un diccionario en forma de [NSAttributedStringKey: Any] , donde NSAttributedStringKey es el nombre clave del atributo y Any es el valor de algún Type. El valor podría ser una fuente, un color, un número entero o cualquier otra cosa. Hay muchos atributos estándar en Swift que ya han sido predefinidos. Por ejemplo:

  • nombre clave: NSAttributedStringKey.font , valor: a UIFont
  • nombre de la clave: NSAttributedStringKey.foregroundColor , value: a UIColor
  • nombre clave: NSAttributedStringKey.link , valor: un NSURL o String

Hay muchos otros Vea este enlace para más. Incluso puede hacer sus propios atributos personalizados como:

  • nombre clave: NSAttributedStringKey.myName , value: algún tipo.
    si haces una extensión :

     extension NSAttributedStringKey { static let myName = NSAttributedStringKey(rawValue: "myCustomAttributeKey") } 

Crear atributos en Swift

Puede declarar atributos al igual que declarar cualquier otro diccionario.

 // single attributes declared one at a time let singleAttribute1 = [ NSAttributedStringKey.foregroundColor: UIColor.green ] let singleAttribute2 = [ NSAttributedStringKey.backgroundColor: UIColor.yellow ] let singleAttribute3 = [ NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleDouble.rawValue ] // multiple attributes declared at once let multipleAttributes: [NSAttributedStringKey : Any] = [ NSAttributedStringKey.foregroundColor: UIColor.green, NSAttributedStringKey.backgroundColor: UIColor.yellow, NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleDouble.rawValue ] // custom attribute let customAttribute = [ NSAttributedStringKey.myName: "Some value" ] 

Tenga en cuenta el valor rawValue que se necesitó para el valor de estilo de subrayado.

Como los atributos son solo diccionarios, también puede crearlos haciendo un diccionario vacío y luego agregándole pares de valor-clave. Si el valor contendrá múltiples tipos, entonces debe usar Any como tipo. Aquí está el ejemplo multipleAttributes de arriba, recreado de esta manera:

 var multipleAttributes = [NSAttributedStringKey : Any]() multipleAttributes[NSAttributedStringKey.foregroundColor] = UIColor.green multipleAttributes[NSAttributedStringKey.backgroundColor] = UIColor.yellow multipleAttributes[NSAttributedStringKey.underlineStyle] = NSUnderlineStyle.styleDouble.rawValue 

Cadenas atribuidas

Ahora que comprende los atributos, puede crear cadenas atribuidas.

Inicialización

Hay algunas formas de crear cadenas atribuidas. Si solo necesita una cadena de solo lectura, puede usar NSAttributedString . Aquí hay algunas formas de inicializarlo:

 // Initialize with a string only let attrString1 = NSAttributedString(string: "Hello.") // Initialize with a string and inline attribute(s) let attrString2 = NSAttributedString(string: "Hello.", attributes: [NSAttributedStringKey.myName: "A value"]) // Initialize with a string and separately declared attribute(s) let myAttributes1 = [ NSAttributedStringKey.foregroundColor: UIColor.green ] let attrString3 = NSAttributedString(string: "Hello.", attributes: myAttributes1) 

Si necesita cambiar los atributos o el contenido de la cadena más tarde, debe usar NSMutableAttributedString . Las declaraciones son muy similares:

 // Create a blank attributed string let mutableAttrString1 = NSMutableAttributedString() // Initialize with a string only let mutableAttrString2 = NSMutableAttributedString(string: "Hello.") // Initialize with a string and inline attribute(s) let mutableAttrString3 = NSMutableAttributedString(string: "Hello.", attributes: [NSAttributedStringKey.myName: "A value"]) // Initialize with a string and separately declared attribute(s) let myAttributes2 = [ NSAttributedStringKey.foregroundColor: UIColor.green ] let mutableAttrString4 = NSMutableAttributedString(string: "Hello.", attributes: myAttributes2) 

Cambiar una cadena atribuida

Como ejemplo, creemos la cadena atribuida en la parte superior de esta publicación.

Primero crea un NSMutableAttributedString con un nuevo atributo de fuente.

 let myAttribute = [ NSAttributedStringKey.font: UIFont(name: "Chalkduster", size: 18.0)! ] let myString = NSMutableAttributedString(string: "Swift", attributes: myAttribute ) 

Si está trabajando junto, configure la cadena atribuida a una UITextView (o UILabel ) de esta manera:

 textView.attributedText = myString 

Usted no usa textView.text .

Aquí está el resultado:

enter image description here

A continuación, agregue otra cadena atribuida que no tenga ningún atributo establecido. (Tenga en cuenta que aunque utilicé let para declarar myString arriba, aún puedo modificarlo porque es un NSMutableAttributedString . Esto me parece bastante desagradable y no me sorprendería que esto cambie en el futuro. Déjeme un comentario cuando eso sucede.)

 let attrString = NSAttributedString(string: " Attributed Strings") myString.append(attrString) 

enter image description here

A continuación, seleccionaremos la palabra “Cuerdas”, que comienza en el índice 17 y tiene una longitud de 7 . Tenga en cuenta que este es un NSRange y no un Swift Range . (Consulte esta respuesta para obtener más información sobre rangos). El método addAttribute nos permite poner el nombre de la clave de atributo en el primer punto, el valor del atributo en el segundo punto y el rango en el tercer punto.

 var myRange = NSRange(location: 17, length: 7) // range starting at location 17 with a lenth of 7: "Strings" myString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.red, range: myRange) 

enter image description here

Finalmente, agreguemos un color de fondo. Para addAttributes , addAttributes método addAttributes (observe la s ). Podría agregar múltiples atributos a la vez con este método, pero solo agregaré uno nuevamente.

 myRange = NSRange(location: 3, length: 17) let anotherAttribute = [ NSAttributedStringKey.backgroundColor: UIColor.yellow ] myString.addAttributes(anotherAttribute, range: myRange) 

enter image description here

Tenga en cuenta que los atributos se superponen en algunos lugares. Agregar un atributo no sobrescribe un atributo que ya está allí.

Relacionado

  • Cómo cambiar el texto de un NSMutableAttributedString pero mantener los atributos

Otras lecturas

  • Cómo recuperar los atributos desde una ubicación de toque
  • Guía de progtwigción de cadenas atribuidas (muy informativo pero desafortunadamente solo en Objective-C)

Swift usa el mismo NSMutableAttributedString que Obj-C. Lo crea al pasar el valor calculado como una cadena:

 var attributedString = NSMutableAttributedString(string:"\(calculatedCoffee)") 

Ahora crea la cadena g atribuida (heh). Nota: UIFont.systemFontOfSize(_) ahora es un inicializador failable, por lo que debe ser desenvuelto antes de poder usarlo:

 var attrs = [NSFontAttributeName : UIFont.systemFontOfSize(19.0)!] var gString = NSMutableAttributedString(string:"g", attributes:attrs) 

Y luego añádalo:

 attributedString.appendAttributedString(gString) 

A continuación, puede configurar UILabel para que muestre NSAttributedString de la siguiente manera:

 myLabel.attributedText = attributedString 

Swift: xcode 6.1

  let font:UIFont? = UIFont(name: "Arial", size: 12.0) let attrString = NSAttributedString( string: titleData, attributes: NSDictionary( object: font!, forKey: NSFontAttributeName)) 

Swift 4:

 let attributes = [NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Bold", size: 17)!, NSAttributedStringKey.foregroundColor: UIColor.white] 

Versión Xcode 6 :

 let attriString = NSAttributedString(string:"attriString", attributes: [NSForegroundColorAttributeName: UIColor.lightGrayColor(), NSFontAttributeName: AttriFont]) 

Versión Xcode 9.3 :

 let attriString = NSAttributedString(string:"attriString", attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: AttriFont]) 

Recomiendo encarecidamente usar una biblioteca para cadenas atribuidas. Lo hace mucho más fácil cuando lo desea, por ejemplo, una cadena con cuatro colores diferentes y cuatro fonts diferentes. Aquí está mi favorito Se llama SwiftyAttributes

Si desea crear una cadena con cuatro colores diferentes y fonts diferentes usando SwiftyAttributes:

 let magenta = "Hello ".withAttributes([ .textColor(.magenta), .font(.systemFont(ofSize: 15.0)) ]) let cyan = "Sir ".withAttributes([ .textColor(.cyan), .font(.boldSystemFont(ofSize: 15.0)) ]) let green = "Lancelot".withAttributes([ .textColor(.green), .font(.italicSystemFont(ofSize: 15.0)) ]) let blue = "!".withAttributes([ .textColor(.blue), .font(.preferredFont(forTextStyle: UIFontTextStyle.headline)) ]) let finalString = magenta + cyan + green + blue 

finalString se mostraría como

Muestra como imagen

Funciona bien en beta 6

 let attrString = NSAttributedString( string: "title-title-title", attributes: NSDictionary( object: NSFont(name: "Arial", size: 12.0), forKey: NSFontAttributeName)) 

Swift 2.0

Aquí hay una muestra:

 let newsString: NSMutableAttributedString = NSMutableAttributedString(string: "Tap here to read the latest Football News.") newsString.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleDouble.rawValue], range: NSMakeRange(4, 4)) sampleLabel.attributedText = newsString.copy() as? NSAttributedString 

O

 let stringAttributes = [ NSFontAttributeName : UIFont(name: "Helvetica Neue", size: 17.0)!, NSUnderlineStyleAttributeName : 1, NSForegroundColorAttributeName : UIColor.orangeColor(), NSTextEffectAttributeName : NSTextEffectLetterpressStyle, NSStrokeWidthAttributeName : 2.0] let atrributedString = NSAttributedString(string: "Sample String: Attributed", attributes: stringAttributes) sampleLabel.attributedText = atrributedString 

¡Creé una herramienta en línea que resolverá tu problema! Puede escribir su cadena y aplicar estilos gráficamente y la herramienta le brinda código c objective y rápido para generar esa cadena.

También es de código abierto, así que no dude en extenderlo y enviar mensajes de opinión.

Herramienta de transformador

Github

enter image description here

 func decorateText(sub:String, des:String)->NSAttributedString{ let textAttributesOne = [NSAttributedStringKey.foregroundColor: UIColor.darkText, NSAttributedStringKey.font: UIFont(name: "PTSans-Bold", size: 17.0)!] let textAttributesTwo = [NSAttributedStringKey.foregroundColor: UIColor.black, NSAttributedStringKey.font: UIFont(name: "PTSans-Regular", size: 14.0)!] let textPartOne = NSMutableAttributedString(string: sub, attributes: textAttributesOne) let textPartTwo = NSMutableAttributedString(string: des, attributes: textAttributesTwo) let textCombination = NSMutableAttributedString() textCombination.append(textPartOne) textCombination.append(textPartTwo) return textCombination } 

//Implementación

 cell.lblFrom.attributedText = decorateText(sub: sender!, des: " - \(convertDateFormatShort3(myDateString: datetime!))") 

Para mí, las soluciones anteriores no funcionaron al establecer un color o propiedad específico.

Esto funcionó

 let attributes = [ NSFontAttributeName : UIFont(name: "Helvetica Neue", size: 12.0)!, NSUnderlineStyleAttributeName : 1, NSForegroundColorAttributeName : UIColor.darkGrayColor(), NSTextEffectAttributeName : NSTextEffectLetterpressStyle, NSStrokeWidthAttributeName : 3.0] var atriString = NSAttributedString(string: "My Attributed String", attributes: attributes) 

Swift 2.1 – Xcode 7

 let labelFont = UIFont(name: "HelveticaNeue-Bold", size: 18) let attributes :[String:AnyObject] = [NSFontAttributeName : labelFont!] let attrString = NSAttributedString(string:"foo", attributes: attributes) myLabel.attributedText = attrString 

La mejor manera de acercarse a las Cadenas Atribuidas en iOS es mediante el uso del editor de Texto Atribuido incorporado en el constructor de la interfaz y evite el código duro innecesario NSAtrributedStringKeys en sus archivos de origen.

Posteriormente, puede reemplazar de forma dinámica placehoderls en tiempo de ejecución mediante esta extensión:

 extension NSAttributedString { func replacing(placeholder:String, with valueString:String) -> NSAttributedString { if let range = self.string.range(of:placeholder) { let nsRange = NSRange(range,in:valueString) let mutableText = NSMutableAttributedString(attributedString: self) mutableText.replaceCharacters(in: nsRange, with: valueString) return mutableText as NSAttributedString } return self } } 

Agregue una etiqueta del guión gráfico con texto atribuido que se vea así.

enter image description here

Luego, simplemente actualice el valor cada vez que lo necesite así:

 label.attributedText = initalAttributedString.replacing(placeholder: "", with: newValue) 

Asegúrese de guardar en initalAttributedString el valor original.

Para comprender mejor este enfoque, lea este artículo: https://medium.com/mobile-appetite/text-attributes-on-ios-the-effortless-approach-ff086588173e

Swift 4

 let attributes = [NSAttributedStringKey.font : UIFont(name: CustomFont.NAME_REGULAR.rawValue, size: CustomFontSize.SURVEY_FORM_LABEL_SIZE.rawValue)!] let attributedString : NSAttributedString = NSAttributedString(string: messageString, attributes: attributes) 

Necesita eliminar el valor bruto en swift 4

 extension UILabel{ func setSubTextColor(pSubString : String, pColor : UIColor){ let attributedString: NSMutableAttributedString = self.attributedText != nil ? NSMutableAttributedString(attributedString: self.attributedText!) : NSMutableAttributedString(string: self.text!); let range = attributedString.mutableString.range(of: pSubString, options:NSString.CompareOptions.caseInsensitive) if range.location != NSNotFound { attributedString.addAttribute(NSForegroundColorAttributeName, value: pColor, range: range); } self.attributedText = attributedString } } 
  let attrString = NSAttributedString ( string: "title-title-title", attributes: [NSAttributedStringKey.foregroundColor: UIColor.black]) 

Será realmente fácil resolver su problema con la biblioteca que creé. Se llama Atributika.

 let calculatedCoffee: Int = 768 let g = Style("g").font(.boldSystemFont(ofSize: 12)).foregroundColor(.red) let all = Style.font(.systemFont(ofSize: 12)) let str = "\(calculatedCoffee)g".style(tags: g) .styleAll(all) .attributedString label.attributedText = str 

768g

Puede encontrarlo aquí https://github.com/psharanda/Atributika

Los atributos se pueden configurar directamente en swift 3 …

  let attributes = NSAttributedString(string: "String", attributes: [NSFontAttributeName : UIFont(name: "AvenirNext-Medium", size: 30)!, NSForegroundColorAttributeName : UIColor .white, NSTextEffectAttributeName : NSTextEffectLetterpressStyle]) 

Luego usa la variable en cualquier clase con atributos

 extension String { //MARK: Getting customized string struct StringAttribute { var fontName = "HelveticaNeue-Bold" var fontSize: CGFloat? var initialIndexOftheText = 0 var lastIndexOftheText: Int? var textColor: UIColor = .black var backGroundColor: UIColor = .clear var underLineStyle: NSUnderlineStyle = .styleNone var textShadow: TextShadow = TextShadow() var fontOfText: UIFont { if let font = UIFont(name: fontName, size: fontSize!) { return font } else { return UIFont(name: "HelveticaNeue-Bold", size: fontSize!)! } } struct TextShadow { var shadowBlurRadius = 0 var shadowOffsetSize = CGSize(width: 0, height: 0) var shadowColor: UIColor = .clear } } func getFontifiedText(partOfTheStringNeedToConvert partTexts: [StringAttribute]) -> NSAttributedString { let fontChangedtext = NSMutableAttributedString(string: self, attributes: [NSFontAttributeName: UIFont(name: "HelveticaNeue-Bold", size: (partTexts.first?.fontSize)!)!]) for eachPartText in partTexts { let lastIndex = eachPartText.lastIndexOftheText ?? self.count let attrs = [NSFontAttributeName : eachPartText.fontOfText, NSForegroundColorAttributeName: eachPartText.textColor, NSBackgroundColorAttributeName: eachPartText.backGroundColor, NSUnderlineStyleAttributeName: eachPartText.underLineStyle, NSShadowAttributeName: eachPartText.textShadow ] as [String : Any] let range = NSRange(location: eachPartText.initialIndexOftheText, length: lastIndex - eachPartText.initialIndexOftheText) fontChangedtext.addAttributes(attrs, range: range) } return fontChangedtext } 

}

// Úselo como abajo

  let someAttributedText = "Some Text".getFontifiedText(partOfTheStringNeedToConvert: < #T##[String.StringAttribute]#>)