Как подкласс NSTextAttachment?

Вот моя проблема:

Я использую Core Data для хранения расширенного ввода текста из приложений iOS и/или OS X и хотел бы, чтобы изображения вставлялись в NSTextView или UITextView для: а) сохранить свое первоначальное разрешение и b) на дисплее масштабируется, чтобы соответствовать текстуру правильно, что означает масштабирование в зависимости от размера представления на устройстве.

В настоящее время я использую - (void)textStorage:(NSTextStorage *)textStorage didProcessEditing:(NSTextStorageEditActions)editedMask range:(NSRange)editedRange changeInLength:(NSInteger)delta для поиска вложений и затем генерирую изображение с масштабным коэффициентом и назначая его атрибуту textAttachment.image.

Этот вид работ, потому что я просто изменяю масштабный коэффициент, и исходное изображение сохраняется, но я считаю, что более элегантным решением будет использование подкласса NSTextAttachmentContainer и возврат из него CGREct соответствующего размера с помощью

- (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex

Итак, мой вопрос: как мне создать и вставить такой подкласс?

Я использую textStorage: didProcessEditing для итерации по каждому вложению и замену его NSTextAttachmentContainer собственным классом или я могу просто создать категорию, а затем каким-то образом использовать эту категорию для изменения поведения по умолчанию. Последнее кажется гораздо менее навязчивым, но как мне заставить текстовые элементы автоматически использовать эту категорию?

Oops: Только что заметил, что NSTextAttachmentContainer - это протокол, поэтому я предполагаю, что создание категории в NSTextAttachment и переопределение метода выше - это вариант.

Mmm: нельзя использовать категорию для переопределения существующего метода класса, поэтому я думаю, что подкласс - это единственный вариант, в каком случае я могу получить UITextView для использования моего подкласса вложений, или мне нужно перебирать атрибут string, чтобы заменить все NSTextAttachments с экземплярами MYTextAttachment. И каково будет влияние отмены этой строки на OS X, чтобы сказать по умолчанию OS X NSTextAttachment (который отличается от класса iOS)?

Ответы

Ответ 1

Основываясь на этой отличной статье, если вы хотите использовать

- (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex

чтобы масштабировать вложение текста изображения, вы должны создать свой собственный подкласс NSTextAttachment

@interface MYTextAttachment : NSTextAttachment 
@end

с помощью операции масштабирования в реализации:

@implementation MYTextAttachment

- (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex {
    CGFloat width = lineFrag.size.width;

    // Scale how you want
    float scalingFactor = 1.0;   
    CGSize imageSize = [self.image size];   
    if (width < imageSize.width)
        scalingFactor = width / imageSize.width;
    CGRect rect = CGRectMake(0, 0, imageSize.width * scalingFactor, imageSize.height * scalingFactor);

    return rect;
}
@end

на основе

lineFrag.size.width

который дает вам (или, что я понял) ширину, полученную textView, на которой у вас (будет) установлен атрибутный текст "вложение" вашего пользовательского вложения текста.

После создания подкласса NSTextAttachment все, что вам нужно сделать, это использовать его. Создайте его экземпляр, установите изображение, затем создайте с ним новую атрибутированную строку и добавьте его в NSMutableAttributedText для примера:

MYTextAttachment* _textAttachment = [MYTextAttachment new];
_textAttachment.image = [UIImage ... ];

[_myMutableAttributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:_immediateTextAttachment]];

Для информации кажется, что

 - (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex 

вызывается всякий раз, когда текстовое представление запрашивается для ретрансляции.

Надеюсь, что это поможет, хотя оно не отвечает на все аспекты вашей проблемы.

Ответ 2

Swift 3 (на основе @Bluezen answer):

class MyTextAttachment : NSTextAttachment {

    override func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {

        guard let image = self.image else {
            return CGRect.zero
        }

        let height = lineFrag.size.height

        // Scale how you want
        var scalingFactor = CGFloat(0.8)
        let imageSize = image.size
        if height < imageSize.height {
            scalingFactor *= height / imageSize.height
        }
        let rect = CGRect(x: 0, y: 0, width: imageSize.width * scalingFactor, height: imageSize.height * scalingFactor)

        return rect
    }

}

Обратите внимание, что я масштабируюсь по высоте, так как я получал значение ширины lineFrag 10000000 в моем конкретном случае использования. Также обратите внимание, что я заменил scalingFactor = ... на scalingFactor *= ... так, чтобы в этом случае я мог использовать дополнительный, неединичный масштабный коэффициент (0,8).