Как подкласс 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).