【问题标题】:UILabel font size as per UIView frame size根据 UIView 框架大小的 UILabel 字体大小
【发布时间】:2022-02-05 16:52:08
【问题描述】:

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

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
        }

    }
}

根据代码,我得到了这个输出。 Please check this clip 在此剪辑中,您可以看到标签字体大小不流畅。请帮帮我

【问题讨论】:

标签: ios swift iphone uiview uilabel


【解决方案1】:

我不能肯定地说,因为很难完全理解一些变量,比如vv 等。

但是,我认为获得不稳定结果的主要问题可能是:

  1. 计算当前帧字体的方式有问题
  2. 看来你只是在手势有ended时才调整字体,而我认为即使它是changed也会很有用

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

  1. 获取视图的新边界(主要是宽度和高度)
  2. 为您的标签设置最大字体大小
  3. 不断缩小字体,直到找到适合新视图大小的字体

这很简单,如果文本不太大,应该可以工作

我自己做了一些研究,因为这是一个有趣的问题,我遇到了一些优雅的解决方案来找到optimal font size using binary search

这就是我的实现方式:

1.自定义 UILabel 子类称为 FlexiFontLabel

逻辑在cmets中有解释

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
    }
}

这给了我以下结果,根据标签框更改字体大小时非常平滑

【讨论】:

    猜你喜欢
    • 2012-01-02
    • 1970-01-01
    • 2019-09-18
    • 2018-09-30
    • 2012-05-15
    • 2012-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多