提问人:Kamlesh Shingarakhiya 提问时间:2/2/2022 最后编辑:Kamlesh Shingarakhiya 更新时间:2/5/2022 访问量:715
UILabel 字体大小与 UIView 帧大小一致
UILabel font size as per UIView frame size
问:
我正在创建具有动态大小的可编辑标签。我正在使用平移手势来缩放我的视图,我想根据视图增加字体大小。我使用了那段代码
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
}
}
}
根据代码,我得到了这个输出。请检查此剪辑在此剪辑中,您可以看到标签字体大小不平滑。请帮帮我
答:
1赞
Shawn Frank
2/5/2022
#1
我不能肯定地说,因为很难完全理解一些变量像什么。vv
但是,我认为获得断断续续的结果的主要问题可能是:
- 计算当前帧字体的方式有问题
- 似乎您只在手势有时调整字体,而我认为即使它正在调整,它也很有用
ended
changed
一种方法是按照以下步骤操作:
- 获取视图的新边界(主要是宽度和高度)
- 为标签指定最大字体大小
- 继续缩小字体,直到获得适合视图新大小的字体
这将很容易,如果文本不是太大,应该可以工作
在我自己做了一些研究之后,因为这是一个有趣的问题,我遇到了一些优雅的解决方案,可以使用二进制搜索找到最佳字体大小
因此,我是这样实现的:
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
}
}
这给了我以下结果,当根据标签框更改字体大小时,该结果非常流畅
评论
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/......,这是我在堆栈溢出时提出的问题。
评论