I have many different strings that are inside UILabel and look like that one for example, but much longer:
[[France]] is a member state of the [[European Union]] as well as the [[Schengen Agreement]]. There are many friendly car and truck drivers. Drivers have to pay toll on motorways (except in [[Bretagne|Brittany]])
What I need to do is to make each of the substrings inside the double square brackets clickable with links to different pages. So when the user clicks on the part of a string with European Union it moves him to a page called European Union etc. What I was able to do so far is to make those substrings of UILabel clickable, but only when there is a single link to be clicked. However, when I am trying to add tap gesture for each of these substrings, it makes only the last substring clickable. Is there a way to add a different gesture to each different part of the string in UILabel? Is there maybe a different better way of approaching this? Or maybe it should work correctly and there is something wrong with the way I am adding the tap gestures to the UILabel?
This is how my extension looks like at the moment:
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
    }
}}
And this is how I implement it:
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)
    }
}
And that's how I am addingTapGesture to each substring in ViewController. descriptionClickableValuesArray it's an array with the extracted square bracket values. checkIfClickable checks if a page with a related title is found and self.pageDescription is a label that is passed 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!])
        }
    })
 
    