Получить постучал слово из UITextView в Swift

Я знаю, что эта проблема была решена в Objective-C, но я не видел никакого решения в Swift. Я пытался преобразовать код решения из этого поста, но я получаю ошибки:

func textTapped(recognizer: UITapGestureRecognizer){

    var textView: UITextView = recognizer.view as UITextView
    var layoutManager: NSLayoutManager = textView.layoutManager
    var location: CGPoint = recognizer.locationInView(textView)
    location.x -= textView.textContainerInset.left
    location.y -= textView.textContainerInset.top

    var charIndex: Int
    charIndex = layoutManager.characterIndexForPoint(location, inTextContainer: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

    if charIndex < textView.textStorage.length {
        // do the stuff
        println(charIndex)
    }
}

Я думаю, что проблема в этой строке (см. Ошибку здесь):

 var textView: UITextView = recognizer.view as UITextView

... который я преобразовал из Objective-C на основе этой строки:

 UITextView *textView = (UITextView *)recognizer.view;

Наконец, я также сомневаюсь, как эта функция должна вызываться. Насколько я понимаю, функция должна быть передана в Selector в viewDidLoad(), вот так:

 let aSelector: Selector = "textTapped:"   

 let tapGesture = UITapGestureRecognizer(target: self, action: aSelector)
 tapGesture.numberOfTapsRequired = 1
 view.addGestureRecognizer(tapGesture)

Поскольку я получаю вышеупомянутую ошибку, я не уверен, что это будет работать. Но я думаю, что мне нужно было бы передать параметр в функции textTapped (распознаватель) в селектор, а также. Тем не менее, я прочитал, что вы можете передать только функцию, а не какие-либо параметры.

3 ответа

Решение

Вам нужно добавить UITapGestureRecognizer к UITextView что вы хотите иметь возможность нажать. Вы добавляете UITapGestureRecognizer на ваш ViewController"s view, Вот почему актерский состав доставляет вам неприятности. Вы пытаетесь разыграть UIView к UITextView,

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textTapped))
tapGesture.numberOfTapsRequired = 1
myTextView.addGestureRecognizer(tapGesture)

технически recognizer.view это необязательный тип (UIView!) и может быть nil, но кажется маловероятным, что ваш textTapped() будет называться это не было установлено. Аналогично layoutManager имеет тип NSLayoutManager!, Чтобы быть в безопасности, хотя, способ Swift сделать это:

guard let textView = recognizer.view as? UITextView, let layoutManager = textView.layoutManager else {
    return
}
// code using textView and layoutManager goes here

На самом деле, если бы вы написали это так, вы бы не разбились, потому что UIView в UITextView не удалось бы.

Чтобы все это работало тогда, добавьте атрибуты к вашей приписанной строке, которые вы извлечете в своей подпрограмме textTapped:

var beginning = NSMutableAttributedString(string: "To the north you see a ")
var attrs = [NSFontAttributeName: UIFont.systemFontOfSize(19.0), "idnum": "1", "desc": "old building"]
var condemned = NSMutableAttributedString(string: "condemned building", attributes: attrs)
beginning.appendAttributedString(condemned)
attrs = [NSFontAttributeName: UIFont.systemFontOfSize(19.0), "idnum": "2", "desc": "lake"]
var lake = NSMutableAttributedString(string: " on a small lake", attributes: attrs)
beginning.appendAttributedString(lake)
myTextView.attributedText = beginning

Вот полный textTapped:

@objc func textTapped(recognizer: UITapGestureRecognizer) {
    guard let textView = recognizer.view as? UITextView, let layoutManager = textView.layoutManager else {
        return
    }
    var location: CGPoint = recognizer.locationInView(textView)
    location.x -= textView.textContainerInset.left
    location.y -= textView.textContainerInset.top

    /* 
    Here is what the Documentation looks like :

    Returns the index of the character falling under the given point,    
    expressed in the given container's coordinate system.  
    If no character is under the point, the nearest character is returned, 
    where nearest is defined according to the requirements of selection by touch or mouse.  
    This is not simply equivalent to taking the result of the corresponding 
    glyph index method and converting it to a character index, because in some 
    cases a single glyph represents more than one selectable character, for example an fi ligature glyph.
    In that case, there will be an insertion point within the glyph, 
    and this method will return one character or the other, depending on whether the specified 
    point lies to the left or the right of that insertion point.  
    In general, this method will return only character indexes for which there 
    is an insertion point (see next method).  The partial fraction is a fraction of the distance 
    from the insertion point logically before the given character to the next one, 
    which may be either to the right or to the left depending on directionality.
    */
    var charIndex = layoutManager.characterIndexForPoint(location, inTextContainer: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

    guard charIndex < textView.textStorage.length else {
        return
    }

    var range = NSRange(location: 0, length: 0)
    if let idval = textView.attributedText?.attribute("idnum", atIndex: charIndex, effectiveRange: &range) as? NSString {
        print("id value: \(idval)")
        print("charIndex: \(charIndex)")
        print("range.location = \(range.location)")
        print("range.length = \(range.length)")
        let tappedPhrase = (textView.attributedText.string as NSString).substringWithRange(range)
        print("tapped phrase: \(tappedPhrase)")
        var mutableText = textView.attributedText.mutableCopy() as NSMutableAttributedString
        mutableText.addAttributes([NSForegroundColorAttributeName: UIColor.redColor()], range: range)
        textView.attributedText = mutableText
    }
    if let desc = textView.attributedText?.attribute("desc", atIndex: charIndex, effectiveRange: &range) as? NSString {
        print("desc: \(desc)")
    }
}

Для Swift 3.0 ИЛИ выше

добавить жест касания в UITextView

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapOnTextView(_:)))
textView.addGestureRecognizer(tapGesture)

добавить метод обработчика крана

@objc private final func tapOnTextView(_ tapGesture: UITapGestureRecognizer){

  let point = tapGesture.location(in: textView)
  if let detectedWord = getWordAtPosition(point)
  {

  }
}

получить слово из точки

private final func getWordAtPosition(_ point: CGPoint) -> String?{
if let textPosition = textView.closestPosition(to: point)
{
  if let range = textView.tokenizer.rangeEnclosingPosition(textPosition, with: .word, inDirection: 1)
  {
    return textView.text(in: range)
  }
}
return nil}

Быстрое решение 5>:

      // Add this somewhere in the init of the UITextView subclass
let tapGesture = UITapGestureRecognizer(
    target: self,
    action: #selector(textTapped)
)
tapGesture.numberOfTapsRequired = 1
addGestureRecognizer(tapGesture)

// A method in your UITextView subclass
func textTapped(recognizer: UITapGestureRecognizer) {
    let point = recognizer.location(in: self)

    guard
        let textPosition = closestPosition(to: point),
        let range = tokenizer.rangeEnclosingPosition(
            textPosition,
            with: .word,
            inDirection: .init(rawValue: 1)
        ) else {
        // Happens if the user taps on the timestamp, it is inside the textview, but it is filled with spaces
        return
    }

    print(text(in: range) ?? "Not found, this should not happen")
}
Другие вопросы по тегам