Как обнаружить отпечаток на изображении внутри UITextView, содержащий текст, приписываемый HTML [duplicate]

Невозможно объяснить поведение, потому что оно вызывает как неопределенное поведение неопределенного поведения , так и неопределенное поведение , поэтому мы не можем делать какие-либо общие предсказания об этом коде, хотя, если вы прочитайте работу Олве Модала, такие как Deep C и Unspecified и Undefined , иногда вы можете делать хорошие догадки в очень конкретных случаях с конкретным компилятором и средой, но пожалуйста, не делайте этого где-нибудь рядом с производством.

Итак, переходим к неуказанному поведению , в черновик c99 standard раздел 6.5 пункт 3 говорит ( emphasis mine ):

Группировка операторов и операндов обозначается синтаксисом.74) За исключением случаев, указанных ниже (для функции -call (), & amp ;, ||,?: и запятые), порядок оценки подвыражений и порядок, в котором происходят побочные эффекты, не определены.

blockquote>

Итак, когда у нас есть такая строка:

i = i++ + ++i;

, мы не знаем будет ли сначала оцениваться i++ или ++i. Это главным образом для того, чтобы дать компилятору лучшие варианты оптимизации .

Здесь также имеется неопределенное поведение , так как программа модифицирует переменные (i ], u и т. д.) более одного раза между точками последовательности . Из проекта стандартного раздела 6.5 пункта 2 ( emphasis mine ):

Между предыдущей и следующей точкой последовательности объект должен иметь свой запомненное значение, измененное не более одного раза путем оценки выражения. Кроме того, предыдущее значение должно быть считано только для определения хранимого значения.

blockquote>

он ссылается на следующие примеры кода как неопределенные:

i = ++i + 1;
a[i++] = i; 

Всего в этих примерах код пытается изменить объект более одного раза в одной и той же точке последовательности, что закончится с ; в каждом из этих случаев:

i = i++ + ++i;
^   ^       ^

i = (i++);
^    ^

u = u++ + ++u;
^   ^       ^

u = (u++);
^    ^

v = v++ + ++v;
^   ^       ^

Неопределенное поведение определяется в черновик c99 стандарта в разделе 3.4.4 как:

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

blockquote>

и неопределенное поведение определено в разделе 3.4.3 следующим образом:

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

blockquote>

и отмечает, что:

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

blockquote>

13
задан Michał Zygar 11 October 2013 в 14:19
поделиться

4 ответа

Метод делегата работает, но ТОЛЬКО, если вложение имеет изображение в атрибуте изображения И если editable = NO! Поэтому, если у вас есть образ, вставленный в атрибутString из другого места, кажется, что данные заканчиваются тем, что они хранятся в файлеWrapper, и в следующий раз, когда вы добавите атрибут attribring в textView, атрибут изображения равен нулю, а менеджер компоновки или что-то еще получает изображение из файла.

Где-то в документах упоминается, что в NSTextAttachment нет методов для сохранения атрибута изображения.

Чтобы протестировать эту попытку, скопируйте фотографию из приложения «Фото» и вставьте ее в свой текстовый рисунок, теперь, если вы удерживаете ее пальцем, вы должны увидеть всплывающее меню по умолчанию. Теперь, если вы сохраните этот богатый текст, скажете в объект Core Data, а затем извлечете его, атрибут изображения будет равен нулю, но данные изображения будут в attachment.fileWrapper.regularFileContents

. Это боль, и я бы с удовольствием знать намерения инженеров. Таким образом, у вас есть два варианта.

  1. Создайте свой собственный NSTextAttachment и включите методы для архивирования изображения и других настроек (ПОЖАЛУЙСТА, ПОКАЖИТЕ МЕНЯ, ЧТО СЛИШКОМ, КОГДА ВЫ НАПИСАЕТЕ ЭТО ОДИН ВЫХОД)
  2. Каждый раз, ваша строка обратно в textView вы найдете все вложения и воссоздали атрибут изображения так: attachment.image = [UIImage imageWithData: attachment.fileWrapper.regularFileContents];

Помните о побочном эффекте Это делает недействительным fileWrapper. Я хочу изменить размер изображения, но также сохранить оригинал, чтобы я не потерял полное разрешение. Я думаю, что единственный способ сделать это может заключаться в подклассе NSTextAttachment.

EDIT:

Я выяснил, как создать пользовательские NSTextAttachments - вот ссылка для заинтересованных http://ossh.com.au/design-and- технология / разработка программного обеспечения / реализация-rich-text-with-images-on-os-x-and-ios /

EDIT 2: настройка меню в режиме редактирования см. в разделе следующие документы Apple, проблема «touchEnded» никогда не кажется вызванной, поэтому вам, возможно, придется попробовать использовать touchhesBegan. Однако осторожно, что вы не вмешиваетесь в поведение редактирования по умолчанию.

https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/AddingCustomEditMenuItems/AddingCustomEditMenuItems.html

Обратите внимание, что в приведенном ниже коде вам нужно будет добавить код после комментария // selection management, чтобы определить, какой символ был затронут, проверьте, является ли это особым текстовым вложением, а затем измените меню редактирования или предпринять некоторые другие действия.

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *theTouch = [touches anyObject];

    if ([theTouch tapCount] == 2  && [self becomeFirstResponder]) {

        // selection management code goes here...

        // bring up edit menu.
        UIMenuController *theMenu = [UIMenuController sharedMenuController];
        CGRect selectionRect = CGRectMake (currentSelection.x, currentSelection.y, SIDE, SIDE);
        [theMenu setTargetRect:selectionRect inView:self];
        [theMenu setMenuVisible:YES animated:YES];

    }
}

В качестве альтернативы вы можете добавить пользовательское меню, добавив пункт меню, а затем изменив метод canPerformAction.

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    LOG(@"canPerformAction: called");

    if (action == @selector(viewImage)) {
       // Check the selected character is the special text attachment character

       return YES;
    }
   return NO;
}

Вот код добавления, но его немного суетливый. Второй метод просто отключает меню редактирования по умолчанию, если обнаружено вложение.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    FLOG(@"touchesBegan:withEvent: called");

    if (self.selectedRange.location != NSNotFound) {
        FLOG(@" selected location is %d", self.selectedRange.location);

        int ch;

        if (self.selectedRange.location >= self.textStorage.length) {
            // Get the character at the location
            ch = [[[self textStorage] string] characterAtIndex:self.selectedRange.location-1];
        } else {
            // Get the character at the location
            ch = [[[self textStorage] string] characterAtIndex:self.selectedRange.location];
        }

        if (ch == NSAttachmentCharacter) {
            FLOG(@" selected character is %d, a TextAttachment", ch);
        } else {
            FLOG(@" selected character is %d", ch);
        }
    }

}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    FLOG(@"canPerformAction: called");

        FLOG(@" selected location is %d", self.selectedRange.location);
        FLOG(@" TextAttachment character is %d", NSAttachmentCharacter);

        if (self.selectedRange.location != NSNotFound) {

            int ch;

            if (self.selectedRange.location >= self.textStorage.length) {
                // Get the character at the location
                ch = [[[self textStorage] string] characterAtIndex:self.selectedRange.location-1];
            } else {
                // Get the character at the location
                ch = [[[self textStorage] string] characterAtIndex:self.selectedRange.location];
            }

            if (ch == NSAttachmentCharacter) {
                FLOG(@" selected character is %d, a TextAttachment", ch);
                return NO;
            } else {
                FLOG(@" selected character is %d", ch);
            }

            // Check for an attachment
            NSTextAttachment *attachment = [[self textStorage] attribute:NSAttachmentAttributeName atIndex:self.selectedRange.location effectiveRange:NULL];
            if (attachment) {
                FLOG(@" attachment attribute retrieved at location %d", self.selectedRange.location);
                return NO;
            }
            else
                FLOG(@" no attachment at location %d", self.selectedRange.location);
        }
    return [super canPerformAction:action withSender:sender];
}
3
ответ дан Duncan Groenewald 24 August 2018 в 19:45
поделиться

Apple делает это очень сложно. Как отмечают другие, метод делегата вызывается, но только тогда, когда isEditable является false, или когда пользователь делает нажатие и удерживает вложение. Если вы хотите получить информацию о простом взаимодействии с краном во время редактирования, забудьте об этом.

Я пошел по путям touchesBegan: и hitTest:, как с проблемами. Способы касания называются после , [[8] уже обработали взаимодействие, а hitTest: слишком грубо, потому что оно противоречит первому статусу ответчика и т. Д.

] Мое решение в конце концов было распознавателями жестов. Apple использует их внутри, что объясняет, почему touchesBegan: в действительности не является жизнеспособным: распознаватели жестов уже обработали событие.

Я создал новый класс распознавания жестов для использования с UITextView. Он просто проверяет местоположение крана, и если это приложение, оно обрабатывает его. Я делаю все другие распознаватели жестов подчиненными моему, поэтому мы сначала смотрим на события, а другие только вступают в игру, если наша неудача.

Класс распознавателя жестов ниже, наряду с расширение для добавления к UITextView. Я добавляю его в свой подкласс UITextView в awakeFromNib, вот так. (Вам не нужно использовать подкласс, если у вас его нет.)

override func awakeFromNib() {
    super.awakeFromNib()

    let recognizer = AttachmentTapGestureRecognizer(target: self, action: #selector(handleAttachmentTap(_:)))
    add(recognizer)

, и я обрабатываю действие, вызывая существующий метод UITextViewDelegate textView(_:,shouldInteractWith:,in:,interaction:). Вы можете так же легко поместить код обработки непосредственно в действие, а не использовать делегат.

@IBAction func handleAttachmentTap(_ sender: AttachmentTapGestureRecognizer) {
    let _ = delegate?.textView?(self, shouldInteractWith: sender.attachment!, in: NSRange(location: sender.attachmentCharacterIndex!, length: 1), interaction: .invokeDefaultAction)
}

Вот основной класс.

import UIKit
import UIKit.UIGestureRecognizerSubclass

/// Recognizes a tap on an attachment, on a UITextView.
/// The UITextView normally only informs its delegate of a tap on an attachment if the text view is not editable, or a long tap is used.
/// If you want an editable text view, where you can short cap an attachment, you have a problem.
/// This gesture recognizer can be added to the text view, and will add requirments in order to recognize before any built-in recognizers.
class AttachmentTapGestureRecognizer: UIGestureRecognizer {

    /// Character index of the attachment just tapped
    private(set) var attachmentCharacterIndex: Int?

    /// The attachment just tapped
    private(set) var attachment: NSTextAttachment?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        attachmentCharacterIndex = nil
        attachment = nil

        let textView = view as! UITextView
        if touches.count == 1, let touch = touches.first, touch.tapCount == 1 {
            let point = touch.location(in: textView)
            let glyphIndex: Int? = textView.layoutManager.glyphIndex(for: point, in: textView.textContainer, fractionOfDistanceThroughGlyph: nil)
            let index: Int? = textView.layoutManager.characterIndexForGlyph(at: glyphIndex ?? 0)
            if let characterIndex = index, characterIndex < textView.textStorage.length {
                if NSAttachmentCharacter == (textView.textStorage.string as NSString).character(at: characterIndex) {
                    attachmentCharacterIndex = characterIndex
                    attachment = textView.textStorage.attribute(.attachment, at: characterIndex, effectiveRange: nil) as? NSTextAttachment
                    state = .recognized
                } else {
                    state = .failed
                }
            }
        } else {
            state = .failed
        }
    }
}

extension UITextView {

    /// Add an attachment recognizer to a UITTextView
    func add(_ attachmentRecognizer: AttachmentTapGestureRecognizer) {
        for other in gestureRecognizers ?? [] {
            other.require(toFail: attachmentRecognizer)
        }
        addGestureRecognizer(attachmentRecognizer)
    }

}

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

0
ответ дан Drew McCormack 24 August 2018 в 19:45
поделиться

Swift 3 answer:

func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange) -> Bool {
    return true
}

Убедитесь, что у вас есть textView isEditable = false, isSelectable = true и isUserInteractionEnabled = true. Ответ Duncan не упоминал isUserInteractionEnabled, это должно быть true, иначе оно не будет работать.

Вы можете сделать это программно (textView.isEditable = false) или через инспектор атрибутов:

2
ответ дан Josh O'Connor 24 August 2018 в 19:45
поделиться

Используйте hitTest, чтобы получить штрих в подклассе UITextView. Это позволяет избежать проблем со стандартными функциями редактирования. Из позиции получим индекс символа, а затем проверьте символ для вложения.

0
ответ дан scottdev 24 August 2018 в 19:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: