Изображение центра NSTextAttachment рядом с одной строкой UILabel
Я хотел бы добавить изображение 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.
![enter image description here]()
Как изменить прямоугольник, в котором нарисовано вложение?
Ответы
Ответ 1
Вы можете изменить rect путем подклассификации 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-origin "на глаз", и если вы измените шрифт или размер значка, вы, вероятно, захотите изменить Y-origin. Но я не мог найти лучшего способа, за исключением того, что помещал значок в отдельный вид изображения (у которого есть свои недостатки).
Ответ 2
Вы можете использовать capHeight шрифта.
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)
Изображение вложения отображается на базовой линии текста.
И ось Y этого, как ядро графической системы координат.
Если вы хотите переместить изображение вверх, установите bounds.origin.y
в положительное положение.
Изображение должно быть выровнено по центру вертикально по высоте текста.
Поэтому нам нужно установить bounds.origin.y
на (capHeight - imageHeight)/2
.
Чтобы избежать зазубрин, мы должны округлить дробную часть y. Но шрифты и изображения обычно маленькие, даже разница в 1 пиксель делает изображение выровненным. Поэтому я применил функцию округления перед делением. Делает дробную часть значения y равным .0 или .5
В вашем случае высота изображения больше, чем capHeight шрифта. Но вы можете использовать так же. Значение смещения y будет отрицательным. И это будет изложено ниже базовой линии.
![enter image description here]()
Ответ 3
Попробуйте - [NSTextAttachment bounds]
. Не требуется подклассов.
В контексте, я представляю UILabel
для использования в качестве изображения вложения, а затем для определения границ:
attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height)
и исходные тексты в изображении метки и тексте в строке строки с атрибутами по желанию.
Ответ 4
Я нашел идеальное решение для этого, хотя для меня это работает как шарм, однако вы должны попробовать его сами (возможно, константа зависит от разрешения устройства и, возможно, что угодно;)
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
)
Ответ 5
Как насчет:
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);
Подклассы не нужны
Ответ 6
@Travis правильно, что смещение - это descender шрифта. Если вам также необходимо масштабировать изображение, вам нужно будет использовать подкласс NSTextAttachment. Ниже приведен код, который был вдохновлен этой статьей. Я также разместил его как 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)
}
}
Используйте следующее:
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
Ответ 7
Если у вас очень большой асцендент и вы хотите центрировать изображение (в центре высоты шапки), как я, попробуйте это
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)
Ответ 8
Мы можем сделать расширение в Swift 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
Ответ 9
У меня есть класс категории для NSString с UIImage и наоборот.
https://github.com/Pradeepkn/TextWithImage
Enjoy.
Ответ 10
Пожалуйста, используйте -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;
}