SwiftUI for MacOS 中的多行 TextField/TextEditor 表单
Posted
技术标签:
【中文标题】SwiftUI for MacOS 中的多行 TextField/TextEditor 表单【英文标题】:Multiline TextField/TextEditor in SwiftUI for MacOS in forms 【发布时间】:2021-12-04 12:09:57 【问题描述】:我需要一个适用于 MacOS 的 SwiftUI 多行文本输入控件,满足以下要求:
允许像在编辑器中一样进行光标控制(即按 RETURN 会导致换行) 表单中的工作标签我尝试使用带有 lineLimit() 修饰符的 TextField,它看起来完全符合我的需要,即标签显示正确(包括对齐),但如果它为空且 RETURN 键没有,则它只有 1 行的高度t 做我想做的事(即换行):
struct ContentView: View
@State var field1 = ""
@State var field2 = ""
@State var notes = ""
var body: some View
Form
TextField("Label", text: $field1)
TextField("Long Label", text: $field2)
TextField("Notes", text: $notes)
.lineLimit(10)
.padding()
.frame(height: 150)
然后我尝试了一个 TextEditor,但这缺乏定义标签的能力。标签的位置使得 Form 元素对 MacOS 非常有用,因为它允许标签的正确对齐而没有任何黑客攻击。缺少边框样式只是一个小问题,可以使用边框样式解决:
struct ContentView: View
@State var field1 = ""
@State var field2 = ""
@State var notes = ""
var body: some View
Form
TextField("Label", text: $field1)
TextField("Long Label", text: $field2)
TextEditor(text: $notes)
.padding()
.frame(height: 150)
我只对面向未来的干净解决方案感兴趣。如果没有,hack 必须至少非常灵活,即所有标签必须正确对齐。 workingdog 的解决方案不适合我,因为一旦标签文本发生变化,一切都会崩溃。
【问题讨论】:
您可以尝试使用HStack
,例如:HStack Text("Note") TextEditor(text: $notes)
为您的文本编辑器获取“标签”。
不幸的是,这并不容易。标签将与其他文本字段左对齐,注释字段将被插入。 “表单”以某种方式识别“真实”文本字段的标签并执行所有对齐操作。但不适用于 HStack 中的自构建标签。
【参考方案1】:
我做了一个“自定义”Form
看起来像一个真实的。
代码:
struct ContentView: View
@State private var field1 = ""
@State private var field2 = ""
@State private var notes = ""
@State private var maxLabelWidth: CGFloat?
var body: some View
VStack
FormItem("Label", text: $field1)
FormItem("Long Label", text: $field2)
FormItem("Notes", text: $notes, kind: .textEditor)
.padding()
.onPreferenceChange(MaxWidthKey.self) maxWidth in
maxLabelWidth = maxWidth
.environment(\.maxLabelWidth, maxLabelWidth)
struct MaxWidthKey: PreferenceKey
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat)
value = max(value, nextValue())
struct MaxLabelWidthKey: EnvironmentKey
static let defaultValue: CGFloat? = nil
extension EnvironmentValues
var maxLabelWidth: CGFloat?
get self[MaxLabelWidthKey.self]
set self[MaxLabelWidthKey.self] = newValue
struct FormItem: View
enum Kind
case textEditor
case textField
@Environment(\.maxLabelWidth) private var maxLabelWidth
@Binding private var text: String
private let title: String
private let kind: Kind
init(_ title: String, text: Binding<String>, kind: Kind = .textField)
_text = text
self.title = title
self.kind = kind
var body: some View
HStack(alignment: .top)
Text(title)
.foregroundColor(Color(NSColor.labelColor))
.frame(maxWidth: maxLabelWidth, alignment: .trailing)
.background(
GeometryReader geo in
Color.clear.preference(
key: MaxWidthKey.self,
value: geo.size.width
)
)
.padding(.top, 3)
switch kind
case .textEditor:
TextEditor(text: $text)
.font(.system(size: 13))
.padding(.top, 3)
case .textField:
TextField("", text: $text)
结果:
虽然它没有设置TextEditor
背景,但它可能与您所获得的一样接近。
【讨论】:
这是迄今为止最好的解决方案!非常感谢你做的这些。此外,这可能是未来的证明,因为它不是一种“hacky”方法。【参考方案2】:这种方法怎么样(根据您的需要调整):
struct ContentView: View
@State var field1 = ""
@State var field2 = ""
@State var notes = ""
var body: some View
VStack (alignment: .leading, spacing: 20)
Form
TextField("Label", text: $field1)
TextField("Long Label", text: $field2)
HStack (alignment: .top)
Spacer().frame(width: 30)
Text("Notes")
TextEditor(text: $notes).frame(height: 200)
.padding()
.frame(height: 400)
【讨论】:
我想过这样的黑客攻击,但希望有一个干净的解决方案。这样做的问题是,您必须手动调整标签的宽度,而“表单”会根据所有标签的最大宽度自动调整。如果我使用这样的东西,我可能会完全删除“表单”,这样表单控件的宽度和 HStack 中的控件之间就不会出现差异。【参考方案3】:我个人更喜欢在这种情况下将相同的视图与overlay
放在一起,如下所示:
Form
TextField("Label", text: $field1)
TextField("Long Label", text: $field2)
TextEditor(text: $notes)
.overlay(
TextEditor(text: .constant("label"))
.allowsHitTesting(false)
.opacity(notes.isEmpty ? 1 : 0)
)
缺点是TextEditor
不像大多数其他 SwiftUI 视图那样工作:它自己绘制默认背景。您可以使用this hack 使光标通过叠加层可见,并自己在主TextEditor
上绘制背景。
【讨论】:
我不明白这种方法。如果我使用此代码,则左侧的“标签”列中没有文本编辑器的标签,但它会覆盖到文本编辑器本身。此外,无法在文本编辑器中输入任何内容。我使用的是针对 MacOS 12 的 XCode 13.1。您是否使用这些版本对其进行了测试? 这些是我正在使用的确切版本。您是否从链接的答案中添加了NSTextView
扩展名?
好的,通过链接的破解,文本是可编辑的。但是没有表单标签,不太理想。以上是关于SwiftUI for MacOS 中的多行 TextField/TextEditor 表单的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI for MacOS 窗口,带圆角,不带标题栏
在 SwiftUI for macOS 应用程序中获取点击手势的鼠标/指针位置
Swiftui: MacOS App: Fetching Core Data in ContentView OK,但是如何使用 AppDelegate 的结果呢?