Centro NSTextAttachment imagen al lado de una sola línea UILabel


Me gustaría añadir una imagen NSTextAttachment a mi cadena atribuida y centrarla verticalmente.

He utilizado el siguiente código para crear mi cadena

        NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:DDLocalizedString(@"title.upcomingHotspots") attributes:attrs];
        NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
        attachment.image = [[UIImage imageNamed:@"help.png"] imageScaledToFitSize:CGSizeMake(14.f, 14.f)];
        cell.textLabel.attributedText = [str copy];

Sin embargo, la imagen parece alinearse con la parte superior de la etiqueta de texto de la celda.

introduzca la descripción de la imagen aquí

¿Cómo puedo cambiar el rect en el que se dibuja el adjunto?

Author: Jakub Truhlář, 2014-09-29

9 answers

Puede cambiar el rect subclasificando NSTextAttachment y sobrescribiendo attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex:. Ejemplo:

- (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex {
    CGRect bounds;
    bounds.origin = CGPointMake(0, -5);
    bounds.size = self.image.size;
    return bounds;
}

No Es una solución perfecta. Tienes que averiguar el origen-Y "a simple vista" y si cambias la fuente o el tamaño del icono, probablemente querrás cambiar el origen-Y. Pero no pude encontrar una mejor manera, excepto poniendo el icono en una vista de imagen separada (que tiene sus propias desventajas).

 41
Author: rob mayoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-02 21:46:34

Intenta - [NSTextAttachment bounds]. No se requiere subclase.

Para el contexto, estoy renderizando un UILabel para su uso como la imagen de archivo adjunto, luego estableciendo los límites de esta manera: attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height) y las líneas de base del texto dentro de la imagen de la etiqueta y el texto en la línea de cadena atribuida como se desee.

 87
Author: Travis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-05 22:22:36

Puede usar el capHeight de la fuente.

Objective-C

NSTextAttachment *icon = [[NSTextAttachment alloc] init];
UIImage *iconImage = [UIImage imageNamed:@"icon.png"];
[icon setBounds:CGRectMake(0, roundf(titleFont.capHeight - iconImage.size.height)/2.f, iconImage.size.width, iconImage.size.height)];
[icon setImage:iconImage];
NSAttributedString *iconString = [NSAttributedString attributedStringWithAttachment:icon];
[titleText appendAttributedString:iconString];

Swift

let iconImage = UIImage(named: "icon.png")!
var icon = NSTextAttachment()
icon.bounds = CGRect(x: 0, y: (titleFont.capHeight - iconImage.size.height).rounded() / 2, width: iconImage.size.width, height: iconImage.size.height)
icon.image = iconImage
let iconString = NSAttributedString(attachment: icon)
titleText.append(iconString)

La imagen adjunta se representa en la línea base del texto. Y el eje y de la misma se invierte como el sistema de coordenadas gráficas del núcleo. Si desea mover la imagen hacia arriba, establezca bounds.origin.y en positivo.

La imagen debe estar alineada verticalmente en el centro con la altura de la cabeza del texto. Así que necesitamos establecer el bounds.origin.y a (capHeight - imageHeight)/2.

Evitando algunos irregulares efecto sobre la imagen, debemos redondear la parte de la fracción de la y. Pero las fuentes y las imágenes suelen ser pequeñas, incluso 1px diferencia hace que la imagen parece desalineada. Así que apliqué la función redonda antes de dividir. Hace que la fracción parte del valor y a .0 o .5

En su caso, la altura de la imagen es mayor que la altura de la fuente. Pero puedes usar la misma manera. El valor de desplazamiento y será negativo. Y será dispuesto desde abajo de la línea de base.

introduzca la descripción de la imagen aquí

 79
Author: MG Han,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-19 03:44:00

Encontré una solución perfecta para esto, funciona como un encanto para mí, sin embargo, tienes que probarlo tú mismo (probablemente la constante depende de la resolución del dispositivo y tal vez lo que sea;)

func textAttachment(fontSize: CGFloat) -> NSTextAttachment {
    let font = UIFont.systemFontOfSize(fontSize) //set accordingly to your font, you might pass it in the function
    let textAttachment = NSTextAttachment()
    let image = //some image
    textAttachment.image = image
    let mid = font.descender + font.capHeight
    textAttachment.bounds = CGRectIntegral(CGRect(x: 0, y: font.descender - image.size.height / 2 + mid + 2, width: image.size.width, height: image.size.height))
    return textAttachment
}

Debería funcionar y no debería estar borroso de ninguna manera (gracias a CGRectIntegral)

 56
Author: borchero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-01 18:17:04

¿Qué pasa con:

CGFloat offsetY = -10.0;

NSTextAttachment *attachment = [NSTextAttachment new];
attachment.image = image;
attachment.bounds = CGRectMake(0.0, 
                               offsetY, 
                               attachment.image.size.width, 
                               attachment.image.size.height);

No se necesita subclase

 31
Author: Jakub Truhlář,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-22 21:49:28

@ Travis es correcto que el desplazamiento es el descendedor de fuentes. Si también necesita escalar la imagen, necesitará usar una subclase de NSTextAttachment. A continuación se muestra el código, que se inspiró en este artículo. También lo publiqué como un gist.

import UIKit

class ImageAttachment: NSTextAttachment {
    var verticalOffset: CGFloat = 0.0

    // To vertically center the image, pass in the font descender as the vertical offset.
    // We cannot get this info from the text container since it is sometimes nil when `attachmentBoundsForTextContainer`
    // is called.

    convenience init(_ image: UIImage, verticalOffset: CGFloat = 0.0) {
        self.init()
        self.image = image
        self.verticalOffset = verticalOffset
    }

    override func attachmentBoundsForTextContainer(textContainer: NSTextContainer, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
        let height = lineFrag.size.height
        var scale: CGFloat = 1.0;
        let imageSize = image!.size

        if (height < imageSize.height) {
            scale = height / imageSize.height
        }

        return CGRect(x: 0, y: verticalOffset, width: imageSize.width * scale, height: imageSize.height * scale)
    }
}

Utilizar como sigue:

var text = NSMutableAttributedString(string: "My Text")
let image = UIImage(named: "my-image")!
let imageAttachment = ImageAttachment(image, verticalOffset: myLabel.font.descender)
text.appendAttributedString(NSAttributedString(attachment: imageAttachment))
myLabel.attributedText = text
 9
Author: phatmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-22 22:33:35

Si tienes un ascendente muy grande y quieres centrar la imagen (centro de la altura de la tapa) como yo prueba esto

let attachment: NSTextAttachment = NSTextAttachment()
attachment.image = image
if let image = attachment.image{
    let y = -(font.ascender-font.capHeight/2-image.size.height/2)
    attachment.bounds = CGRect(x: 0, y: y, width: image.size.width, height: image.size.height).integral
}

El cálculo de y es como la imagen de abajo

introduzca la descripción de la imagen aquí

Tenga en cuenta que el valor y es 0 porque queremos que la imagen se desplace hacia abajo desde el origen

Si quieres que esté en el medio de toda la etiqueta.Utilice este valor y:

let y = -((font.ascender-font.descender)/2-image.size.height/2)
 5
Author: IndyZa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-03 19:40:43

Podemos hacer una extensión en swift 4 que genere un adjunto con una imagen centrada como esta:

extension NSTextAttachment {
    static func getCenteredImageAttachment(with imageName: String, and 
    font: UIFont?) -> NSTextAttachment? {
        let imageAttachment = NSTextAttachment()
    guard let image = UIImage(named: imageName),
        let font = font else { return nil }

    imageAttachment.bounds = CGRect(x: 0, y: (font.capHeight - image.size.height).rounded() / 2, width: image.size.width, height: image.size.height)
    imageAttachment.image = image
    return imageAttachment
    }
}

Entonces puedes hacer la llamada enviando el nombre de la imagen y la fuente:

let imageAttachment = NSTextAttachment.getCenteredImageAttachment(with: imageName,
                                                                   and: youLabel?.font)

Y luego añadir el archivo imageAttachment a la cadena attributedString

 2
Author: Oscar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-17 12:28:16

Por favor use-lineFrag.Tamaño.altura / 5.0 para la altura de los límites. Esto centra exactamente la imagen y se alinea con el texto para todo el tamaño de las fuentes

override func attachmentBoundsForTextContainer(textContainer: NSTextContainer, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect
{
    var bounds:CGRect = CGRectZero

    bounds.size = self.image?.size as CGSize!
    bounds.origin = CGPointMake(0, -lineFrag.size.height/5.0);

    return bounds;
}
 0
Author: Harish Kumar Kailas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-16 05:40:59