如何检测NSTextAttachment上的触摸

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何检测NSTextAttachment上的触摸相关的知识,希望对你有一定的参考价值。

检测用户在ios上点击NSTextAttachment的最佳方法是什么?

我认为其中一种方法是检查carret位置上的角色是否是NSAttachmentCharacter,但它似乎并不正确。

我也尝试过UITextViewDelegate方法:-(BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange但是当textView.editable=YES没有调用它

答案

委托方法确实有效,但只有附件在图像属性中有图像并且如果editable = NO!因此,如果你有一个图像粘贴到来自其他地方的attributionString,似乎数据最终存储在fileWrapper中,下次你将attributedString放回textView时,image属性为nil,布局管理器或其他任何东西都会得到图片来自fileWrapper。

在文档的某处,它确实提到NSTextAttachment中没有方法来保持图像属性的持久性。

要测试此操作,请尝试从Photo应用程序复制照片并将其粘贴到textView中,现在如果您按住手指,则应该会弹出默认菜单。现在,如果你保存这个富文本,比如说进入一个核心数据实体,然后检索它,图像属性将是零,但图像数据将在attachment.fileWrapper.regularFileContents

这很痛苦,我很想知道工程师的意图。所以你有两个选择。

  1. 创建您自己的自定义NSTextAttachment并包含用于存档图像和其他设置的方法(请告诉我,当你想到这一点时太多了)
  2. 每次将字符串放回textView之前,您都会找到所有附件并重新创建图像属性,如下所示: attachment.image = [UIImage imageWithData:attachment.fileWrapper.regularFileContents];

请记住,这样做的副作用是使fileWrapper无效。我想调整图像的大小,但也保留原始图像,所以我没有松开全分辨率。我认为这样做的唯一方法可能是继承NSTextAttachment。

编辑:

我想出了如何创建自定义NSTextAttachments - 这里是一个链接感兴趣的http://ossh.com.au/design-and-technology/software-development/implementing-rich-text-with-images-on-os-x-and-ios/

编辑2:要在编辑模式下自定义菜单,请参阅以下Apple文档,问题是“touchEnded”似乎永远不会被调用,因此您可能必须尝试使用​​touchesBegan。但请注意,不要干扰默认的编辑行为。

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回答:

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

确保你的textView isEditable = falseisSelectable = trueisUserInteractionEnabled = true。 Duncan的答案没有提及isUserInteractionEnabled,这必须是true,否则它不会工作。

您可以通过编程方式(textView.isEditable = false)或通过属性检查器执行此操作:enter image description here enter image description here

另一答案

Apple让这真的很难。正如其他人指出的那样,委托方法被调用,但只有当isEditablefalse时,或者当用户点击并按住附件时。如果您想在编辑过程中了解简单的点按互动,请忘掉它。

我沿着touchesBegan:hitTest:路走下去,两个都有问题。在UITextView已经处理了交互之后调用了触摸方法,并且hitTest:太粗糙,因为它与第一响应者状态混淆等等。

我的解决方案最终是手势识别器。 Apple正在内部使用这些,这解释了为什么touchesBegan:首先不是真正可行的:手势识别器已经处理了这个事件。

我创建了一个新的手势识别器类,用于UITextView。它只是检查水龙头的位置,如果是附件,它会处理它。我将所有其他手势识别器从属于我的手势识别器,因此我们先看看事件,其他人只有在我们的事件失败时才会发挥作用。

下面是手势识别器类,以及将其添加到UITextView的扩展。我在UITextViewawakeFromNib子类中添加它,就像这样。 (如果没有子类,则无需使用子类。)

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
    pr

以上是关于如何检测NSTextAttachment上的触摸的主要内容,如果未能解决你的问题,请参考以下文章

如何检测 UIView 上的触摸并检测单击哪个 UIView 类似于 UIButton 上的触摸?

如何检测 UICollectionView 上的触摸?

如何检测 UINavigationController 工具栏背景上的触摸

如何检测 UITableView 上的“快速触摸”以切换到/从全屏?

如何检测 UI 图像上的触摸?

SpriteKit:如何检测从节点外部开始的节点上的触摸