我知道这个问题已经在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;
最后,我也对如何调用此函数表示怀疑。据我了解,该函数应该传递给viewDidLoad()中的Selector,如下所示:
let aSelector: Selector = "textTapped:"
let tapGesture = UITapGestureRecognizer(target: self, action: aSelector)
tapGesture.numberOfTapsRequired = 1
view.addGestureRecognizer(tapGesture)
因为我得到了前面提到的错误,我不确定它是否会起作用。但我想我还需要将textTapped函数(识别器)中的参数传递给Selector。但是,我已经读过你只能传递函数而不是任何参数。
你需要添加 UITapGestureRecognizer
到了 UITextView
你希望能够点击。你现在正在添加 UITapGestureRecognizer
到你的 ViewController
的 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)")
}
}
你需要添加 UITapGestureRecognizer
到了 UITextView
你希望能够点击。你现在正在添加 UITapGestureRecognizer
到你的 ViewController
的 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)
添加tap处理程序方法
@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}