UILabel 字体大小与 UIView 帧大小一致

UILabel font size as per UIView frame size

提问人:Kamlesh Shingarakhiya 提问时间:2/2/2022 最后编辑:Kamlesh Shingarakhiya 更新时间:2/5/2022 访问量:715

问:

我正在创建具有动态大小的可编辑标签。我正在使用平移手势来缩放我的视图,我想根据视图增加字体大小。我使用了那段代码

func scaleGesture(recognizer: UIPanGestureRecognizer)
{
    DispatchQueue.main.async { [self] in
        
        let touchPoint = recognizer.location(in: view)
        
        if recognizer.state == .began
        {
            initialBounds = vv.bounds
            initialDistance = CGpointGetDistance(vv.center, point2: touchPoint)
            vv.translatesAutoresizingMaskIntoConstraints = true
        }
        else if recognizer.state == .changed
        {
            let horizontalChange = recognizer.translation(in: view).x
            let scale = sqrtf(Float(CGpointGetDistance(vv.center, point2: touchPoint)) / Float(initialDistance!))
            vv.frame = CGRect(x:  vv.frame.origin.x , y: vv.frame.origin.y, width: initialBounds!.width + horizontalChange, height: initialBounds!.height + horizontalChange)
            vv.contentLabel.fitFontForSize(vv.frame.size)
        }
        else
        {
            fontSize = vv.contentLabel.font.pointSize
        }

    }
}

根据代码,我得到了这个输出。请检查此剪辑在此剪辑中,您可以看到标签字体大小不平滑。请帮帮我

iOS Swift iPhone UI查看 UILABEL

评论

0赞 udi 2/2/2022
无法查看剪辑。
0赞 Kamlesh Shingarakhiya 2/2/2022
请查看此 dropbox.com/s/9ngijhv71wenq4q/RPReplay_Final1643881387.mov?dl=0
0赞 Blind Ninja 2/2/2022
这回答了你的问题吗?如何调整标签的字体大小以适合矩形?
0赞 Kamlesh Shingarakhiya 2/3/2022
@BlindNinja我已经检查过了,但它无法正常工作

答:

1赞 Shawn Frank 2/5/2022 #1

我不能肯定地说,因为很难完全理解一些变量像什么。vv

但是,我认为获得断断续续的结果的主要问题可能是:

  1. 计算当前帧字体的方式有问题
  2. 似乎您只在手势有时调整字体,而我认为即使它正在调整,它也很有用endedchanged

一种方法是按照以下步骤操作:

  1. 获取视图的新边界(主要是宽度和高度)
  2. 为标签指定最大字体大小
  3. 继续缩小字体,直到获得适合视图新大小的字体

这将很容易,如果文本不是太大,应该可以工作

在我自己做了一些研究之后,因为这是一个有趣的问题,我遇到了一些优雅的解决方案,可以使用二进制搜索找到最佳字体大小

因此,我是这样实现的:

1. 名为 FlexiFontLabel 的自定义 UILabel 子类

逻辑在评论中进行了解释

class FlexiFontLabel: UILabel
{
    // Boundary of minimum and maximum
    private let maxFontSize = CGFloat(500)
    private let minFontSize = CGFloat(5)
    
    // Margin of error is needed in binary search
    // so we can return when reach close enough
    private let marginOFError = CGFloat(0.5)
    
    // layoutSubviews() will get called while updating
    // font size so we want to lock adjustments if
    // processing is already in progress
    private var isUpdatingFontSize = false
    
    // Once this is set to true, the label should only
    // only support multiple lines rather than one
    var doesAdjustFontSizeToFitFrame = false
    {
        didSet
        {
            if doesAdjustFontSizeToFitFrame
            {
                numberOfLines = 0
            }
        }
    }
    
    // Adjusting the frame of the label automatically calls this
    override func layoutSubviews()
    {
        super.layoutSubviews()
        
        // Make sure the label is set to auto adjust the font
        // and it is not currently processing the font size
        if doesAdjustFontSizeToFitFrame
            && !isUpdatingFontSize
        {
            adjustFontSizeIfRequired()
        }
    }
    
    
    /// Adjusts the font size to fit the label's frame using binary search
    private func adjustFontSizeIfRequired()
    {
        guard let currentText = text,
              var currentFont = font else
        {
            print("failed")
            return
        }
        
        // Lock function from being called from layout subviews
        isUpdatingFontSize = true
        
        // Set max and min font sizes
        var currentMaxFontSize = maxFontSize
        var currentMinFontSize = minFontSize
        
        while true
        {
            // Binary search between min and max
            let midFontSize = (currentMaxFontSize + currentMinFontSize) / 2;
            
            // Exit if approached minFontSize enough
            if (midFontSize - currentMinFontSize <= marginOFError)
            {
                // Set min font size and exit because we reached
                // the biggest font size that fits
                currentFont = UIFont(name: currentFont.fontName,
                                     size: currentMinFontSize)!
                
                break;
            }
            else
            {
                // Set the current font size to the midpoint
                currentFont = UIFont(name: currentFont.fontName,
                                     size: midFontSize)!
            }
            
            // Configure an attributed string which can be used to find an
            // appropriate rectangle for a font size using its boundingRect
            // function
            let attribute = [NSAttributedString.Key.font: currentFont]
            let attributedString = NSAttributedString(string: currentText,
                                                      attributes: attribute)
            
            let options: NSStringDrawingOptions = [.usesLineFragmentOrigin,
                                                   .usesFontLeading]
            
            // Get a bounding box with the width of the current label and
            // an unlimited height
            let constrainedSize = CGSize(width: frame.width,
                                         height: CGFloat.greatestFiniteMagnitude)
            
            // Get the appropriate rectangle for the text using the current
            // midpoint font
            let newRect = attributedString.boundingRect(with: constrainedSize,
                                                        options: options,
                                                        context: nil)
            
            // Get the current area of the new rect and the current
            // label's bounds
            let newArea = newRect.width * newRect.height
            let currentArea = bounds.width * bounds.height
            
            // See if the new frame is lesser than the current label's area
            if newArea < currentArea
            {
                // The best font size is in the bigger half
                currentMinFontSize = midFontSize + 1
            }
            else
            {
                // The best font size is in the smaller half
                currentMaxFontSize = midFontSize - 1
            }
        }
        
        // set the font of the current label
        font = currentFont
        
        // Open label to be adjusted again
        isUpdatingFontSize = false
    }
}

2. 标签的使用方法

这就像通过一个小的添加来设置任何 UILabel

let label = FlexiFontLabel()
/// Do all your set up like frames, colors
// alignments etc

// This opens the label to auto adjust itself
label.doesAdjustFontSizeToFitFrame = true

3. 平移手势实现 这里没有发生任何有趣的事情,因为所有的调整都发生在 UILabel 子类中,但我只想向你展示我为更好地理解所做的工作

@objc
func scaleGesture(recognizer: UIPanGestureRecognizer)
{
    let location = recognizer.location(in: view)
    
    // This is my logic to prevent the bottom right anchor going
    // below a certain threshold, you can ignore it as it has
    // nothing to do with your solution
    if location.x >= minX && location.y >= minY
    {
        // Update the position of the anchor
        circle.center = location
        
        // Calculate the new frame for the label
        var newLabelFrame = label.frame
        let newWidth = location.x - originalLabelFrame.origin.x
        let newHeight = location.y - originalLabelFrame.origin.y
        newLabelFrame.size = CGSize(width: newWidth,
                                    height: newHeight)
        
        // After updating the label, layoutSubviews() will be called
        // automatically which will update the font size
        label.frame = newLabelFrame
    }
}

这给了我以下结果,当根据标签框更改字体大小时,该结果非常流畅

adjust font size of label to fit the rectangle CGRect frame bounds UILabel auto adjust resize font size calculate max font that fit rectangle calculate font to fit frame

评论

0赞 Asim Iftikhar Abbasi 9/2/2022
如果我们使用 UISlider 增加字体大小怎么办,那么如何相应地增加视图呢?
0赞 Shawn Frank 9/2/2022
@AsimIftikharAbbasi - 您可以应用类似的方法,尽管您将在帧大小而不是字体大小上应用二进制搜索逻辑。
0赞 Asim Iftikhar Abbasi 9/2/2022
感谢您的回复,但它不起作用。你能给我举个例子吗?
0赞 Shawn Frank 9/2/2022
@AsimIftikharAbbasi - 我建议创建另一个问题并添加您尝试过的代码,但该代码不起作用。SO 社区的人或我自己可能会帮助你。
0赞 Asim Iftikhar Abbasi 9/2/2022
stackoverflow.com/questions/73578462/......,这是我在堆栈溢出时提出的问题。