我知道这个问题已经在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函数(识别器)中的参数传递到选择器中。但是,我读到您只能传递函数,不能传递任何参数。
您需要将添加UITapGestureRecognizer到UITextView您希望能够点击的。您目前正在将添加UITapGestureRecognizer到ViewController的中view。这就是演员阵容让您陷入困境的原因。您正在尝试将UIView转换为UITextView。
UITapGestureRecognizer
UITextView
ViewController
view
UIView
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textTapped)) tapGesture.numberOfTapsRequired = 1 myTextView.addGestureRecognizer(tapGesture)
从技术上讲,它recognizer.view是一个可选类型(UIView!),可以是nil,但是似乎textTapped()没有将您命名为未设置的类型。同样,layoutManager类型为NSLayoutManager!。为了安全起见,Swift的方法是:
recognizer.view
UIView!
nil
textTapped()
layoutManager
NSLayoutManager!
guard let textView = recognizer.view as? UITextView, let layoutManager = textView.layoutManager else { return } // code using textView and layoutManager goes here
实际上,如果您以此方式编写,就不会崩溃,因为UIViewto 的条件强制转换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:
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)") } }