Hình ảnh trung tâm NSTextAttachment bên cạnh dòng UILabel đơn


116

Tôi muốn nối NSTextAttachmenthình ảnh vào chuỗi được gán của mình và đặt nó ở giữa theo chiều dọc.

Tôi đã sử dụng đoạn mã sau để tạo chuỗi của mình:

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

Tuy nhiên, hình ảnh dường như thẳng hàng với đỉnh của tế bào textLabel.

văn bản đính kèm vấn đề bù ảnh chụp màn hình

Làm thế nào tôi có thể thay đổi trực tràng trong đó các tập tin đính kèm được rút ra?


Tôi có một lớp thể loại để có NSString với UIImage và ngược lại. github.com/Pradeepkn/TextWithImage Thưởng thức.
PradeepKN

Câu trả lời:


58

Bạn có thể thay đổi trực tràng bằng cách phân lớp NSTextAttachmentvà ghi đè attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex:. Thí dụ:

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

Đó không phải là một giải pháp hoàn hảo. Bạn phải tìm ra nguồn gốc Y bằng mắt và nếu bạn thay đổi phông chữ hoặc kích thước biểu tượng, có thể bạn sẽ muốn thay đổi nguồn gốc Y. Nhưng tôi không thể tìm thấy một cách tốt hơn, ngoại trừ bằng cách đặt biểu tượng trong chế độ xem hình ảnh riêng biệt (có nhược điểm riêng).


2
Không biết tại sao họ lại bỏ phiếu này, nó đã giúp tôi rất nhiều +1
Jasper

Nguồn gốc Y là trình giải mã phông chữ. Xem câu trả lời của tôi dưới đây.
phatmann

4
Câu trả lời của Travis là một giải pháp sạch hơn mà không cần phân lớp.
SwampT BreathTom

Để biết thêm chi tiết, hãy xem stackoverflow @ mg-han 's stackoverflow.com/a/45161058/280503 (Theo tôi nên là câu trả lời được chọn cho câu hỏi.)
gardenofwine

191

Bạn có thể sử dụng capHeight của phông chữ.

Mục tiêu-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];

Nhanh

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)

Hình ảnh đính kèm được hiển thị trên đường cơ sở của văn bản. Và trục y của nó bị đảo ngược giống như hệ tọa độ đồ họa lõi. Nếu bạn muốn di chuyển hình ảnh lên trên, hãy đặt thành bounds.origin.ytích cực.

Hình ảnh phải được căn giữa theo chiều dọc với độ sáng của văn bản. Vì vậy, chúng ta cần phải thiết lập bounds.origin.yđể (capHeight - imageHeight)/2.

Để tránh một số hiệu ứng lởm chởm trên hình ảnh, chúng ta nên làm tròn phần phân số của y. Nhưng phông chữ và hình ảnh thường nhỏ, thậm chí chênh lệch 1px làm cho hình ảnh trông như bị sai lệch. Vì vậy, tôi áp dụng chức năng tròn trước khi chia. Nó làm cho phần phân số của giá trị y thành .0 hoặc .5

Trong trường hợp của bạn, chiều cao hình ảnh lớn hơn capHeight của phông chữ. Nhưng bạn có thể sử dụng cùng một cách. Giá trị offset y sẽ âm. Và nó sẽ được đặt ra từ bên dưới của đường cơ sở.

nhập mô tả hình ảnh ở đây


CẢM ƠN BẠN!!!!!!!!!!!!!!
Alex

107

Hãy thử - [NSTextAttachment bounds]. Không yêu cầu phân lớp.

Đối với ngữ cảnh, tôi hiển thị một UILabelmục đích sử dụng làm hình ảnh đính kèm, sau đó đặt giới hạn như vậy: attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height)và đường cơ sở của văn bản trong hình ảnh nhãn và văn bản trong chuỗi ký tự được sắp xếp theo ý muốn.


Điều này hoạt động miễn là bạn không cần phải phóng to hình ảnh.
phatmann

12
Dành cho Swift 3.0:attachment.bounds = CGRect(x: 0.0, y: self.font.descender, width: attachment.image!.size.width, height: attachment.image!.size.height)
Andy

Thật sự cảm ơn! Không biết về descendertài sản của UIFont!
Bến

61

Mặc dù vậy, tôi đã tìm thấy một giải pháp hoàn hảo cho vấn đề này, nó hoạt động như một cơ duyên đối với tôi, tuy nhiên bạn phải tự mình thử nó (có lẽ là hằng số phụ thuộc vào độ phân giải của thiết bị và có thể là bất cứ điều gì;)

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
}

Nên làm việc và không nên bị mờ theo bất kỳ cách nào (nhờ CGRectIntegral)


Cảm ơn bạn đã đăng bài này, nó dẫn tôi đến một cách tiếp cận khá tốt. Tôi nhận thấy rằng bạn đang thêm một phép thuật 2 vào phép tính tọa độ y của bạn.
Ben

2
Đây là những gì tôi đã sử dụng cho tính toán y của mình: descender + (abs (descender) + capHeight) / 2 - iconHeight / 2
Ben

Tại sao +2 cho nguồn gốc Y?
William LeGate

@WilliamLeGate Tôi thực sự không biết, chỉ cần dùng thử và nó hoạt động với tất cả các kích thước phông chữ mà tôi đã thử nghiệm (những cái tôi cần) ..
borchero

Tốt chết tiệt ... Câu trả lời này là tuyệt vời.
GGirotto

37

Thế còn:

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

Không cần phân lớp


2
Hoạt động tốt hơn so với sử dụng self.font.descender (có giá trị mặc định là ~ 4 trên trình giả lập iPhone 4s chạy iOS 8). -10 trông giống như một xấp xỉ tốt hơn cho kiểu / kích thước phông chữ mặc định.
Kedar Paranjape

10

@Travis là chính xác rằng phần bù là bộ giảm dần phông chữ. Nếu bạn cũng cần chia tỷ lệ hình ảnh, bạn sẽ cần sử dụng một lớp con của NSTextAttachment. Dưới đây là mã, được lấy cảm hứng từ bài viết này . Tôi cũng đăng nó như một ý chính .

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

Sử dụng như sau:

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

Nếu bạn có độ cao rất lớn và muốn căn giữa hình ảnh (trung tâm của chiều cao nắp) như tôi hãy thử điều này

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
}

Tính toán y như hình dưới đây

nhập mô tả hình ảnh ở đây

Lưu ý rằng giá trị y là 0 vì chúng tôi muốn hình ảnh chuyển xuống từ gốc

Nếu bạn muốn nó ở giữa toàn bộ nhãn. Sử dụng giá trị y này:

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

7

Chúng ta có thể tạo một tiện ích mở rộng trong swift 4 để tạo tệp đính kèm với hình ảnh chính giữa như thế này:

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

Sau đó, bạn có thể thực hiện cuộc gọi gửi tên của hình ảnh và phông chữ:

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

Và sau đó nối hình ảnhAttachment vào AttributionString


1

Trong trường hợp của tôi, gọi sizeToFit () đã giúp. Trong nhanh 5.1

Bên trong nhãn tùy chỉnh của bạn:

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

Vui lòng sử dụng -lineFrag.size.height / 5.0 cho chiều cao giới hạn. Điều này chính xác tập trung hình ảnh và căn chỉnh với văn bản cho tất cả các kích thước của phông chữ

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.0không đúng Thay vào đó là phông chữ giảm dần.
phatmann
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.