当前位置: 首页 > 知识库问答 >
问题:

在启用自动收缩到最小字体大小的情况下检测UILabel的点击

马弘益
2023-03-14

我正在使用我修改的以下 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)
    }

}

我像这样设置标签来检测点击,就像其他答案所说的那样:

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并相应地设置段落样式和行距(我有预感这可能是问题所在,但不确定如何测试)等等...

最后,我有我的点击标签功能

 @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

它计算文本边界框或文本容器偏移量或触摸文本容器位置的部分很可能是错误点,但我不确定是哪个,因为这很难理解。

我可以说它无法正常工作,因为在我的字符串中它没有接近末尾,并且无论我在哪里单击它,它总是说 indexofcharacter=36,这意味着它没有正确计算我上面提到的变量之一。

如果他们知道为什么这不起作用,任何帮助都是值得的。

为了让你知道文本现在是什么样子,我将在下面展示给你看。我的最终目标是让我可以点击公式中的部分来显示值,所以你可以说是可点击的链接。

更新E::

我忘记提到,当我更新UITapGesture识别器并添加以下行时:

let mutableAttribString = NSMutableAttributedString(attributedString: label.attributedText!)
        mutableAttribString.addAttributes([NSAttributedString.Key.font: label.font!], range: NSRange(location: 0, length: label.attributedText!.length))

并将其设置为文本存储,< code > let text storage = NSTextStorage(attributed string:mutable attribute string)我确实在数字上看到了一些改进,但还差得很远。

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

你会注意到textboundingbox至少更像labelsize。我在某处读到,如果你使用不同的字体,可能会出现问题,所以这就是为什么我试图添加它,以确保它能正常工作。

同样有趣的是,将其添加到UITapGesture识别器似乎也有所帮助:

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,因此它会相应地自动收缩,然后您会看到错误,如果默认大小较小,那么它似乎工作正常,这告诉我错误与最初的大小非常大有关?

共有1个答案

董子航
2023-03-14

所以我终于想出了一个可以接受的答案。

关键代码如下:

let formulaLabelWidth = formulaLabel.bounds.size.width

var font_size:CGFloat = 36.0 //Change this to a higher number if you need to.
var stringSize = NSString(string: formula).size(withAttributes: [.font : self.formulaLabel.font.withSize(font_size)])
while(stringSize.width>formulaLabelWidth){
    font_size = font_size - 1
    stringSize = NSString(string: formula).size(withAttributes: [.font : self.formulaLabel.font.withSize(font_size)])
}


 formulaLabel.font = formulaLabel.font.withSize(font_size)

这段代码所做的是绘制字符串,就像它有一定的字体大小一样,给你宽度和高度的界限。在我的例子中,我只关心宽度,这要归功于如何在接口生成器中设置UILabel的技巧。

为了使这种策略发挥作用,必须在你的UILabel上设置它,因为它做了所有的艰苦工作来找到适合文本的完美大小。

这确实适用于多行,而且我在我的许多公式中都使用了它。这些行用字符分隔,并自动计算,因为它只是增加了使用<code>绘制的高度。size(withAttributes)函数。

他们曾经有一个sizeForFont,但它被弃用了,所以我研究了这个函数作为一个可能的解决方案,事实上它确实可以用一些聪明的想法来工作。

至于检测水龙头,请使用我创建的以下修改后的UITapGesture识别器。

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 paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = 6
        paragraphStyle.lineBreakMode = .byTruncatingTail
        paragraphStyle.alignment = .center
        mutableAttribString.addAttributes([.paragraphStyle: paragraphStyle], range: NSMakeRange(0, mutableAttribString.string.count))

        let textStorage = NSTextStorage(attributedString: mutableAttribString)

        // Configure textContainer
        textContainer.lineFragmentPadding = 0.0
        textContainer.lineBreakMode = label.lineBreakMode
        textContainer.maximumNumberOfLines = label.numberOfLines

        // Configure layoutManager and textStorage
        layoutManager.addTextContainer(textContainer)

        textStorage.addLayoutManager(layoutManager)

        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 = 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)

        //let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
                                                        // locationOfTouchInLabel.y - textContainerOffset.y);
        let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)

        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        print("IndexOfCharacter=",indexOfCharacter)

        print("TargetRange=",targetRange)
        return NSLocationInRange(indexOfCharacter, targetRange)
    }

}

您可以随意删除注释,但是我把它们留在那里,这样您就可以看到它选择了正确的字符串索引。

另请注意,我在那里有一个段落设置,行距为 6 和对齐中心,因此请随意将它们更改为您的用例,但不要更改换行模式!这对于系统在绘制时自动找到最佳字体大小至关重要。

同样在此代码中引用了当前的标签字体,这是我添加的新部分,因为如果没有它,文本存储计算将远远偏离堆栈溢出的其他答案。

与点击手势功能放在一起

@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")
        }
    }

当然,设置点击手势UILabel在视图中或适当的地方…

formulaLabel.addGestureRecognizer(UITapGestureRecognizer(target:self, action: #selector(tapLabel(gesture:))))

你有自己的工作

  • 多线友好
  • 自动收缩友好
  • 可点击友好

UILabel!

 类似资料:
  • 问题内容: 我需要在JScrollPane内用可调整大小的列创建一个JTable(当用户增加列宽- 出现水平滚动条时)。为此我有用。同样,当视口足够宽以容纳整个表格时,列应拉伸以填充视口宽度。为此,我具有JTable类的重写方法,如下所示: 这种方法行之有效,除了一件事:当我第一次尝试调整列的大小时,它将自己的宽度返回到开始位置。如果我快速调整列的大小并释放鼠标表,则可以继续正常工作。那么,这种行

  • 我需要检测字体缩放后文本组件的字体大小。假设我有一个字体大小为18px的文本组件 用户通过操作系统辅助功能设置设置了大字体。现在我的文本已经用更大的字体显示了(超过18px)。我知道我可以使用< code > allowFontScaling = { false } ,但我不想失去文本的可访问性。我看到React native有一个API来获取字体比例< code > pixel ratio .

  • 给定一定数量的节点,是否有公式计算AVL树的最大和最小高度? 例如: 教科书中的问题: 一棵包含3个节点、5个节点和7个节点的AVL树的最大/最小高度是多少<教科书式答案: 3个节点的AVL树的最大/最小高度为2/2,5个节点为3/3,7个节点为4/3 我不知道他们是否通过一些神奇的公式计算出来,或者他们是否画出了每个给定高度的AVL树并以这种方式确定。

  • 我正在计划一个相机的属性,我需要一个计算机视觉系统。我必须检测图像中的一些字母数字区域,然后使用Tesseract和OpenCV用OCR转换它们。一个典型的例子是高速公路上的车牌识别(但在我的项目中,速度不是问题)。 为了得到可靠的OCR转换,我需要知道哪一个可以是文本的最小像素高度,以估计相机分辨率、镜头的距离和焦距。 出于这个原因,我想知道OpenCV和Tesseract库是否为文本的最小可靠

  • 问题内容: 我一直试图(徒劳地)建立一个页面,当我更改窗口大小时,其元素将重新调整大小。我可以在CSS中使用它来处理图像,但是没有问题,但是我似乎无法在文本上完成相同的操作,而且我不确定在CSS中是否有可能。而且我似乎找不到能完成此任务的jQuery脚本。 当用户调整窗口大小时,我实质上希望页面能够动态流畅地缩放,而无需用户调用页面缩放。通过css在我的img div上这可以正常工作,但对于文本则