问题:
UILabel
可能会在左右边缘剪切斜体(斜)字符甚至脚本。以下屏幕截图显示了该问题。在左边缘,'j'的下降部分被剪掉;在右边缘,'l'的上升部分被剪掉。我意识到这是微妙的,并不是每个人都会关心(但是,更大的字体大小会使问题变得更糟)。
这是使用Zapfino的一个不那么微妙的例子,大小为22.请注意,jupiter中的'j'看起来几乎像'i':
在上面的示例中,标签的背景颜色为橙色,文本左对齐,标签保持其内在内容大小。
这是UILabel
的默认行为,对于多个版本的iOS来说就是这样(所以我不希望苹果公司提供修复)。
我尝试了什么:
将标签的clipsToBounds
属性设置为NO
无法解决问题。我也知道我可以在标签上设置固定的宽度约束,使文本在后端有更多的空间。但是,固定宽度约束不会给上面示例中的'j'提供更多空间。
我将使用利用自动布局和标签alignmentRectInsets
的解决方案来回答我自己的问题。
答案 0 :(得分:21)
顶部标签显示文本左对齐时UILabel
的默认行为,标签保持其内在内容大小。底部标签是UILabel
的简单(几乎是微不足道的)子类。底部标签不会剪切'j'或'l';相反,它使文本在左右边缘有一些呼吸空间,而没有居中对齐文本(yuck)。
虽然标签本身在屏幕上看起来没有对齐,但它们的文字看起来是对齐的;而且,在IB中,标签的左边缘实际上是对齐的,因为我覆盖了alignmentRectInsets
子类中的UILabel
。
以下是配置两个标签的代码:
#import "ViewController.h"
#import "NonClippingLabel.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *topLabel;
@property (weak, nonatomic) IBOutlet NonClippingLabel *bottomLabel;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *string = @"jupiter ariel";
UIFont *font = [UIFont fontWithName:@"Helvetica-BoldOblique" size:28];
NSDictionary *attributes = @{NSFontAttributeName: font};
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
self.topLabel.attributedText = attrString;
self.bottomLabel.attributedText = attrString;
}
这是NonClippingLabel
子类的实现:
#import <UIKit/UIKit.h>
@interface NonClippingLabel : UILabel
@end
@implementation NonClippingLabel
#define GUTTER 4.0f // make this large enough to accommodate the largest font in your app
- (void)drawRect:(CGRect)rect
{
// fixes word wrapping issue
CGRect newRect = rect;
newRect.origin.x = rect.origin.x + GUTTER;
newRect.size.width = rect.size.width - 2 * GUTTER;
[self.attributedText drawInRect:newRect];
}
- (UIEdgeInsets)alignmentRectInsets
{
return UIEdgeInsetsMake(0, GUTTER, 0, GUTTER);
}
- (CGSize)intrinsicContentSize
{
CGSize size = [super intrinsicContentSize];
size.width += 2 * GUTTER;
return size;
}
@end
不编辑字体文件,不使用Core Text;对于那些使用iOS 6+和自动布局的人来说,这只是一个相对简单的UILabel
子类。
<强>更新强>
Augie发现我的原始解决方案阻止了多行文字的自动换行。我使用drawInRect:
代替drawAtPoint:
来修复该问题,以便在标签的drawRect:
方法中绘制文字。
以下是截图:
顶级标签是普通香草UILabel
。底部标签是NonClippingLabel
,具有极端的排水沟设置,以容纳22.0尺寸的Zapfino。两个标签都使用自动布局左右对齐。
答案 1 :(得分:2)
来自bilobatum的固定NonClippingLabel
方法的sizeThatFits
的快速版本。
class NonClippingLabel: UILabel {
let gutter: CGFloat = 4
override func draw(_ rect: CGRect) {
super.drawText(in: rect.insetBy(dx: gutter, dy: 0))
}
override var alignmentRectInsets: UIEdgeInsets {
return .init(top: 0, left: gutter, bottom: 0, right: gutter)
}
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width += gutter * 2
return size
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
let fixedSize = CGSize(width: size.width - 2 * gutter, height: size.height)
let sizeWithoutGutter = super.sizeThatFits(fixedSize)
return CGSize(width: sizeWithoutGutter.width + 2 * gutter,
height: sizeWithoutGutter.height)
}
}
答案 2 :(得分:0)
无需费力去解决这个愚蠢的Apple bug(我做到了),快速而简单的修改只是在您的末尾添加一个空格字符串以停止截断最后一个斜体字母。显然对多行标签a,或裁剪的第一个字母降序没有帮助...
答案 3 :(得分:0)
Swift 和 SwiftUI 版本基于Vadim Akhmerov和bilobatum的答案。所有四个边缘现在都可以自定义,并且可以更改/更新。
还作为Github Gist托管:https://gist.github.com/ryanlintott/2340f35977bf2d1f7b6ea40aa379bcc6
import SwiftUI
import UIKit
struct NoClipText: UIViewRepresentable {
typealias UIViewType = NoClipLabel
let text: String
let font: UIFont
let clipExtension: EdgeSizes
func makeUIView(context: Context) -> UIViewType {
let uiView = UIViewType()
uiView.text = text
uiView.font = font
uiView.clipExtension = clipExtension
return uiView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
uiView.text = text
uiView.font = font
uiView.clipExtension = clipExtension
}
}
class NoClipLabel: UILabel {
static let defaultClipExtension: EdgeSizes = .all(10)
var clipExtension: EdgeSizes
var top: CGFloat { clipExtension.top }
var left: CGFloat { clipExtension.left }
var bottom: CGFloat { clipExtension.bottom }
var right: CGFloat { clipExtension.right }
var width: CGFloat { left + right }
var height: CGFloat { bottom + top }
required init(clipExtension: EdgeSizes = defaultClipExtension) {
self.clipExtension = clipExtension
super.init(frame: CGRect.zero)
}
override init(frame: CGRect) {
clipExtension = Self.defaultClipExtension
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
clipExtension = Self.defaultClipExtension
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
super.drawText(in: rect.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)))
}
override var alignmentRectInsets: UIEdgeInsets {
return .init(top: top, left: left, bottom: bottom, right: right)
}
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width += width
size.height += height
return size
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
let fixedSize = CGSize(width: size.width - width, height: size.height - height)
let sizeWithoutExtension = super.sizeThatFits(fixedSize)
return CGSize(width: sizeWithoutExtension.width + width,
height: sizeWithoutExtension.height + height)
}
}
struct EdgeSizes: Equatable {
let top: CGFloat
let left: CGFloat
let bottom: CGFloat
let right: CGFloat
init(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
self.top = top
self.left = left
self.bottom = bottom
self.right = right
}
init(vertical: CGFloat = 0, horizontal: CGFloat = 0) {
self.top = vertical
self.left = horizontal
self.bottom = vertical
self.right = horizontal
}
init(_ all: CGFloat) {
self.top = all
self.left = all
self.bottom = all
self.right = all
}
static let zero = EdgeSizes(0)
static func all(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(size)
}
static func vertical(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(vertical: size)
}
static func horizontal(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(horizontal: size)
}
}