UILabel 斜体字体裁剪
UILabel italic font clipping
我正在尝试解决这个问题,因为在我正在处理的应用程序中我有很多字体,因此当用户更改字体时标签的大小是动态计算的。
我遇到的问题是,如果字体是斜体,那么 UILabel 会在最后被剪掉,如下图所示:
这是我目前尝试过的方法:
- 借助 CoreText 和
CGSize CTFramesetterSuggestFrameSizeWithConstraints ( CTFramesetterRef framesetter, CFRange stringRange, CFDictionaryRef frameAttributes, CGSize constraints, CFRange *fitRange );
计算宽度
- 借助 NSAttributedString 和
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context
计算宽度
- 借助 NSString 和
- (CGSize)sizeWithAttributes:(NSDictionary<NSString *,id> *)attrs
计算宽度
- 使用临时 UITextView 和
sizeThatFits´ and
fitToSize`
由于应用程序中有很多字体,我需要动态设置标签的宽度,因此 UILabel 的子类化和在 drawFrameInRect
上添加更多点是行不通的。
这是 Github 上的示例代码。
任何 help/advice 不胜感激。
我会在您的按钮和标签上使用自动布局,以始终根据您选择标签的字体大小使其居中。
然后,删除 updateLabel
方法中的所有代码。我不确定您为什么使用 UITextview
并将爵士乐带入画面。如果您在情节提要中单击您的标签并查看属性检查器并找到 "Autoshrink" 它应该设置为 "Minimum Font Size" 而不是 "Fixed Font Size."
然后我会在您的标签上使用 NSAttributedString
来根据单击的按钮更改其大小和字体。
我下载了您的 GitHub 项目,看来 UILabel 和 UITextField 计算文本边界的方式肯定不同。因此,我首先尝试将 UITextField 上的 sizeToFit 的结果大小分配给 UILabel 的大小。但是,这种方法不适用于 Strato 字体。
在尝试了其他一些失败的方法之后,我记得斜体字体的角度大致相同。字体的斜体版本通常会按与原始宽度相同的比例增加边界框的宽度。
更新:
因此,您应该能够从斜体字符串的宽度和字体大小中推导出其宽度。我在你的项目中尝试过这种方法,它适用于你列出的所有字体。我使用了以下公式:
CGRect calculatedRect = [attributedString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) context:nil];
calculatedRect.size.width = calculatedRect.size.width + (label.font.pointSize * 0.2);
由于这取决于斜体增加的宽度,斜体加粗字体,如Strata,对宽度的影响较大。
显然,如果文本 API 得到扩充以包括可靠的文本宽度计算,您将能够采用它。但是,如果您想要向后兼容,我提出的解决方法可能仍然是更好的选择。
希望对您有所帮助!
即使我没有时间测试它,我也很确定问题出在标签尺寸是根据预付款计算的。前进是从一个角色的基点到下一个角色的移动量。通常对于斜体字体,advance 可以小于边界。因此添加预付款将切断布局的末尾。
即巴斯克维尔斜体 H
:
(lldb) p bounds[0].size
(CGSize) = (width=33.53515625, height=26.484375)
(lldb) p advances[0]
(CGSize) = (width=30, height=0)
所以我认为如果文本以理想的方式布局(无压缩等),则必须添加最后一个字符的前进框和边界框之间的区别。
要获得这两个值,您必须调用一些 CT 函数:
// What you probably have
CTFontRef font = …;
CFStringRef string = …;
CFAAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
// Get the character and glyph
CFIndex count = CFStringGetLength(string); // The length of the string
UniChar character;
CFStringGetCharacters( string, CFMakeRange( count-1, 1), &character );
CGGlyph glyph;
CTStringGetGlyphsForCharacters( font, &character, &glyph,1 );
// Get the advance
CGSize advance;
CTFontGetAdvancesForGlyphs( font, 0, &glyph, &advance, 1);
// Get the bounds
CGRect bounds;
CTFontGetAdvancesForGlyphs( font, 0, &glyph, &bounds, 1);
在 Safari 中输入。
补充:你左边有同样的问题:向大写L前进是0(第一个字符),但是bounding box有一个负x。因此,左侧被剪裁。也在那里添加 space..(到目前为止更容易。)
在 boundingRectWithSize:options:context:
中使用 NSStringDrawingUsesDeviceMetrics
会取得更好的效果。
CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}];
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));
并根据 adjustedSize.Hope 更新标签的宽度和高度
1: 使用您当前选择的字体将文本设置为您的标签。
2: 使用此方法
- (CGSize)getSizeforController:(id)view
{
UILabel *tempView =(UILabel *)view;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
context.minimumScaleFactor = 0.8;
float height = tempView.frame.size.height;
CGSize size=[tempView.text boundingRectWithSize:CGSizeMake(2000, height)options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName : tempView.font}context:context].size;
return size;
}
3: 然后设置
CGSize newSize = [self.getSizeforController:self.lblYourLabel];
self.lblYourLabel.frame.size = newSize;
希望这对你有用。
试试这个
CGSize lableWidth = CGSizeMake(300, CGFLOAT_MAX);
CGSize requiredSize = [yourLabel sizeWithFont:[UIFont fontWithName:@"FontName" size:17] constrainedToSize:lableWidth lineBreakMode:NSLineBreakByWordWrapping];
int calculatedHeight = requiredSize.height;
这对我有用。
func labelThatFits(_ title: String) -> UILabel {
let titleLabel = UILabel()
titleLabel.font = UIFont(name: fontName, size: 20)
titleLabel.text = title
titleLabel.textAlignment = .center
titleLabel.widthAnchor.constraint(equalToConstant: titleLabel.intrinsicContentSize.width + 10).isActive = true
return titleLabel
}
我正在尝试解决这个问题,因为在我正在处理的应用程序中我有很多字体,因此当用户更改字体时标签的大小是动态计算的。
我遇到的问题是,如果字体是斜体,那么 UILabel 会在最后被剪掉,如下图所示:
这是我目前尝试过的方法:
- 借助 CoreText 和
CGSize CTFramesetterSuggestFrameSizeWithConstraints ( CTFramesetterRef framesetter, CFRange stringRange, CFDictionaryRef frameAttributes, CGSize constraints, CFRange *fitRange );
计算宽度
- 借助 NSAttributedString 和
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context
计算宽度
- 借助 NSString 和
- (CGSize)sizeWithAttributes:(NSDictionary<NSString *,id> *)attrs
计算宽度
- 使用临时 UITextView 和
sizeThatFits´ and
fitToSize`
由于应用程序中有很多字体,我需要动态设置标签的宽度,因此 UILabel 的子类化和在 drawFrameInRect
上添加更多点是行不通的。
这是 Github 上的示例代码。
任何 help/advice 不胜感激。
我会在您的按钮和标签上使用自动布局,以始终根据您选择标签的字体大小使其居中。
然后,删除 updateLabel
方法中的所有代码。我不确定您为什么使用 UITextview
并将爵士乐带入画面。如果您在情节提要中单击您的标签并查看属性检查器并找到 "Autoshrink" 它应该设置为 "Minimum Font Size" 而不是 "Fixed Font Size."
然后我会在您的标签上使用 NSAttributedString
来根据单击的按钮更改其大小和字体。
我下载了您的 GitHub 项目,看来 UILabel 和 UITextField 计算文本边界的方式肯定不同。因此,我首先尝试将 UITextField 上的 sizeToFit 的结果大小分配给 UILabel 的大小。但是,这种方法不适用于 Strato 字体。
在尝试了其他一些失败的方法之后,我记得斜体字体的角度大致相同。字体的斜体版本通常会按与原始宽度相同的比例增加边界框的宽度。
更新:
因此,您应该能够从斜体字符串的宽度和字体大小中推导出其宽度。我在你的项目中尝试过这种方法,它适用于你列出的所有字体。我使用了以下公式:
CGRect calculatedRect = [attributedString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) context:nil];
calculatedRect.size.width = calculatedRect.size.width + (label.font.pointSize * 0.2);
由于这取决于斜体增加的宽度,斜体加粗字体,如Strata,对宽度的影响较大。
显然,如果文本 API 得到扩充以包括可靠的文本宽度计算,您将能够采用它。但是,如果您想要向后兼容,我提出的解决方法可能仍然是更好的选择。
希望对您有所帮助!
即使我没有时间测试它,我也很确定问题出在标签尺寸是根据预付款计算的。前进是从一个角色的基点到下一个角色的移动量。通常对于斜体字体,advance 可以小于边界。因此添加预付款将切断布局的末尾。
即巴斯克维尔斜体 H
:
(lldb) p bounds[0].size
(CGSize) = (width=33.53515625, height=26.484375)
(lldb) p advances[0]
(CGSize) = (width=30, height=0)
所以我认为如果文本以理想的方式布局(无压缩等),则必须添加最后一个字符的前进框和边界框之间的区别。
要获得这两个值,您必须调用一些 CT 函数:
// What you probably have
CTFontRef font = …;
CFStringRef string = …;
CFAAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
// Get the character and glyph
CFIndex count = CFStringGetLength(string); // The length of the string
UniChar character;
CFStringGetCharacters( string, CFMakeRange( count-1, 1), &character );
CGGlyph glyph;
CTStringGetGlyphsForCharacters( font, &character, &glyph,1 );
// Get the advance
CGSize advance;
CTFontGetAdvancesForGlyphs( font, 0, &glyph, &advance, 1);
// Get the bounds
CGRect bounds;
CTFontGetAdvancesForGlyphs( font, 0, &glyph, &bounds, 1);
在 Safari 中输入。
补充:你左边有同样的问题:向大写L前进是0(第一个字符),但是bounding box有一个负x。因此,左侧被剪裁。也在那里添加 space..(到目前为止更容易。)
在 boundingRectWithSize:options:context:
中使用 NSStringDrawingUsesDeviceMetrics
会取得更好的效果。
CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}];
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));
并根据 adjustedSize.Hope 更新标签的宽度和高度
1: 使用您当前选择的字体将文本设置为您的标签。
2: 使用此方法
- (CGSize)getSizeforController:(id)view
{
UILabel *tempView =(UILabel *)view;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
context.minimumScaleFactor = 0.8;
float height = tempView.frame.size.height;
CGSize size=[tempView.text boundingRectWithSize:CGSizeMake(2000, height)options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName : tempView.font}context:context].size;
return size;
}
3: 然后设置
CGSize newSize = [self.getSizeforController:self.lblYourLabel];
self.lblYourLabel.frame.size = newSize;
希望这对你有用。
试试这个
CGSize lableWidth = CGSizeMake(300, CGFLOAT_MAX);
CGSize requiredSize = [yourLabel sizeWithFont:[UIFont fontWithName:@"FontName" size:17] constrainedToSize:lableWidth lineBreakMode:NSLineBreakByWordWrapping];
int calculatedHeight = requiredSize.height;
这对我有用。
func labelThatFits(_ title: String) -> UILabel {
let titleLabel = UILabel()
titleLabel.font = UIFont(name: fontName, size: 20)
titleLabel.text = title
titleLabel.textAlignment = .center
titleLabel.widthAnchor.constraint(equalToConstant: titleLabel.intrinsicContentSize.width + 10).isActive = true
return titleLabel
}