我有两个 UILabels 彼此相邻,并进行左右调整,如下所示。
|-Some text left adjusted----------some other text right adjusted-|
两个标签都具有 adjustmentFontSizeToFitWidth = YES 并通过以下约束相互链接
[NSLayoutConstraint constraintWithItem:_rightLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:_leftLabel
attribute:NSLayoutAttributeRight
multiplier:1
constant:10]
这样它们会占用尽可能多的空间,如果没有足够的空间容纳原始字体大小,由于调整FontSizeToFitWidth,它会被降低,这样就不会截断任何文本。
我的问题是,当一个标签由于文本太长而需要降低其字体大小时,我希望另一个标签也降低其字体大小,以便两者大小相同,而不是一个标签可能是另一个标签大小的两倍。我也想限制字体大小以匹配,但可惜我不知道如何做到这一点,有什么想法吗?
通常,标签文本使用您在字体属性中指定的字体绘制。但是,如果此属性设置为 YES,并且文本属性中的文本超出了标签的边界矩形,则接收器开始减小字体大小,直到字符串适合或达到最小字体大小。
我由此推断,更新的字体是在绘制时计算的,并且
font
属性只能读取,不能写入。 因此,我相信 Andrew 在 font
属性上使用 KVO 的建议不会起作用。
因此,为了达到您想要的结果,您需要计算调整后的字体大小。
正如 Jackson 在评论中指出的那样,这种获取实际字体的非常方便的 NSString 方法在 iOS 7 中已被弃用。从技术上讲,您仍然可以使用它,直到它被删除。
另一种选择是循环遍历字体比例,直到找到适合两个标签的字体比例。 我能够让它正常工作; 这是一个示例项目,展示了我是如何做到的。
另外,这是链接停止工作时的代码:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_rightLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:_leftLabel
attribute:NSLayoutAttributeRight
multiplier:1
constant:10];
[self.view addConstraint:constraint];
}
- (IBAction)makeRightLabelLongerPressed:(id)sender {
self.rightLabel.text = @"some much longer right label text";
}
- (IBAction)adjustLabelSizes:(id)sender {
NSLog(@"Attempting to adjust label sizes…");
CGFloat minimumScaleFactor = fmaxf(self.rightLabel.minimumScaleFactor, self.leftLabel.minimumScaleFactor);;
UIFont * startingFont = self.rightLabel.font;
for (double currentScaleFactor = 1.0; currentScaleFactor > minimumScaleFactor; currentScaleFactor -= 0.05) {
UIFont *font = [startingFont fontWithSize:startingFont.pointSize * currentScaleFactor];
NSLog(@" Attempting font with scale %f (size = %f)…", currentScaleFactor, font.pointSize);
BOOL leftLabelWorks = [self wouldThisFont:font workForThisLabel:self.leftLabel];
BOOL rightLabelWorks = [self wouldThisFont:font workForThisLabel:self.rightLabel];
if (leftLabelWorks && rightLabelWorks) {
NSLog(@" It fits!");
self.leftLabel.font = font;
self.rightLabel.font = font;
return;
} else {
NSLog(@" It didn't fit. :-(");
}
}
NSLog(@" It won't fit without violating the minimum scale (%f), so set both to minimum. Some text may get truncated.", minimumScaleFactor);
UIFont *minimumFont = [self.rightLabel.font fontWithSize:self.rightLabel.font.pointSize * self.rightLabel.minimumScaleFactor];
self.rightLabel.font = minimumFont;
self.leftLabel.font = minimumFont;
}
- (BOOL) wouldThisFont:(UIFont *)testFont workForThisLabel:(UILabel *)testLabel {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:testFont, NSFontAttributeName, nil];
NSAttributedString *as = [[NSAttributedString alloc] initWithString:testLabel.text attributes:attributes];
CGRect bounds = [as boundingRectWithSize:CGSizeMake(CGRectGetWidth(testLabel.frame), CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) context:nil];
BOOL itWorks = [self doesThisSize:bounds.size fitInThisSize:testLabel.bounds.size];
return itWorks;
}
- (BOOL)doesThisSize:(CGSize)aa fitInThisSize:(CGSize)bb {
if ( aa.width > bb.width ) return NO;
if ( aa.height > bb.height ) return NO;
return YES;
}
这种方法可以简单地重构为类别方法,以取代 Jackson 链接到的已弃用方法。
您可以这样解决这个问题:
斯威夫特5
extension UILabel {
var actualFontSize: CGFloat {
guard let attributedText = attributedText else { return font.pointSize }
let text = NSMutableAttributedString(attributedString: attributedText)
text.setAttributes([.font: font as Any], range: NSRange(location: 0, length: text.length))
let context = NSStringDrawingContext()
context.minimumScaleFactor = minimumScaleFactor
text.boundingRect(with: frame.size, options: .usesLineFragmentOrigin, context: context)
let adjustedFontSize: CGFloat = font.pointSize * context.actualScaleFactor
return adjustedFontSize
}
}
用途:
firstLabel.text = firstText
secondLabel.text = secondText
view.setNeedsLayout()
view.layoutIfNeeded()
let smallestSize = min(firstLabel.actualFontSize, secondLabel.actualFontSize)
firstLabel.font = firstLabel.font.withSize(smallestSize)
secondLabel.font = secondLabel.font.withSize(smallestSize)
嗯,这不是一个限制。但我使用了layoutSubviews 覆盖将两个标签字体设置为相同大小
override func layoutSubviews() {
super.layoutSubviews()
label1.font = label2.font
}
您可以尝试使用 键值观察 来观察一个标签上字体属性的变化,当变化时,将另一个标签设置为使用相同的字体。
在您的 -viewDidLoad 方法中:
// Add self as an observer of _rightLabel's font property
[_rightLabel addObserver:self forKeyPath:@"font" options:NSKeyValueObservingOptionNew context:NULL];
在同一个控制器实现中(上述代码片段上下文中的 self):
// Observe changes to the font property of _rightLabel
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == _rightLabel && [keyPath isEqualToString:@"font"]) {
// Set _leftLabel's font property to be the new value set on _rightLabel
_leftLabel.font = change[NSKeyValueChangeNewKey];
}
}
我找到了更好的解决方案。只需将文本空间从头到尾添加到 uilabel 中,文本长度较短。字体也一样。