web-dev-qa-db-fra.com

Centrer l'image NSTextAttachment à côté d'une ligne unique UILabel

J'aimerais ajouter une image NSTextAttachment à ma chaîne attribuée et la centrer verticalement.

J'ai utilisé le code suivant pour créer ma chaîne

        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];

Cependant, l'image semble s'aligner en haut du libellé textLabel de la cellule.

enter image description here

Comment puis-je changer le rect dans lequel l'attachement est dessiné?

101
Sean Danzeiser

Vous pouvez changer le recto en sous-classant NSTextAttachment et en remplaçant attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex:. Exemple:

- (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;
}

Ce n'est pas une solution parfaite. Vous devez déterminer l’origine des Y à l’œil et, si vous modifiez la taille de la police ou de l’icône, vous souhaiterez probablement modifier l’origine des Y. Mais je ne pouvais pas trouver de meilleur moyen, sauf en plaçant l'icône dans une vue d'image séparée (ce qui a ses propres inconvénients).

53
rob mayoff

Vous pouvez utiliser le capHeight de la police.

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];

Rapide

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)

L'image de pièce jointe est rendue sur la ligne de base du texte. Et son axe y est inversé comme le système de coordonnées graphique principal. Si vous souhaitez déplacer l'image vers le haut, définissez le paramètre bounds.Origin.y au positif.

L'image doit être alignée verticalement au centre avec la majuscule du texte. Nous devons donc définir le bounds.Origin.y à (capHeight - imageHeight)/2.

En évitant certains effets irréguliers sur l'image, nous devrions arrondir la partie fraction de y. Mais les polices et les images sont généralement petites, même une différence de 1 px fait que l’image semble mal alignée. J'ai donc appliqué la fonction round avant de diviser. Cela fait que la partie fraction de la valeur y soit 0,0 ou 0,5

Dans votre cas, la hauteur de l’image est plus grande que la majuscule de la police. Mais vous pouvez utiliser la même manière. La valeur de décalage y sera négative. Et il sera présenté à partir du bas de la ligne de base.

enter image description here

144
MG Han

Essayez - [NSTextAttachment bounds]. Aucun sous-classement requis.

Pour le contexte, je suis en train de rendre un UILabel pour l'utiliser comme image de pièce jointe, puis je fixe les limites comme suit: attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height) et les lignes de base du texte dans l'image de l'étiquette et du texte dans la chaîne attribuée s'alignent comme souhaité .

100
Travis

J'ai trouvé une solution parfaite à ce problème, fonctionne comme un charme pour moi, cependant, vous devez l'essayer vous-même (la constante dépend probablement de la résolution de l'appareil et peut-être de ce que vous voulez;)

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
}

Devrait fonctionner et ne devrait en aucun cas être flou (grâce à CGRectIntegral)

59
borchero

Qu'en est-il de:

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);

Aucun sous-classement nécessaire

34
Jakub Truhlář

@ Travis a raison de dire que le décalage correspond au descendant de police. Si vous devez également redimensionner l'image, vous devrez utiliser une sous-classe de NSTextAttachment. Ci-dessous, le code qui a été inspiré par cet article . Je l'ai aussi posté en tant que 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)
    }
}

Utilisez comme suit:

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
10
phatmann

Si vous avez un très gros ascendant et que vous voulez centrer l'image (centre de la hauteur de la casquette) comme moi, essayez ceci

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
}

Le calcul de y est comme l'image ci-dessous

enter image description here

Notez que la valeur y est 0 car nous voulons que l’image passe de l’origine

Si vous voulez qu'il se trouve au milieu de l'étiquette entière, utilisez cette valeur y:

let y = -((font.ascender-font.descender)/2-image.size.height/2)
8
IndyZa

Nous pouvons créer une extension dans Swift 4) qui génère une pièce jointe avec une image centrée comme celle-ci:

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
    }
}

Ensuite, vous pouvez faire l'appel en envoyant le nom de l'image et la police:

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

Et ensuite, ajoutez l'imageAttachment à la chaîne attribuée

4
Oscar

Veuillez utiliser -lineFrag.size.height/5.0 pour la hauteur des limites. Cela centre exactement l'image et est aligné avec le texte pour toutes les tailles de polices

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