поиск местоположения определенных символов в UILabel на iPhone

У меня есть UILabel с некоторым текстом, скажем «Hello World abcdefg». Метка может состоять из нескольких строк, разных размеров шрифта и т. Д.

Вопрос: Как мне найти координаты всех букв "d" в этом UILabel.

Логический первый шаг - найти положение этих символов в строке (UILabel.text), но затем как мне перевести это в координаты, когда они фактически нарисованы на экране

Идея состоит в том, чтобы найти эти координаты и нарисовать что-то особенное поверх этого персонажа (в основном, чтобы покрыть его нестандартным изображением).


person Nick    schedule 07.07.2010    source источник
comment
Этот другой ответ содержит часть того, что вы, вероятно, будете необходимость.   -  person progrmr    schedule 07.07.2010


Ответы (3)


Основные инструменты для измерения текста на iPhone находятся в UIStringDrawing.h, но ни один из них не делает то, что вам нужно. Вам в основном придется перебирать подстроки по одному символу за раз, измеряя каждую. Когда строка переносится (результат становится выше), разделите ее после последнего символа, который не был перенесен, и добавьте высоту строки к вашей координате y.

- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode;
person drawnonward    schedule 07.07.2010
comment
вздох, это будет немного поработать, но я думаю, это лучший вариант. Спасибо за ответ! - person Nick; 08.07.2010

С момента выхода iOS 7.0 методы изменились. Попробуй это

- (CGFloat)charactersOffsetBeforeDayPartOfLabel {
    NSRange range = [[self stringFromDate:self.currentDate] rangeOfString:[NSString stringWithFormat:@"%i",[self dayFromDate:self.currentDate]]];
    NSString *chars = [[self stringFromDate:self.currentDate] substringToIndex:range.location];
    NSMutableArray *arrayOfChars = [[NSMutableArray alloc]init];
    [chars enumerateSubstringsInRange:NSMakeRange(0, [chars length]) options:(NSStringEnumerationByComposedCharacterSequences) usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
        [arrayOfChars addObject:substring];
    }];
    CGFloat charsOffsetTotal = 0;
    for (NSString *i in arrayOfChars){
        NSDictionary *attributes = @{NSFontAttributeName: [UIFont fontWithName:@"Helvetica Neue" size:16.0f]};
        charsOffsetTotal += [i sizeWithAttributes:attributes].width;
    }
    return charsOffsetTotal;
}
person Mikhail Baynov    schedule 17.03.2014
comment
Зачем нужно рассчитывать размер каждого персонажа в отдельности? Почему бы просто не получить размер всей подстроки сразу? - person rmaddy; 19.11.2014

Вот и все:

fileprivate let selfSizing = UILabel()

class DualColorLabel: UILabel
{
    var filled: UIColor?
    var unfilled: UIColor?
    var origin: String?
    var widths: [CGFloat] = []
    var fuckupLockup = false
    override var text: String? {
        didSet {
            if fuckupLockup {
                print ("SDBOFLAG-13822 wtf?")
            }
        }
    }
    func setupColorsAndText(filled: UIColor,
                    unfilled: UIColor)
    {
        self.filled = filled
        self.unfilled = unfilled
        guard let text = origin, text.count > 0 else {
            assertionFailure("usage error")
            return
        }
        guard font != nil else {
            usageError()
            return
        }
        for index in 1...text.count {
            let s = String.Index(utf16Offset: 0, in: text)
            let e = String.Index(utf16Offset: index, in: text)
            let beginning = text[s..<e]
            let p = String(beginning)
            
            selfSizing.font = font
            selfSizing.text = p
            let size = selfSizing.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
            let width = size.width
            widths.append(width)
        }
    }
    
    func setupfill(adjusted: CGRect)
    {
        assert(adjusted.origin.x <= 0, "fixed this code for fill in the middle: currently supported only fill at start")
        let endOffset = adjusted.width + adjusted.origin.x
        guard let font = self.font else {
            usageError()
            return
        }
        guard let origin = origin, let filled = filled,
              let unfilled = unfilled else {
            usageError()
            return
        }
        var idx = String.Index(utf16Offset: origin.count, in: origin)
        for (index, width) in widths.enumerated() {
            if endOffset < width {
                idx = String.Index(utf16Offset: index, in: origin)
                print ("SDBOFLAG-13822 index \(index) for text \(origin)")
                break
            }
        }
        let total = NSMutableAttributedString()
        do {
            let s = String.Index(utf16Offset: 0, in: origin)
            let beginning = origin[s..<idx]
            let p = String(beginning)
            print("SDBOFLAG-13822 filled text \(p)")
            let filledAttributes:
                [NSAttributedString.Key : Any] = [NSAttributedString.Key.foregroundColor:
//                                                    UIColor.yellow,
                                                    filled,
                                                  NSAttributedString.Key.font:
                                                    font
                ]
            
            let filledPortion = NSAttributedString(string: p, attributes: filledAttributes)
            total.append(filledPortion)
        }
        let unfilledAttributes:
            [NSAttributedString.Key : Any] = [NSAttributedString.Key.foregroundColor:
//                                                UIColor.blue,
                                              unfilled,
                                              NSAttributedString.Key.font: font]
        let e = String.Index(utf16Offset: origin.count, in: origin)
        let ending = origin[idx..<e]
        let str = String(ending)
        print("SDBOFLAG-13822 unfilled text \(str)")
        let unfilledPortion = NSAttributedString(string: str, attributes: unfilledAttributes)
        total.append(unfilledPortion)

        self.attributedText = total
        fuckupLockup = true
    }
    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */

}

func usageError()
{
    assertionFailure("usage error")
}

Расчет ширины фрагментов выполняется в массиве ширины согласно предоставленным предложениям.

person Anton Tropashko    schedule 10.11.2020