Центруйте зображення NSTextAttachment поруч з однорядним UILabel


117

Я хотів би додати NSTextAttachmentзображення до атрибутованого рядка і розмістити його по центру вертикально.

Я використовував такий код, щоб створити свій рядок:

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

Однак зображення, схоже, вирівнюється до верхньої частини комірки textLabel.

скріншот проблеми компенсації тексту вкладення тексту

Як я можу змінити пряму, в якій намальовано вкладення?


У мене є категорія категорії для того, щоб мати NSString з UIImage і навпаки. github.com/Pradeepkn/TextWithImage Насолоджуйтесь.
PradeepKN

Відповіді:


59

Ви можете змінити прямокутник, підкласифікувавши NSTextAttachmentта переосмисливши attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex:. Приклад:

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

Це не ідеальне рішення. Ви повинні з'ясувати походження Y "на око", і якщо ви зміните шрифт або розмір значка, ви, ймовірно, захочете змінити походження Y. Але я не міг знайти кращого способу, крім того, як поставити піктограму в окремий вигляд зображення (у якого є свої недоліки).


2
Не маю уявлення, чому вони проголосували за це, мені це допомогло так +1
Джаспер

Походження Y - спадний шрифт. Дивіться мою відповідь нижче.
phatmann

4
Відповідь Тревіса - це більш чисте рішення без підкласу.
SwampThingTom

Більш детально ознайомтеся з відповіддю @ mg-han stackoverflow.com/a/45161058/280503 (На мою думку, це має бути обрана відповідь на питання)
gardenofwine

191

Ви можете використовувати capHeight шрифту.

Ціль-С

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

Швидкий

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)

Зображення вкладеного зображення відображається на базовій лінії тексту. І вісь y її повертається як основна система координат графіки. Якщо ви хочете перемістити зображення вгору, встановіть bounds.origin.yпозитивне.

Зображення слід вирівняти вертикально по центру з верхньою величиною тексту. Таким чином , ми повинні встановити bounds.origin.yв (capHeight - imageHeight)/2.

Уникаючи деякого нерівного ефекту на зображенні, ми повинні округлити частину дробу. Але шрифти та зображення зазвичай невеликі, навіть 1px різниця робить зображення схожим на нерівне. Тому я застосував круглу функцію перед поділом. Це робить частину частки значення y рівним .0 або .5

У вашому випадку висота зображення більша за верхню висоту шрифту. Але можна використовувати той самий спосіб. Значення зміщення y буде від’ємним. І він буде викладений знизу від базової лінії.

введіть тут опис зображення


СПАСИБІ!!!!!!!!!!!!!!
Олексій

107

Спробуйте - [NSTextAttachment bounds]. Підкласи не потрібні.

Для контексту я надаю UILabelдля використання як зображення вкладеного файлу, а потім встановлюю такі межі: attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height)і базові лінії тексту в межах зображення мітки та тексту в атрибутованій рядковій лінійці за бажанням.


Це працює до тих пір, поки вам не потрібно масштабувати зображення.
phatmann

12
Для Свіфта 3.0:attachment.bounds = CGRect(x: 0.0, y: self.font.descender, width: attachment.image!.size.width, height: attachment.image!.size.height)
Енді

Дивовижне, дякую! Не знали про descenderвласність UIFont!
Бен

61

Я знайшов ідеальне рішення для цього, але працює як шарм для мене, однак, ви повинні спробувати це самостійно (напевно, константа залежить від роздільної здатності пристрою і, можливо, будь-чого;)

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
}

Має працювати і не має розмиватися жодним чином (завдяки CGRectIntegral)


Дякую за повідомлення про це, це призвело до мене досить хороший підхід. Я помітив, що ви додаєте дещо чарівний 2 до свого обчислення координат y.
Бен

2
Ось що я використав для свого y-розрахунку: descender + (abs (спадний) + capHeight) / 2 - iconHeight / 2
Ben

Чому +2 для походження Y?
William LeGate

@WilliamLeGate Я справді не знаю, просто спробував це, і він працював на всі тести, які я тестував (потрібні мені) ..
borchero

Прокляте ... Ця відповідь дивовижна.
GGirotto

38

А як на рахунок:

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

Підкласи не потрібні


2
Працює краще, ніж використання self.font.descender (який має за замовчуванням ~ 4 на тренажері iPhone 4s під управлінням iOS 8). -10 виглядає як краще наближення до стилю / розміру шрифту за замовчуванням.
Кедар Паранджапе

10

@Travis вірно, що компенсацією є зменшення шрифту. Якщо вам також потрібно масштабувати зображення, вам потрібно буде використовувати підклас NSTextAttachment. Нижче наведено код, який надихнув цю статтю . Я також розмістив це як суть .

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

Використовуйте наступним чином:

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

Якщо у вас дуже великий підйом і хочете централізувати зображення (центр висоти шапки), як я, спробуйте це

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
}

Розрахунок y - як на малюнку нижче

введіть тут опис зображення

Зауважте, що значення y дорівнює 0, тому що ми хочемо, щоб зображення змістилося вниз від початку

Якщо ви хочете, щоб він знаходився посередині цілої мітки. Використовуйте це значення y:

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

7

Ми можемо зробити розширення у швидкій формі 4, яка генерує додаток із зображенням по центру, як це:

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

Потім ви можете здійснити дзвінок, надіславши ім'я зображення та шрифту:

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

А потім додайте imageAttachment до атрибутованоїString


1

У моєму випадку виклик sizeToFit () допоміг. Швидкий 5.1

Всередині вашої власної етикетки:

func updateUI(text: String?) {
    guard let text = text else {
        attributedText = nil
        return
    }

    let attributedString = NSMutableAttributedString(string:"")

    let textAttachment = NSTextAttachment ()
    textAttachment.image = image

    let sizeSide: CGFloat = 8
    let iconsSize = CGRect(x: CGFloat(0),
                           y: (font.capHeight - sizeSide) / 2,
                           width: sizeSide,
                           height: sizeSide)
    textAttachment.bounds = iconsSize

    attributedString.append(NSAttributedString(attachment: textAttachment))
    attributedString.append(NSMutableAttributedString(string: text))
    attributedText = attributedString

    sizeToFit()
}

0

Будь ласка, використовуйте -lineFrag.size.height / 5,0 для висоти меж. Це точно центрує зображення та вирівнюється з текстом для всіх розмірів шрифтів

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

-lineFrag.size.height/5.0невірно. Натомість це спадний шрифт.
phatmann
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.