将 GestureRecognizer 添加到 String 无法正常工作
Add GestureRecognizer to String not working properly
我有一个 Label
,其中我有两个特定的 words
,应该是 clickable
。它是这样的:
Nutungsbedigungen
和 Datenschutzrichtlinien
应该都是 clickable
。我为实现这一目标所做的是:
设置标签:
func setUpDocumentsLabel(){
var textArray = [String]()
var fontArray = [UIFont]()
var colorArray = [UIColor]()
textArray.append("Mit 'fortfahren' akzeptierst du die")
textArray.append("Nutzungsbedingungen")
textArray.append("und")
textArray.append("Datenschutzrichtlinien.")
fontArray.append(Fonts.regularFontWithSize(size: 13.0))
fontArray.append(Fonts.boldFontWithSize(size: 13.0))
fontArray.append(Fonts.regularFontWithSize(size: 13.0))
fontArray.append(Fonts.boldFontWithSize(size: 13.0))
colorArray.append(Colors.white)
colorArray.append(Colors.white)
colorArray.append(Colors.white)
colorArray.append(Colors.white)
self.documentsLabel.attributedText = getAttributedString(arrayText: textArray, arrayColors: colorArray, arrayFonts: fontArray)
self.documentsLabel.isUserInteractionEnabled = true
let tapgesture = UITapGestureRecognizer(target: self, action: #selector(tappedOnLabel(_ :)))
tapgesture.numberOfTapsRequired = 1
self.documentsLabel.addGestureRecognizer(tapgesture)
}
点击操作:
@objc func tappedOnLabel(_ gesture: UITapGestureRecognizer) {
guard let text = self.documentsLabel.text else { return }
let nutzen = (text as NSString).range(of: "Nutzungsbedingungen")
let daten = (text as NSString).range(of: "Datenschutzrichtlinien")
if gesture.didTapAttributedTextInLabel(label: self.documentsLabel, inRange: nutzen) {
let alertcontroller = UIAlertController(title: "Tapped on", message: "user tapped on Nutzungsbedingungen", preferredStyle: .alert)
let alertAction = UIAlertAction(title: "OK", style: .default) { (alert) in
}
alertcontroller.addAction(alertAction)
self.present(alertcontroller, animated: true)
} else if gesture.didTapAttributedTextInLabel(label: self.documentsLabel, inRange: daten){
let alertcontroller = UIAlertController(title: "Tapped on", message: "user tapped on Datenschutzrichtlinien", preferredStyle: .alert)
let alertAction = UIAlertAction(title: "OK", style: .default) { (alert) in
}
alertcontroller.addAction(alertAction)
self.present(alertcontroller, animated: true)
}
}
getAttributedString:
func getAttributedString(arrayText:[String]?, arrayColors:[UIColor]?, arrayFonts:[UIFont]?) -> NSMutableAttributedString {
let finalAttributedString = NSMutableAttributedString()
for i in 0 ..< (arrayText?.count)! {
let attributes = [NSAttributedString.Key.foregroundColor: arrayColors?[i], NSAttributedString.Key.font: arrayFonts?[i]]
let attributedStr = (NSAttributedString.init(string: arrayText?[i] ?? "", attributes: attributes as [NSAttributedString.Key : Any]))
if i != 0 {
finalAttributedString.append(NSAttributedString.init(string: " "))
}
finalAttributedString.append(attributedStr)
}
return finalAttributedString
}
问题:
strings
不是每个字符都 clickable
! clickable
区的length
因设备而异:
对于Datenschutzrichtlinien
:
iPhone 11: clickable
来自 D - t
iPhone SE: clickable
来自 D - 2nd i
对于Nutzungsbedingungen
:
在每个设备上都很好,但 iPhone SE:仅来自 N - 2nd n
我不知道为什么会这样,如果有人能帮助我,我将不胜感激!
更新:
我正在为 UITapGestureRecognizer
使用这个 extension
:
extension UITapGestureRecognizer {
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
let textStorage = NSTextStorage(attributedString: label.attributedText!)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.location(in: label)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x,
y: locationOfTouchInLabel.y - textContainerOffset.y);
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return NSLocationInRange(indexOfCharacter, targetRange)
}
你在这里遇到的具体问题是因为你的标签有
.textAlignment = .center
但是 didTapAttributedTextInLabel()
代码不知道...它根据从 [ 开始的“und Datenschutzrichtlinien.” 计算位置=30=]标签左边缘.
您可以尝试此方法来修复它 - 快速测试(仅在一台设备上)- 似乎可以解决问题。
在您的 FirstLaunchViewController
class 中,不要将文档标签居中:
let documentsLabel: UILabel = {
let v = UILabel()
// don't do this
//v.textAlignment = .center
v.numberOfLines = 0
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
您似乎只使用了一次 func getAttributedString(...)
,那就是为 documentsLabel
设置属性文本的格式,因此请按如下方式更改它:
func getAttributedString(arrayText:[String]?, arrayColors:[UIColor]?, arrayFonts:[UIFont]?) -> NSMutableAttributedString {
let finalAttributedString = NSMutableAttributedString()
for i in 0 ..< (arrayText?.count)! {
let attributes = [NSAttributedString.Key.foregroundColor: arrayColors?[i], NSAttributedString.Key.font: arrayFonts?[i]]
let attributedStr = (NSAttributedString.init(string: arrayText?[i] ?? "", attributes: attributes as [NSAttributedString.Key : Any]))
if i != 0 {
finalAttributedString.append(NSAttributedString.init(string: " "))
}
finalAttributedString.append(attributedStr)
}
// add paragraph attribute
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = .center
let attributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.paragraphStyle: paragraph]
finalAttributedString.addAttributes(attributes, range: NSRange(location: 0, length: finalAttributedString.length))
return finalAttributedString
}
现在您的 didTapAttributedTextInLabel(...)
函数应该检测到相对于 "tappable" 单词的正确点击位置。
我有一个 Label
,其中我有两个特定的 words
,应该是 clickable
。它是这样的:
Nutungsbedigungen
和 Datenschutzrichtlinien
应该都是 clickable
。我为实现这一目标所做的是:
设置标签:
func setUpDocumentsLabel(){
var textArray = [String]()
var fontArray = [UIFont]()
var colorArray = [UIColor]()
textArray.append("Mit 'fortfahren' akzeptierst du die")
textArray.append("Nutzungsbedingungen")
textArray.append("und")
textArray.append("Datenschutzrichtlinien.")
fontArray.append(Fonts.regularFontWithSize(size: 13.0))
fontArray.append(Fonts.boldFontWithSize(size: 13.0))
fontArray.append(Fonts.regularFontWithSize(size: 13.0))
fontArray.append(Fonts.boldFontWithSize(size: 13.0))
colorArray.append(Colors.white)
colorArray.append(Colors.white)
colorArray.append(Colors.white)
colorArray.append(Colors.white)
self.documentsLabel.attributedText = getAttributedString(arrayText: textArray, arrayColors: colorArray, arrayFonts: fontArray)
self.documentsLabel.isUserInteractionEnabled = true
let tapgesture = UITapGestureRecognizer(target: self, action: #selector(tappedOnLabel(_ :)))
tapgesture.numberOfTapsRequired = 1
self.documentsLabel.addGestureRecognizer(tapgesture)
}
点击操作:
@objc func tappedOnLabel(_ gesture: UITapGestureRecognizer) {
guard let text = self.documentsLabel.text else { return }
let nutzen = (text as NSString).range(of: "Nutzungsbedingungen")
let daten = (text as NSString).range(of: "Datenschutzrichtlinien")
if gesture.didTapAttributedTextInLabel(label: self.documentsLabel, inRange: nutzen) {
let alertcontroller = UIAlertController(title: "Tapped on", message: "user tapped on Nutzungsbedingungen", preferredStyle: .alert)
let alertAction = UIAlertAction(title: "OK", style: .default) { (alert) in
}
alertcontroller.addAction(alertAction)
self.present(alertcontroller, animated: true)
} else if gesture.didTapAttributedTextInLabel(label: self.documentsLabel, inRange: daten){
let alertcontroller = UIAlertController(title: "Tapped on", message: "user tapped on Datenschutzrichtlinien", preferredStyle: .alert)
let alertAction = UIAlertAction(title: "OK", style: .default) { (alert) in
}
alertcontroller.addAction(alertAction)
self.present(alertcontroller, animated: true)
}
}
getAttributedString:
func getAttributedString(arrayText:[String]?, arrayColors:[UIColor]?, arrayFonts:[UIFont]?) -> NSMutableAttributedString {
let finalAttributedString = NSMutableAttributedString()
for i in 0 ..< (arrayText?.count)! {
let attributes = [NSAttributedString.Key.foregroundColor: arrayColors?[i], NSAttributedString.Key.font: arrayFonts?[i]]
let attributedStr = (NSAttributedString.init(string: arrayText?[i] ?? "", attributes: attributes as [NSAttributedString.Key : Any]))
if i != 0 {
finalAttributedString.append(NSAttributedString.init(string: " "))
}
finalAttributedString.append(attributedStr)
}
return finalAttributedString
}
问题:
strings
不是每个字符都 clickable
! clickable
区的length
因设备而异:
对于Datenschutzrichtlinien
:
iPhone 11: clickable
来自 D - t
iPhone SE: clickable
来自 D - 2nd i
对于Nutzungsbedingungen
:
在每个设备上都很好,但 iPhone SE:仅来自 N - 2nd n
我不知道为什么会这样,如果有人能帮助我,我将不胜感激!
更新:
我正在为 UITapGestureRecognizer
使用这个 extension
:
extension UITapGestureRecognizer {
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
let textStorage = NSTextStorage(attributedString: label.attributedText!)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.location(in: label)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x,
y: locationOfTouchInLabel.y - textContainerOffset.y);
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return NSLocationInRange(indexOfCharacter, targetRange)
}
你在这里遇到的具体问题是因为你的标签有
.textAlignment = .center
但是 didTapAttributedTextInLabel()
代码不知道...它根据从 [ 开始的“und Datenschutzrichtlinien.” 计算位置=30=]标签左边缘.
您可以尝试此方法来修复它 - 快速测试(仅在一台设备上)- 似乎可以解决问题。
在您的 FirstLaunchViewController
class 中,不要将文档标签居中:
let documentsLabel: UILabel = {
let v = UILabel()
// don't do this
//v.textAlignment = .center
v.numberOfLines = 0
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
您似乎只使用了一次 func getAttributedString(...)
,那就是为 documentsLabel
设置属性文本的格式,因此请按如下方式更改它:
func getAttributedString(arrayText:[String]?, arrayColors:[UIColor]?, arrayFonts:[UIFont]?) -> NSMutableAttributedString {
let finalAttributedString = NSMutableAttributedString()
for i in 0 ..< (arrayText?.count)! {
let attributes = [NSAttributedString.Key.foregroundColor: arrayColors?[i], NSAttributedString.Key.font: arrayFonts?[i]]
let attributedStr = (NSAttributedString.init(string: arrayText?[i] ?? "", attributes: attributes as [NSAttributedString.Key : Any]))
if i != 0 {
finalAttributedString.append(NSAttributedString.init(string: " "))
}
finalAttributedString.append(attributedStr)
}
// add paragraph attribute
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = .center
let attributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.paragraphStyle: paragraph]
finalAttributedString.addAttributes(attributes, range: NSRange(location: 0, length: finalAttributedString.length))
return finalAttributedString
}
现在您的 didTapAttributedTextInLabel(...)
函数应该检测到相对于 "tappable" 单词的正确点击位置。