Swift - Textview 识别点击的单词不起作用

Swift - Textview Identify Tapped Word Not Working

老用户,第一次发帖,所以如果我在提出问题时有任何错误,我深表歉意。我已经为此工作了几个小时,我决定是时候请教专家了。我还搜索了 "answered" 和工作中的每个类似问题,这让我相信它们已经过时了。

我正在尝试从稍后将在代码中使用的 UITextview 中获取点击的单词。比如在文本视图中有一段文字:

"The initial return on time investment is much smaller, due to him trading his upfront cost for sweat-equity in the company, but the potential long-term payout is much greater"。

我希望能够点击一个词,例如'investment'和运行它通过另一个函数来定义它。但是简单地点击这个词,程序就会崩溃,而且我没有收到被点击的词。

我实现了点击手势识别器:

let tap = UITapGestureRecognizer(target: self, action: #selector(tapResponse(_:)))
    tap.delegate = self
    tvEditor.addGestureRecognizer(tap)

然后写函数:2

func tapResponse(recognizer: UITapGestureRecognizer) {
    let location: CGPoint = recognizer.locationInView(tvEditor)
    let position: CGPoint = CGPointMake(location.x, location.y)
    let tapPosition: UITextPosition = tvEditor.closestPositionToPoint(position)!
    let textRange: UITextRange = tvEditor.tokenizer.rangeEnclosingPosition(tapPosition, withGranularity: UITextGranularity.Word, inDirection: 1)!

    let tappedWord: String = tvEditor.textInRange(textRange)!
    print("tapped word : %@", tappedWord)
}

理想情况下,这应该从 Textview 的点击部分获取 location,通过获取 .x & 获取 position。 y,然后在最接近 position 的点查看 Textview,找到 Range 包含粒度的位置(到 return 这个词),并将内容设置为字符串,我目前只是将其打印到控制台。然而,在点击这个词时,我收到了这个崩溃。3

连同“致命错误:在打开可选值时意外发现 nil” 在控制台中。

如有任何帮助,我们将不胜感激。我可能只是遗漏了一些简单的东西,或者它可能会复杂得多。

  • 每当在单词之间的空格处接收到点击时,由TextView编辑的tapPosition return可以为零nil
  • Swift 有一个名为 optional ? 的新运算符,它告诉编译器该变量可能具有 nil 值。如果变量名后不使用?表示变量永远不能有nil值。
  • 在 Swift 中,使用 ! 运算符意味着您强制编译器从可选变量中强制提取值。所以,在那种情况下,如果变量的值为 nil,它会在强制时崩溃。

所以,实际发生的是

  • 您正在创建变量 let tapPosition: UITextPositionlet textRange: UITextRangelet tappedWord: String 不是可选的
  • return方法的类型myTextView.closestPositionToPoint(position)tvEditor.textInRange(textRange)是可选变量UITextPosition?String?
  • 将可选变量的值赋给非可选变量需要!
  • 方法是 returning nil 并且您强制它获取值 ! 导致 CRASH

你能做什么

  • 在强制任何可选变量之前,只需确保它具有使用

    的一些值
    if variable != nil
    {
        print(variable!)
    }
    

正确的方法是

func tapResponse(recognizer: UITapGestureRecognizer) {
    let location: CGPoint = recognizer.locationInView(myTextView)
    let position: CGPoint = CGPointMake(location.x, location.y)
    let tapPosition: UITextPosition? = myTextView.closestPositionToPoint(position)
    if tapPosition != nil {
        let textRange: UITextRange? = myTextView.tokenizer.rangeEnclosingPosition(tapPosition!, withGranularity: UITextGranularity.Word, inDirection: 1)
        if textRange != nil
        {
            let tappedWord: String? = myTextView.textInRange(textRange!)
            print("tapped word : ", tappedWord)
        }
    }
}

Swift 3.0 答案 - 2016 年 7 月 1 日起工作

在我的 ViewDidLoad() 中 -

我使用了之前 VC 中的文本,所以我的变量 "theText" 已经声明了。我包含了一个已注明的示例字符串。

 //Create a variable of the text you wish to attribute.

 let textToAttribute = theText  // or "This is sample text"

 // Break your string in to an array, to loop through it.

    let textToAttributeArray = textToAttribute.componentsSeparatedByString(" ")

 // Define a variable as an NSMutableAttributedString() so you can append to it in your loop. 

    let attributedText = NSMutableAttributedString()


 // Create a For - In loop that goes through each word your wish to attribute.

    for word in textToAttributeArray{

    // Create a pending attribution variable. Add a space for linking back together so that it doesn't looklikethis.

    let attributePending = NSMutableAttributedString(string: word + " ")

    // Set an attribute on part of the string, with a length of the word.

    let myRange = NSRange(location: 0, length: word.characters.count)

    // Create a custom attribute to get the value of the word tapped

    let myCustomAttribute = [ "Tapped Word:": word]

    // Add the attribute to your pending attribute variable

    attributePending.addAttributes(myCustomAttribute, range: myRange)

       print(word)
        print(attributePending)

     //append 'attributePending' to your attributedText variable.

        attributedText.appendAttributedString(attributePending) ///////

        print(attributedText)

    }

textView.attributedText = attributedText // Add your attributed text to textview.

现在我们将添加一个点击手势识别器来注册点击。

let tap = UITapGestureRecognizer(target: self, action: #selector(HandleTap(_:)))
    tap.delegate = self
    textView.addGestureRecognizer(tap) // add gesture recognizer to text view.

现在我们在viewDidLoad()下声明一个函数

func HandleTap(sender: UITapGestureRecognizer) {

    let myTextView = sender.view as! UITextView //sender is TextView
    let layoutManager = myTextView.layoutManager //Set layout manager

    // location of tap in myTextView coordinates

    var location = sender.locationInView(myTextView)
    location.x -= myTextView.textContainerInset.left;
    location.y -= myTextView.textContainerInset.top;

    // character index at tap location
    let characterIndex = layoutManager.characterIndexForPoint(location, inTextContainer: myTextView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

    // if index is valid then do something.
    if characterIndex < myTextView.textStorage.length {

        // print the character index
        print("Your character is at index: \(characterIndex)") //optional character index.

        // print the character at the index
        let myRange = NSRange(location: characterIndex, length: 1)
        let substring = (myTextView.attributedText.string as NSString).substringWithRange(myRange)
        print("character at index: \(substring)")

        // check if the tap location has a certain attribute
        let attributeName = "Tapped Word:" //make sure this matches the name in viewDidLoad()
        let attributeValue = myTextView.attributedText.attribute(attributeName, atIndex: characterIndex, effectiveRange: nil) as? String
        if let value = attributeValue {
            print("You tapped on \(attributeName) and the value is: \(value)")
        }

    }
}

除了@AmitSingh 的回答,这是更新的Swift 3.0 版本

    func didTapTextView(recognizer: UITapGestureRecognizer) {
    let location: CGPoint = recognizer.location(in: textView)
    let position: CGPoint = CGPoint(x: location.x, y: location.y)
    let tapPosition: UITextPosition? = textView.closestPosition(to: position)
    if tapPosition != nil {
        let textRange: UITextRange? = textView.tokenizer.rangeEnclosingPosition(tapPosition!, with: UITextGranularity.word, inDirection: 1)
        if textRange != nil
        {
            let tappedWord: String? = textView.text(in: textRange!)
            print("tapped word : ", tappedWord!)
        }
    }
}

其他代码和他的一样。

希望对您有所帮助!