fonts nsattributedstring - ¿Cómo hago una cadena atribuida usando Swift?




text (17)

Estoy tratando de hacer una calculadora de café simple. Necesito mostrar la cantidad de café en gramos. El símbolo "g" para gramos se debe adjuntar a mi UILabel que estoy usando para mostrar la cantidad. Los números en la UILabel están cambiando dinámicamente con la entrada del usuario bien, pero debo agregar una "g" minúscula al final de la cadena que tiene el formato diferente de los números de actualización. La "g" se debe adjuntar 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 en Google mi pequeño corazón.

He buscado en la documentación una cadena atribuida e incluso he degradado un "Creador de cadenas atribuido" de la tienda de aplicaciones, pero el código resultante está en Objective-C y estoy usando Swift. Lo que sería asombroso, y probablemente útil para otros desarrolladores que aprenden este idioma, es un claro ejemplo de cómo crear una fuente personalizada con atributos personalizados utilizando 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 "attribText" es la "g" minúscula con una fuente personalizada que estoy tratando de crear. Tal vez estoy haciendo esto por el camino equivocado. Cualquier ayuda es apreciada!


Answers

Swift 4.2

extension UILabel {

    func boldSubstring(_ substr: String) {
        guard substr.isEmpty == false,
            let text = attributedText,
            let range = text.string.range(of: substr, options: .caseInsensitive) else {
                return
        }
        let attr = NSMutableAttributedString(attributedString: text)
        let start = text.string.distance(from: text.string.startIndex, to: range.lowerBound)
        let length = text.string.distance(from: range.lowerBound, to: range.upperBound)
        attr.addAttributes([NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: self.font.pointSize)],
                           range: NSMakeRange(start, length))
        attributedText = attr
    }
}

La mejor manera de acercarse a Attributed Strings en iOS es utilizando el editor de texto atribuido incorporado en el generador de interfaces y evite las codificaciones NSAtrributedStringKeys en sus archivos de origen.

Más tarde, puede reemplazar dinámicamente placehoderls en tiempo de ejecución utilizando 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 de guión gráfico con texto atribuido que tenga este aspecto.

Luego simplemente actualiza el valor cada vez que lo necesites así:

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

Asegúrese de guardar en initalAttributedString el valor original.

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


Swift: xcode 6.1

    let font:UIFont? = UIFont(name: "Arial", size: 12.0)

    let attrString = NSAttributedString(
        string: titleData,
        attributes: NSDictionary(
            object: font!,
            forKey: NSFontAttributeName))

Versión de 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])

Xcode 10, iOS 12, Swift 4 :

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

Esta respuesta ha sido actualizada para Swift 4.0.

Referencia rápida

La forma general para hacer 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

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

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

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

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

let myShadow = NSShadow()
myShadow.shadowBlurRadius = 3
myShadow.shadowOffset = CGSize(width: 3, height: 3)
myShadow.shadowColor = UIColor.gray

let myAttribute = [ NSAttributedStringKey.shadow: myShadow ]

El resto de este post da más detalles para aquellos que 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 tipo. El valor podría ser una fuente, un color, un número entero u otra cosa. Hay muchos atributos estándar en Swift que ya han sido predefinidos. Por ejemplo:

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

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

  • nombre de clave: NSAttributedStringKey.myName , valor: algún tipo.
    Si haces una extension :

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

Creando atributos en Swift

Puede declarar atributos como 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" ]

Anote el valor en rawValue que fue necesario para el valor de estilo de subrayado.

Debido a que los atributos son solo diccionarios, también puede crearlos creando un diccionario vacío y luego agregándole pares clave-valor. Si el valor contendrá varios tipos, entonces tiene que usar Any como tipo. Aquí está el ejemplo de atributos 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 entiendes los atributos, puedes hacer 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 maneras 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 adelante, 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 de atributos

Como ejemplo, vamos a crear la cadena atribuida en la parte superior de esta publicación.

Primero cree 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 a lo largo, establezca la cadena atribuida a un UITextView (o UILabel ) como este:

textView.attributedText = myString

Usted no usa textView.text .

Aquí está el resultado:

Luego, agregue otra cadena atribuida que no tenga ningún conjunto de atributos. (Tenga en cuenta que, aunque solía declarar myString arriba, todavía puedo modificarlo porque es un NSMutableAttributedString . Esto me parece bastante inoportuno y no me sorprendería si esto cambia en el futuro. Déjeme un comentario cuando eso NSMutableAttributedString . sucede.)

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

A continuación, seleccionaremos la palabra "Cadenas", que comienza en el índice 17 y tiene una longitud de 7 . Tenga en cuenta que se trata de un NSRange y no de Swift Range . (Consulte esta respuesta para obtener más información sobre los rangos). El método addAttribute nos permite colocar 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 lugar.

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)

Por último, vamos a añadir un color de fondo. Para addAttributes , addAttributes método addAttributes (tenga en cuenta los s ). Podría agregar varios 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)

Observe 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


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>g</g>".style(tags: g)
    .styleAll(all)
    .attributedString

label.attributedText = str

Lo puedes encontrar aquí https://github.com/psharanda/Atributika


Funciona bien en beta 6

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

 let attrString = NSAttributedString (
            string: "title-title-title",
            attributes: [NSAttributedStringKey.foregroundColor: UIColor.black])

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

//Implementación

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)

¡Creé una herramienta en línea que va a resolver su problema! Puede escribir su cadena y aplicar estilos gráficamente, y la herramienta le proporciona un código objetivo y rápido para generar esa cadena.

También es de código abierto, así que siéntase libre de extenderlo y enviar PR.

Herramienta transformadora

Github


Swift 4:

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

Para mí, las soluciones anteriores no funcionaron al configurar un color o una propiedad específica.

Esto funcionó:

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
}

Recomendaría encarecidamente utilizar una biblioteca para cadenas atribuidas. Lo hace mucho más fácil cuando quiere, por ejemplo, una cadena con cuatro colores diferentes y cuatro fuentes diferentes. Aquí está mi favorito. Se llama SwiftyAttributes

Si quisieras hacer una cadena con cuatro colores diferentes y diferentes fuentes usando los atributos de 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


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


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

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
}

}

// Utilízalo como abajo

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

Forma de extensión

Swift 4

extension String {
    func contains(find: String) -> Bool{
        return self.range(of: find) != nil
    }
    func containsIgnoringCase(find: String) -> Bool{
        return self.range(of: find, options: .caseInsensitive) != nil
    }
}

var value = "Hello world"

print(value.contains("Hello")) // true
print(value.contains("bo"))    // false

print(value.containsIgnoringCase(find: "hello"))    // true
print(value.containsIgnoringCase(find: "Hello"))    // true
print(value.containsIgnoringCase(find: "bo"))       // false

En general, Swift 4 tiene un método, sin embargo, está disponible desde iOS 8.0+

Swift 3.1

Puede escribir la extensión contains: y containsIgnoringCase para String

extension String { 

   func contains(_ find: String) -> Bool{
     return self.range(of: find) != nil
   }

   func containsIgnoringCase(_ find: String) -> Bool{
     return self.range(of: find, options: .caseInsensitive) != nil 
   }
 }

Versión Swift más antigua

extension String {

    func contains(find: String) -> Bool{
       return self.rangeOfString(find) != nil
     }

    func containsIgnoringCase(find: String) -> Bool{
       return self.rangeOfString(find, options: NSStringCompareOptions.CaseInsensitiveSearch) != nil
     }
}

Ejemplo:

var value = "Hello world"

print(value.contains("Hello")) // true
print(value.contains("bo"))    // false

print(value.containsIgnoringCase("hello"))    // true
print(value.containsIgnoringCase("Hello"))    // true
print(value.containsIgnoringCase("bo"))       // false




fonts uilabel swift nsattributedstring