กึ่งกลาง NSTextAttachment image ถัดจาก 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แต่ภาพจะปรากฏขึ้นเพื่อให้สอดคล้องกับด้านบนของของเซลล์

ภาพหน้าจอปัญหาการชดเชยสิ่งที่แนบมากับข้อความ

ฉันจะเปลี่ยน rect ที่วาดไฟล์แนบได้อย่างไร


ฉันมีคลาสหมวดหมู่สำหรับการมี NSString พร้อม UIImage และในทางกลับกัน github.com/Pradeepkn/TextWithImageเพลิดเพลิน
PradeepKN

คำตอบ:


59

คุณสามารถเปลี่ยนการดูแลรักษาโดย subclassing และเอาชนะ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
Jasper

Y-origin คือฟอนต์ descender ดูคำตอบของฉันด้านล่าง
phatmann

4
คำตอบของ Travis คือโซลูชันที่สะอาดกว่าโดยไม่ต้องมีคลาสย่อย
SwampThingTom

สำหรับรายละเอียดเพิ่มเติมโปรดดูคำตอบของ @ mg-han stackoverflow.com/a/45161058/280503 (ในความคิดของฉันควรเป็นคำตอบที่เลือกไว้สำหรับคำถาม)
gardenofwine

192

คุณสามารถใช้ 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];

รวดเร็ว

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 แต่แบบอักษรและรูปภาพมักจะมีขนาดเล็กแม้ความแตกต่าง 1px ก็ทำให้ภาพดูเหมือนไม่ตรงแนว ผมจึงใช้ฟังก์ชันกลมๆก่อนหาร ทำให้ส่วนของเศษส่วนของค่า y เป็น. 0 หรือ. 5

ในกรณีของคุณความสูงของภาพจะใหญ่กว่าความสูงของแบบอักษร แต่คุณสามารถใช้วิธีเดียวกัน ค่าออฟเซ็ต y จะเป็นลบ และจะวางจากด้านล่างของเส้นฐาน

ใส่คำอธิบายภาพที่นี่


ขอบคุณ!!!!!!!!!!!!!!
อเล็กซ์

107

ลอง- [NSTextAttachment bounds]. ไม่จำเป็นต้องมีคลาสย่อย

สำหรับบริบทฉันกำลังแสดงผล a UILabelเพื่อใช้เป็นรูปภาพไฟล์แนบจากนั้นกำหนดขอบเขตดังนี้: attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height)และเส้นฐานของข้อความภายในรูปภาพป้ายกำกับและข้อความในสตริงที่ประกอบขึ้นตามต้องการ


สิ่งนี้ใช้ได้ตราบเท่าที่คุณไม่จำเป็นต้องปรับขนาดภาพ
phatmann

12
สำหรับ Swift 3.0:attachment.bounds = CGRect(x: 0.0, y: self.font.descender, width: attachment.image!.size.width, height: attachment.image!.size.height)
Andy

ยอดเยี่ยมขอบคุณ! ไม่ทราบเกี่ยวกับ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 (descender) + 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 ดูเหมือนการประมาณที่ดีกว่าสำหรับรูปแบบ / ขนาดฟอนต์เริ่มต้น
Kedar Paranjape

10

@Travis ถูกต้องตรงที่ออฟเซ็ตคือฟอนต์ descender หากคุณต้องการปรับขนาดภาพคุณจะต้องใช้คลาสย่อยของ 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

เราสามารถสร้างส่วนขยายใน 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 กับ attributedString


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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.