Я использую следующий UITapGestureRecognizer, который я изменил, чтобы другие могли копировать и вставлять, чтобы увидеть тот же результат, что и я.
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 mutableAttribString = NSMutableAttributedString(attributedString: label.attributedText!)
//mutableAttribString.addAttributes([NSAttributedString.Key.font: label.font!], range: NSRange(location: 0, length: label.attributedText!.length))
let textStorage = NSTextStorage(attributedString: label.attributedText!)
//let textStorage = NSTextStorage(attributedString: mutableAttribString)
// 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
print("LabelSize=",labelSize)
textContainer.size = labelSize
print("TextContainerSize=",textContainer.size)
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.location(in: label)
print("LocationOfTouchInLabel=",locationOfTouchInLabel)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
print("TextBoundingBox=",textBoundingBox)
//let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
//(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
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)
print("textContainerOffset",textContainerOffset)
//let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
// locationOfTouchInLabel.y - textContainerOffset.y);
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)
print("LocationOfTouchInTextContainer",locationOfTouchInTextContainer)
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
print("IndexOfCharacter=",indexOfCharacter)
print("TargetRange=",targetRange)
return NSLocationInRange(indexOfCharacter, targetRange)
}
}
Я установил метку таким образом, чтобы обнаруживать клики, как в других ответах do:
formulaLabel.addGestureRecognizer(UITapGestureRecognizer(target:self, action: #selector(tapLabel(gesture:))))
Для него установлено значение атрибута.
В случае необходимости у меня есть следующий код, который выполняет анимацию с текстом.
func doFormulaAnimation(){
//animTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true, block: {_ in
//Draw correct formula based on mode.
var formula = ""
var highlight_ranges:[NSRange] = []
if(stateController?.tdfvariables.ebrtMode == true){
formulaLabel.numberOfLines = 1
formula = "BED = N * d * [ RBE + ( d / (α/β) ) ]"
self.formulaLabel.attributedText = NSMutableAttributedString(string: formula, attributes: [ NSAttributedString.Key.foregroundColor: UIColor.label ])
if(self.dosePerFractionSelected==true){
highlight_ranges.append(NSRange(location: 10, length: 1))
highlight_ranges.append(NSRange(location: 24, length: 1))
}
if(self.alphaBetaSelected==true){
highlight_ranges.append(NSRange(location: 29, length: 3))
}
if(self.totalDoseSelected==true){
highlight_ranges.append(NSRange(location: 6, length: 5))
}
if(self.numFractionsSelected==true){
highlight_ranges.append(NSRange(location: 6, length: 1))
}
}
if(stateController?.tdfvariables.ldrMode == true){
formulaLabel.numberOfLines = 3
formula = "BED = R * T * [RBE + (( g * R * T) / (α/β) ) ]\ng = ( 2 / μT ) * ( 1 - ( ( 1 - exp(-μT) / μT ) )\nμ = 0.693 / Thalf repair"
}
if(stateController?.tdfvariables.permMode == true){
formulaLabel.numberOfLines = 2
formula = "BED = ( R0 / L ) * [ RBE + ( R0 / ( ( u + L) * (α/β) ) ) ]\n"
}
UIView.transition(with: self.formulaLabel, duration: 0.5, options: .transitionCrossDissolve, animations: {
let colorAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.label ]
let attributedTextFormula = NSMutableAttributedString(string: formula, attributes: colorAttribute)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 6
paragraphStyle.lineBreakMode = .byTruncatingTail
//paragraphStyle.alignment = .center
attributedTextFormula.addAttribute(
.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: attributedTextFormula.length
))
self.formulaLabel.attributedText = attributedTextFormula
//
}, completion: { finished in
print("finished first transition")
UIView.transition(with: self.formulaLabel, duration: 0.5, options: .transitionCrossDissolve, animations: {
let colorAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.label ]
let attributedTextFormula = NSMutableAttributedString(string: formula, attributes: colorAttribute)
for range in highlight_ranges {
attributedTextFormula.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red, range: range)
}
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 6
paragraphStyle.lineBreakMode = .byTruncatingTail
//paragraphStyle.alignment = .center
attributedTextFormula.addAttribute(
.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: attributedTextFormula.length
))
self.formulaLabel.attributedText = attributedTextFormula
//self.formulaLabel.addInterlineSpacing(spacingValue: 1)
}, completion: { finished in
print("finished second transition")
})
})
}
приведенный выше код демонстрирует, как я соответствующим образом создаю NSMutableString и соответствующим образом устанавливаю стиль абзаца и межстрочный интервал (у меня есть подозрение, что это может быть проблемой, но я не знаю, как это проверить) et c ...
Наконец, я есть моя функция метки касания
@IBAction func tapLabel(gesture: UITapGestureRecognizer) {
guard let text = formulaLabel.attributedText?.string else {
return
}
let AB_Range = text.range(of: "(α/β)")
//let AB_Range = NSRange(location: 29, length: 3)
if gesture.didTapAttributedTextInLabel(label: formulaLabel, inRange: NSRange(AB_Range!, in: text)) {
print("Tapped a/b")
} else {
print("Tapped none")
}
}
В настоящее время она не касается правильного места, но в очень большой степени отключена . После отладки становится очевидным, почему он не работает, но я не уверен, как исправить код или где он пошел не так.
Когда я нажимаю на A / B, как будто должен, я получаю это как информация об отладке.
LabelSize= (315.0, 43.0)
TextContainerSize= (315.0, 43.0)
LocationOfTouchInLabel= (266.0, 28.0)
TextBoundingBox= (0.0, 0.0, 185.373046875, 13.8)
textContainerOffset (64.8134765625, 14.6)
LocationOfTouchInTextContainer (201.1865234375, 13.4)
IndexOfCharacter= 36
TargetRange= {28, 5}
Tapped none
Часть, в которой он вычисляет поле ограничения текста или смещение текстового контейнера или locationoftouchintextcontainer, составляет , скорее всего, это точки ошибки , но я не уверен, что именно, так как это сложно чтобы понять.
Я могу сказать, что он работает неправильно, потому что в моей строке это не где-то рядом с концом, и он всегда говорит indexofcharacter = 36 независимо от того, где я щелкаю мимо него, что означает, что он не вычисляет что-то правильно с одним переменных, о которых я упоминал выше.
Любая помощь приветствуется, если они знают, почему это не работает.
Чтобы дать представление о том, как сейчас выглядит текст, я покажу вам ниже. Моя конечная цель - сделать так, чтобы я мог щелкать по частям в формуле, чтобы отображались значения, чтобы можно было сказать интерактивные ссылки.
ОБНОВЛЕНИЕ ::
Я забыл упомянуть, что когда я обновил UITapGestureRecognizer и добавил эти строки:
let mutableAttribString = NSMutableAttributedString(attributedString: label.attributedText!)
mutableAttribString.addAttributes([NSAttributedString.Key.font: label.font!], range: NSRange(location: 0, length: label.attributedText!.length))
и установил его в текстовое хранилище, let textStorage = NSTextStorage(attributedString: mutableAttribString)
я, кажется, вижу некоторые улучшения в числах, но это все еще далеко.
LabelSize= (315.0, 43.0)
TextContainerSize= (315.0, 43.0)
LocationOfTouchInLabel= (269.0, 25.5)
TextBoundingBox= (7.505859375, 0.0, 299.98828125, 42.9609375)
textContainerOffset (0.0, 0.01953125)
LocationOfTouchInTextContainer (269.0, 25.48046875)
IndexOfCharacter= 17
TargetRange= {28, 5}
Tapped none
Вы заметите, что текстовое поле больше похоже на размер ярлыка. Я где-то читал, что проблема может быть в том, что вы используете разные шрифты, поэтому я попытался добавить это, чтобы убедиться, что он работает правильно.
Также интересно, что добавление этого в UITapGestureRecognizer также помогает:
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 6
paragraphStyle.lineBreakMode = .byTruncatingTail
//paragraphStyle.alignment = .center
mutableAttribString.addAttribute(
.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: mutableAttribString.length
))
Я заметил, что текстовое поле правильно показывает 0,0, когда я делаю это вместо 7,50
LabelSize= (315.0, 43.0)
TextContainerSize= (315.0, 43.0)
LocationOfTouchInLabel= (262.5, 31.0)
TextBoundingBox= (0.0, 0.0, 299.98828125, 42.9609375)
textContainerOffset (7.505859375, 0.01953125)
LocationOfTouchInTextContainer (254.994140625, 30.98046875)
IndexOfCharacter= 17
TargetRange= {28, 5}
Tapped none
** ОСНОВНОЕ ОБНОВЛЕНИЕ *****
Ну отличные новости! Оказывается, исправление шрифта было частью исправления, но поскольку у меня было включено автоматическое сжатие с минимальным размером шрифта и максимальным размером шрифта, оно никогда не вычислялось правильно! Когда я установил фиксированный размер шрифта, все работает!
Теперь возникает вопрос: как заставить его работать с включенным автоматическим сжатием? Обратите внимание, что вам нужно установить размер на что-то очень большое, чтобы доказать, что это работает, например, я установил свой на 55, поэтому он автоматически сжимается, а затем вы видите ошибку, если размер по умолчанию меньше, то, похоже, он работает нормально, что говорит мне, что ошибка как-то связана с размером может быть изначально очень большим?