我不能肯定地说,因为很难完全理解一些变量,比如vv 等。
但是,我认为获得不稳定结果的主要问题可能是:
- 计算当前帧字体的方式有问题
- 看来你只是在手势有
ended时才调整字体,而我认为即使它是changed也会很有用
一种方法是按照以下步骤操作:
- 获取视图的新边界(主要是宽度和高度)
- 为您的标签设置最大字体大小
- 不断缩小字体,直到找到适合新视图大小的字体
这很简单,如果文本不太大,应该可以工作
我自己做了一些研究,因为这是一个有趣的问题,我遇到了一些优雅的解决方案来找到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
}
}
这给了我以下结果,根据标签框更改字体大小时非常平滑