Подтвердить что ты не робот

NSAttributedString, изменить общий шрифт, но сохранить все остальные атрибуты?

Скажем, у меня есть NSMutableAttributedString.

Строка имеет различное сочетание форматирования:

Вот пример:

Эта строка - адская перемена в iOS, она действительно отстой.

Однако сам по себе шрифт - это не тот шрифт, который вам нужен.

Я хочу:

для каждого символа измените этот символ на определенный шрифт (скажем, Avenir)

НО,

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

Как, черт возьми, ты это делаешь?


Примечание:

если вы просто добавляете атрибут "Avenir" по всему диапазону: он просто удаляет все остальные диапазоны атрибутов, вы теряете все форматирование. К сожалению, атрибуты не являются, на самом деле "аддитивные".

4b9b3361

Ответ 1

Поскольку ответ rmaddy не работал для меня (f.fontDescriptor.withFace(font.fontName) не сохраняет черты, как жирные), вот обновленная версия Swift 4, которая также включает в себя обновление цвета:

extension NSMutableAttributedString {
    func setFontFace(font: UIFont, color: UIColor? = nil) {
        beginEditing()
        self.enumerateAttribute(.font, in: NSRange(location: 0, length: self.length)) { (value, range, stop) in
            if let f = value as? UIFont, let newFontDescriptor = f.fontDescriptor.withFamily(font.familyName).withSymbolicTraits(f.fontDescriptor.symbolicTraits) {
                let newFont = UIFont(descriptor: newFontDescriptor, size: font.pointSize)
                removeAttribute(.font, range: range)
                addAttribute(.font, value: newFont, range: range)
                if let color = color {
                    removeAttribute(.foregroundColor, range: range)
                    addAttribute(.foregroundColor, value: color, range: range)
                }
            }
        }
        endEditing()
    }
}

Заметки

Проблема с f.fontDescriptor.withFace(font.fontName) заключается в том, что она удаляет символические черты, такие как italic, bold или compressed, поскольку по какой-то причине они переопределяют те, у которых есть черты шрифта по умолчанию. Почему это так полностью ускользает от меня, это может быть даже надзор над частью Apple. или это "не ошибка, а функция", потому что мы бесплатно получаем новые шрифты.

Поэтому нам нужно создать дескриптор шрифта, который имеет символические черты от исходного дескриптора шрифта шрифта: .withSymbolicTraits(f.fontDescriptor.symbolicTraits). Поддержки к rmaddy для исходного кода, на котором я повторил.

Я уже отправил это в производственном приложении, где мы разбираем строку HTML через NSAttributedString.DocumentType.html а затем изменяем шрифт и цвет с помощью расширения выше. На данный момент проблем нет.

Ответ 2

Вот гораздо более простая реализация, которая сохраняет все атрибуты на месте, включая все атрибуты шрифта, кроме того, что позволяет изменять шрифт.

Обратите внимание, что это использует только шрифт (имя) переданного шрифта. Размер сохраняется от существующего шрифта. Если вы хотите также изменить все существующие размеры шрифта на новый размер, измените f.pointSize на font.pointSize.

extension NSMutableAttributedString {
    func replaceFont(with font: UIFont) {
        beginEditing()
        self.enumerateAttribute(.font, in: NSRange(location: 0, length: self.length)) { (value, range, stop) in
            if let f = value as? UIFont {
                let ufd = f.fontDescriptor.withFamily(font.familyName).withSymbolicTraits(f.fontDescriptor.symbolicTraits)!
                let newFont = UIFont(descriptor: ufd, size: f.pointSize)
                removeAttribute(.font, range: range)
                addAttribute(.font, value: newFont, range: range)
            }
        }
        endEditing()
    }
}

И использовать его:

let someMutableAttributedString = ... // some attributed string with some font face you want to change
someMutableAttributedString.replaceFont(with: UIFont.systemFont(ofSize: 12))

Ответ 3

Важный -

rmaddy изобрел совершенно новую технику для этой раздражающей проблемы в iOS.

Ответ от manmal - окончательная усовершенствованная версия.

Чисто для исторической записи здесь примерно то, как вы собираетесь делать это в старые времена...


// carefully convert to "our" font - "re-doing" any other formatting.
// change each section BY HAND.  total PITA.

func fixFontsInAttributedStringForUseInApp() {

    cachedAttributedString?.beginEditing()

    let rangeAll = NSRange(location: 0, length: cachedAttributedString!.length)

    var boldRanges: [NSRange] = []
    var italicRanges: [NSRange] = []

    var boldANDItalicRanges: [NSRange] = [] // WTF right ?!

    cachedAttributedString?.enumerateAttribute(
            NSFontAttributeName,
            in: rangeAll,
            options: .longestEffectiveRangeNotRequired)
                { value, range, stop in

                if let font = value as? UIFont {

                    let bb: Bool = font.fontDescriptor.symbolicTraits.contains(.traitBold)
                    let ii: Bool = font.fontDescriptor.symbolicTraits.contains(.traitItalic)

                    // you have to carefully handle the "both" case.........

                    if bb && ii {

                        boldANDItalicRanges.append(range)
                    }

                    if bb && !ii {

                        boldRanges.append(range)
                    }

                    if ii && !bb {

                        italicRanges.append(range)
                    }
                }
            }

    cachedAttributedString!.setAttributes([NSFontAttributeName: font_f], range: rangeAll)

    for r in boldANDItalicRanges {
        cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fBOTH, range: r)
    }

    for r in boldRanges {
        cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fb, range: r)
    }

    for r in italicRanges {
        cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fi, range: r)
    }

    cachedAttributedString?.endEditing()
}

,


Сноска. Просто для ясности по связанному моменту. Такие вещи неизбежно начинаются как строка HTML. Здесь примечание о том, как преобразовать строку, NSattributedString html в NSattributedString..., вы получите хорошие диапазоны атрибутов (курсив, полужирный и т.д.), Но шрифты будут шрифтами, которые вам не нужны.

fileprivate extension String {
    func htmlAttributedString() -> NSAttributedString? {
        guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
        guard let html = try? NSMutableAttributedString(
            data: data,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
            documentAttributes: nil) else { return nil }
        return html
    }
}  

,

Даже эта часть задания является нетривиальной, требуется некоторое время для обработки. На практике вы должны использовать его во избежание мерцания.

Ответ 4

Obj-C версия ответа @manmal

@implementation NSMutableAttributedString (Additions)

- (void)setFontFaceWithFont:(UIFont *)font color:(UIColor *)color {
    [self beginEditing];
    [self enumerateAttribute:NSFontAttributeName
                     inRange:NSMakeRange(0, self.length)
                     options:0
                  usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {
                      UIFont *oldFont = (UIFont *)value;
                      UIFontDescriptor *newFontDescriptor = [[oldFont.fontDescriptor fontDescriptorWithFamily:font.familyName] fontDescriptorWithSymbolicTraits:oldFont.fontDescriptor.symbolicTraits];
                      UIFont *newFont = [UIFont fontWithDescriptor:newFontDescriptor size:font.pointSize];
                      if (newFont) {
                          [self removeAttribute:NSFontAttributeName range:range];
                          [self addAttribute:NSFontAttributeName value:newFont range:range];
                      }

                      if (color) {
                          [self removeAttribute:NSForegroundColorAttributeName range:range];
                          [self addAttribute:NSForegroundColorAttributeName value:newFont range:range];
                      }
                  }];
    [self endEditing];
}

@end

Ответ 5

Получить attributedText на label как NSMutableAttributedString и добавить дополнительные атрибуты

- (void)viewDidLoad {
    [super viewDidLoad];
    NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:@"This is NSMutableAttributedString example" attributes:@{ NSForegroundColorAttributeName : [UIColor redColor] }];
    self.lbl.attributedText = attrStr;
}
- (IBAction)changeFont:(UIButton *)sender {
    NSMutableAttributedString *attrStr = [[self.lbl attributedText] mutableCopy];
    [attrStr addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"YourFontName" size:26] range:NSMakeRange(0, attrStr.length)];
    self.lbl.attributedText = attrStr;
}

Ответ 6

Быстро Используйте следующее расширение NSMutableAttributedString, чтобы изменить шрифт с существующими атрибутами. Это довольно просто, так как нам просто нужно установить атрибут NSAttributedString.Key.font.

extension NSMutableAttributedString {
    @discardableResult
    func setFont(_ font: UIFont, range: NSRange? = nil)-> NSMutableAttributedString {
        let range = range ?? NSMakeRange(0, self.length)
        self.addAttributes([.font: font], range: range)
        return self
    }
}

Обычаи:

let attrString = NSMutableAttributedString("Hello...")
attrString.setFont(UIFont.systemFont(ofSize: 20)

Задача: этот довольно просто изменить/установить атрибут, не отражая другие атрибуты. Проблема заключается в том, что пользователь хочет изменить атрибут стиля абзаца [если свойство значения массива] без изменения других значений атрибутов абзаца.

Ответ 7

Будет ли допустимо, чтобы UITextField выполнял работу?

Как это, учитывая attributedString и newfont:

let textField = UITextField()
textField.attributedText = attributedString
textField.font = newFont
let resultAttributedString = textField.attributedText

Извините, я ошибся, он хранит "Атрибуты символов", такие как NSForegroundColorAttributeName, например цвет, но не UIFontDescriptorSymbolicTraits, которые описывают жирный, курсив, сгущенный и т.д.

Они принадлежат шрифту, а не "Атрибутам персонажа". Поэтому, если вы меняете шрифт, вы также меняете свои черты. Извините, но мое предлагаемое решение не работает. Целевой шрифт должен иметь все свойства, доступные в качестве исходного шрифта для этого.