如何知道用户点击了TextView的哪一行 [英] How to know which line of a TextView the user has tapped on
问题描述
我正在构建一个具有多行文本编辑器(UIKit中为UITextView,而SwiftUI中为TextEditor)的应用程序.当用户点击多行文本的任何一行时,我应该知道那是哪一行,因此我可以打印该行或执行其他任何任务.
I'm building an app that has a multi-line text editor (UITextView in UIKit, and TextEditor in SwiftUI). When the user tap on any line of the multi-line text, I should know which line is that, so I can print that line or do any other task.
是否有办法知道用户点击了UITextView的哪一行(使用 textViewDidChangeSelection
委托函数或其他方法)?
Is there a way to know which line of a UITextView the user has tapped on (using textViewDidChangeSelection
delegate function or anything else)?
我当时正在考虑将整个文本存储在一个数组中(每行是一个元素),并连续更新该数组,但是问题仍然存在,我如何知道应该点击哪一行来相应地更新数组?
I was thinking of storing the whole text in an Array (each line is an element), and updating the array continuously, but the problem still exists, how shall I know which line has been tapped on to update the array accordingly?
注意:我正在使用属性字符串为每行赋予不同的样式.
Note: I'm using Attributed Strings to give every line different styling.
推荐答案
Swift 5 UIKit解决方案:
尝试向textView添加轻击手势并检测点击的单词:
Try to add tap gesture to textView and detect the word tapped:
let textView: UITextView = {
let tv = UITextView()
tv.text = "ladòghjòdfghjdaòghjjahdfghaljdfhgjadhgf ladjhgf dagf adjhgf adgljdgadsjhladjghl dgfjhdjgh jdahgfljhadlghal dkgjafahd fgjdsfgh adh"
tv.textColor = .black
tv.font = .systemFont(ofSize: 16, weight: .semibold)
tv.textContainerInset = UIEdgeInsets(top: 40, left: 0, bottom: 0, right: 0)
tv.translatesAutoresizingMaskIntoConstraints = false
return tv
}()
在那之后,在viewDidLoad中设置点击手势和约束:
After that in viewDidLoad set tap gesture and constraints:
let tap = UITapGestureRecognizer(target: self, action: #selector(tapResponse(recognizer:)))
textView.addGestureRecognizer(tap)
view.addSubview(textView)
textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
现在调用该函数以检测单词:
now call the function to detect word:
@objc func tapResponse(recognizer: UITapGestureRecognizer) {
let location: CGPoint = recognizer.location(in: textView)
let position: CGPoint = CGPoint(x: location.x, y: location.y)
guard let position2 = textView.closestPosition(to: position) else { return }
let tapPosition: UITextPosition = position2
guard let textRange: UITextRange = textView.tokenizer.rangeEnclosingPosition(tapPosition, with: UITextGranularity.word, inDirection: UITextDirection(rawValue: 1)) else {return}
let tappedWord: String = textView.text(in: textRange) ?? ""
print("tapped word:", tappedWord)
}
使用属性字符串是同一回事.
with Attributed Strings it is the same thing.
更新
添加此功能以检测行:
@objc func didTapTextView(recognizer: UITapGestureRecognizer) {
if recognizer.state == .recognized {
let location = recognizer.location(ofTouch: 0, in: textView)
if location.y >= 0 && location.y <= textView.contentSize.height {
guard let font = textView.font else {
return
}
let line = Int((location.y - textView.textContainerInset.top) / font.lineHeight) + 1
print("Line is \(line)")
}
}
}
别忘了在点击时更改被调用的功能
don't forget to change called function on tap:
let tap = UITapGestureRecognizer(target: self, action: #selector(didTapTextView(recognizer:)))
编辑以在TAP上显示光标并设置其位置
在didTapTextView函数中显示光标在点击位置添加结束状态到识别器,设置文本视图是可编辑的并成为第一响应者,这是您的didTapTextView函数的样子:
to show the cursor on tap location add ended state to recognizer in didTapTextView function set text view is editable and become first responder, this is your didTapTextView function look like:
@objc func didTapTextView(recognizer: UITapGestureRecognizer) {
if recognizer.state == .ended {
textView.isEditable = true
textView.becomeFirstResponder()
let location = recognizer.location(in: textView)
if let position = textView.closestPosition(to: location) {
let uiTextRange = textView.textRange(from: position, to: position)
if let start = uiTextRange?.start, let end = uiTextRange?.end {
let loc = textView.offset(from: textView.beginningOfDocument, to: position)
let length = textView.offset(from: start, to: end)
textView.selectedRange = NSMakeRange(loc, length)
}
}
}
if recognizer.state == .recognized {
let location = recognizer.location(ofTouch: 0, in: textView)
if location.y >= 0 && location.y <= textView.contentSize.height {
guard let font = textView.font else {
return
}
let line = Int((location.y - textView.textContainerInset.top) / font.lineHeight) + 1
print("Line is \(line)")
}
}
}
在我的示例中,我将光标颜色设置为绿色以使其更加可见,为此它设置了textView着色颜色(我在TextView属性文本上添加了该颜色):
in my example I set cursor color to green to make it much visible, to do it set textView tint color (I added on TextView attributed text):
let textView: UITextView = {
let tv = UITextView()
tv.textContainerInset = UIEdgeInsets(top: 40, left: 0, bottom: 0, right: 0)
tv.translatesAutoresizingMaskIntoConstraints = false
tv.tintColor = .green // cursor color
let attributedString = NSMutableAttributedString(string: "ladòghjòdfghjdaòghjjahdfghaljdfhgjadhgf ladjhgf dagf adjhgf adgljdgadsjhladjghl", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .regular), .foregroundColor: UIColor.red])
attributedString.append(NSAttributedString(string: " dgfjhdjgh jdahgfljhadlghal dkgjafahd fgjdsfgh adh jsfgjskbfgfs gsfjgbjasfg ajshg kjshafgjhsakhg shf", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .bold), .foregroundColor: UIColor.black]))
tv.attributedText = attributedString
return tv
}()
这是结果:
这篇关于如何知道用户点击了TextView的哪一行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!