macOS 上的 SwiftUI 文本字段孤立
SwiftUI text field orphan on macOS
我有一个这样的文本字段
Text("Hello, one two three four five six seven eight!")
.frame(width:270)
.border(.blue)
当它呈现时,它决定将 7 和 8 放在第二行,即使第一行有 space 表示 7。更糟糕的是,它决定缩进截断的顶行,使其在框架内居中。
我该如何解决这个问题,以便在不考虑孤儿的情况下正确包装文本?
编辑:忘了说我想在 macOS 上使用它。我试图将它移植到 Mac。它确实正确地左对齐文本,但它不会换行到第二行。不过,盒子的高度确实得到了相应的计算。
这是我更新后的代码:
struct NonOrphanedText: View
{
var text: String
@State private var height: CGFloat = .zero
var body: some View
{
InternalLabelView(text: text, dynamicHeight: $height)
.frame(maxHeight: height)
}
struct InternalLabelView: NSViewRepresentable
{
var text: String
@Binding var dynamicHeight: CGFloat
func makeNSView(context: Context) -> NSTextField
{
let label = NSTextField()
label.isEditable = false
label.isBezeled = false
label.drawsBackground = false
label.isSelectable = false
label.maximumNumberOfLines = 5
label.usesSingleLineMode = false
label.lineBreakStrategy = .init()
label.lineBreakMode = .byWordWrapping
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return label
}
func updateNSView(_ nsView: NSTextField, context: Context)
{
nsView.stringValue = text
DispatchQueue.main.async
{
dynamicHeight = nsView.sizeThatFits(CGSize(width: nsView.bounds.width, height: CGFloat.greatestFiniteMagnitude)).height
}
}
}
}
我们需要 lineBreakStrategy
,但目前在 SwiftUI 中不可用,因此可能的解决方案是使用 UILabel
.
这是一个可能的解决方案。使用 Xcode 13.2 / iOS 15.2
测试
struct LabelView: View {
var text: String
@State private var height: CGFloat = .zero
var body: some View {
InternalLabelView(text: text, dynamicHeight: $height)
.frame(maxHeight: height)
}
struct InternalLabelView: UIViewRepresentable {
var text: String
@Binding var dynamicHeight: CGFloat
func makeUIView(context: Context) -> UILabel {
let label = UILabel()
label.numberOfLines = 0
label.lineBreakStrategy = .init() // << here !!
label.lineBreakMode = .byWordWrapping
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return label
}
func updateUIView(_ uiView: UILabel, context: Context) {
uiView.text = text
DispatchQueue.main.async {
dynamicHeight = uiView.sizeThatFits(CGSize(width: uiView.bounds.width, height: CGFloat.greatestFiniteMagnitude)).height
}
}
}
}
我有一个这样的文本字段
Text("Hello, one two three four five six seven eight!")
.frame(width:270)
.border(.blue)
当它呈现时,它决定将 7 和 8 放在第二行,即使第一行有 space 表示 7。更糟糕的是,它决定缩进截断的顶行,使其在框架内居中。
我该如何解决这个问题,以便在不考虑孤儿的情况下正确包装文本?
编辑:忘了说我想在 macOS 上使用它。我试图将它移植到 Mac。它确实正确地左对齐文本,但它不会换行到第二行。不过,盒子的高度确实得到了相应的计算。
这是我更新后的代码:
struct NonOrphanedText: View
{
var text: String
@State private var height: CGFloat = .zero
var body: some View
{
InternalLabelView(text: text, dynamicHeight: $height)
.frame(maxHeight: height)
}
struct InternalLabelView: NSViewRepresentable
{
var text: String
@Binding var dynamicHeight: CGFloat
func makeNSView(context: Context) -> NSTextField
{
let label = NSTextField()
label.isEditable = false
label.isBezeled = false
label.drawsBackground = false
label.isSelectable = false
label.maximumNumberOfLines = 5
label.usesSingleLineMode = false
label.lineBreakStrategy = .init()
label.lineBreakMode = .byWordWrapping
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return label
}
func updateNSView(_ nsView: NSTextField, context: Context)
{
nsView.stringValue = text
DispatchQueue.main.async
{
dynamicHeight = nsView.sizeThatFits(CGSize(width: nsView.bounds.width, height: CGFloat.greatestFiniteMagnitude)).height
}
}
}
}
我们需要 lineBreakStrategy
,但目前在 SwiftUI 中不可用,因此可能的解决方案是使用 UILabel
.
这是一个可能的解决方案。使用 Xcode 13.2 / iOS 15.2
测试struct LabelView: View {
var text: String
@State private var height: CGFloat = .zero
var body: some View {
InternalLabelView(text: text, dynamicHeight: $height)
.frame(maxHeight: height)
}
struct InternalLabelView: UIViewRepresentable {
var text: String
@Binding var dynamicHeight: CGFloat
func makeUIView(context: Context) -> UILabel {
let label = UILabel()
label.numberOfLines = 0
label.lineBreakStrategy = .init() // << here !!
label.lineBreakMode = .byWordWrapping
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return label
}
func updateUIView(_ uiView: UILabel, context: Context) {
uiView.text = text
DispatchQueue.main.async {
dynamicHeight = uiView.sizeThatFits(CGSize(width: uiView.bounds.width, height: CGFloat.greatestFiniteMagnitude)).height
}
}
}
}