我有很多不同的字符串在UILabel里面,看起来像这个例子,但更长:
[[法国]]是[[欧盟]]的成员国,也是欧盟的成员国[[申根协议]]。有许多友好的汽车和卡车司机。司机必须付费高速公路收费(除[[布列塔尼|布列塔尼]])
我需要做的是使双方括号内的每个子字符串都可以点击链接到不同的页面。因此,当用户点击带有European Union的字符串部分时,它会将他移动到一个名为European Union等的页面。到目前为止,我所能做的是使UILabel的那些子字符串可点击,但只有当有一个链接要点击时。但是,当我试图为每个子字符串添加点击手势时,它只使最后一个子字符串可单击。是否有一种方法来添加不同的手势在UILabel字符串的每个不同的部分?有没有更好的方法来解决这个问题?或者它应该正确工作,并且我将点击手势添加到UILabel的方式有问题?
这是我的扩展看起来像现在:
extension UITapGestureRecognizer {
func didTapAttributedString(_ string: String, in label: UILabel) -> Bool {
guard let text = label.text else {
return false
}
let range = (text as NSString).range(of: string)
return self.didTapAttributedText(label: label, inRange: range)
}
private func didTapAttributedText(label: UILabel, inRange targetRange: NSRange) -> Bool {
guard let attributedText = label.attributedText else {
assertionFailure("attributedText must be set")
return false
}
let textContainer = createTextContainer(for: label)
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(textContainer)
let textStorage = NSTextStorage(attributedString: attributedText)
if let font = label.font {
textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: NSMakeRange(0, attributedText.length))
}
textStorage.addLayoutManager(layoutManager)
let locationOfTouchInLabel = location(in: label)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let alignmentOffset = aligmentOffset(for: label)
let xOffset = ((label.bounds.size.width - textBoundingBox.size.width) * alignmentOffset) - textBoundingBox.origin.x
let yOffset = ((label.bounds.size.height - textBoundingBox.size.height) * alignmentOffset) - textBoundingBox.origin.y
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - xOffset, y: locationOfTouchInLabel.y - yOffset)
let characterTapped = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let lineTapped = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
let rightMostPointInLineTapped = CGPoint(x: label.bounds.size.width, y: label.font.lineHeight * CGFloat(lineTapped))
let charsInLineTapped = layoutManager.characterIndex(for: rightMostPointInLineTapped, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return characterTapped < charsInLineTapped ? targetRange.contains(characterTapped) : false
}
private func createTextContainer(for label: UILabel) -> NSTextContainer {
let textContainer = NSTextContainer(size: label.bounds.size)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
return textContainer
}
private func aligmentOffset(for label: UILabel) -> CGFloat {
switch label.textAlignment {
case .left, .natural, .justified:
return 0.0
case .center:
return 0.5
case .right:
return 1.0
@unknown default:
return 0.0
}
}}
我是这样实现的:
public func addTapGestureToPartOfString(label: UILabel, stringToBeCalled: String) {
self.setUpDataForClickableLabel(string: stringToBeCalled, label: label)
let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(tappedOnLabel(_:)))
label.lineBreakMode = .byWordWrapping
label.isUserInteractionEnabled = true
tapGesture.numberOfTouchesRequired = 1
label.addGestureRecognizer(tapGesture)
}
@objc func tappedOnLabel(_ gesture: UITapGestureRecognizer) {
guard let label = self.labelToBeCalled,
let stringToBeCalled = self.stringToBeCalled,
let page = self.pageToBeCalled,
let allPages = self.pages else {
return
}
if gesture.didTapAttributedString(stringToBeCalled, in: label) {
let pageDetailViewController = PageDetailViewController(viewModel: PageDetailViewModel())
pageDetailViewController.populatePage(page: page, allPages: allPages)
pageDetailViewController.modalPresentationStyle = .fullScreen
UIApplication.topViewController()?.navigationController?.pushViewController(pageDetailViewController, animated: true)
}
}
这就是我在ViewController中给每个子字符串添加tapgesture的方式。descriptionClickableValuesArray它是一个包含提取的方括号值的数组。checkIfClickable检查是否找到了具有相关标题的页面,并且self.pageDescription是传递给UILabel的标签。
self.viewModel.descriptionClickableValuesArray?.forEach({ clickableValue in
if self.viewModel.checkIfClickable(string: clickableValue) {
self.viewModel.addTapGestureToPartOfString(label: self.pageDescription, stringToBeCalled: clickableValue)
self.viewModel.labelToBeCalled?.set(color: .systemBlue, on: [self.viewModel.stringToBeCalled!])
}
})
关键是在您需要的范围内添加链接属性到有属性字符串,用UITextView呈现该字符串,然后通过UITextViewDelegate处理这些链接
NSAttributedString在UILabel中使用Swift点击事件