SwiftUI 列表中的可编辑文本字段

Posted

技术标签:

【中文标题】SwiftUI 列表中的可编辑文本字段【英文标题】:Editable TextField in SwiftUI List 【发布时间】:2019-08-20 14:41:53 【问题描述】:

更新:14 个月后,AppKit 发行说明中有这样一个有趣的说明:

您在选定列表行中编辑的文本字段现在具有正确的文本前景色。 (68545878)

现在,当将 TextField 放入 List 时,TextField 在第一次被选中时变为焦点,但随后的编辑尝试失败:选中了 List 行但 TextField 没有获得焦点。

O/P:

在 beta6 SwiftUI macOS(not ios)应用程序中,我需要一个带有可编辑文本字段的 List,但列表中的 TextFields 不可编辑。如果我将List 换成Form(或只是VStack),它工作正常,但我需要它与List 一起使用。是否有一些技巧可以告诉列表使字段可编辑?

import SwiftUI

struct ContentView: View 
    @State var stringField: String = ""

    var body: some View 
        List  // works if this is VStack or Form
            Section(header: Text("Form Header"), footer: Text("Form Footer")) 
                TextField("Line 1", text: $stringField).environment(\.isEnabled, true)
                TextField("Line 2", text: $stringField)
                TextField("Line 3", text: $stringField)
                TextField("Line 4", text: $stringField)
                TextField("Line 5", text: $stringField)
            
        
    

【问题讨论】:

您能解释一下为什么要在 5 个文本字段中编辑同一个变量吗? 我不知道它应该做什么,但如果我剪切你的代码并将其粘贴到 Xcode 中并在模拟器中运行它,那么我可以编辑所有文本字段中的文本并显示编辑后的文本在所有文本字段中。它似乎在预览版中不起作用,但在预览版中却很少起作用,以至于我几乎从未使用过它。 我知道它可以在模拟器中工作,但如上所述,这是针对 macOS 应用程序,而不是 iOS 应用程序。绑定到同一个字符串字段只是一个示例——它可以很容易地成为 5 个不同的绑定,并且文本字段在 macOS 上将保持不可编辑。 很抱歉错过了它是一个 macOS 应用程序,尽管在我看来 SwiftUI 仅适用于 mac 上的催化剂。无论如何,它仍然对我有用,丑陋但实用。也许他们在 Beta 7 中修复了它。 看起来你发现了一个错误。对于它的价值ForEach(1...5, id: \.self) TextField("Line \($0)", text: self.$stringField) 有效,即使将其包装在 ScrollView() 【参考方案1】:

下面的代码只是一个实验,用来理解SwiftUI中List的特性,并展示一个替代方案。通过观察各种组合的输出,我了解到,List View 的样式的结构方式是覆盖底层 View 的默认行为,使其变为 Selectable。这意味着 TextField 完全不同。 TextField 是一个可聚焦的元素,我们可以在其中输入。此聚焦变量未连接到列表视图以协同工作。因此,List 会覆盖默认的可聚焦。因此无法使用 TextView 创建列表。但是,如果您需要,下一个最佳选择是 ScrollView 而不是 List 并明确地进行样式设置。检查以下代码和两种方式。

import SwiftUI

struct ContentView: View 
    @State private var arr = ["1","2","3"]
    var body: some View 
        HStack 
            VStack 
                List 
                    ForEach(self.arr.indices, id:\.self) 
                        TextField("", text: self.$arr[$0])
                    
                
            
            .frame(minWidth: 150, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
            VStack 
                ScrollView 
                    ForEach(self.arr.indices, id:\.self) 
                        TextField("", text: self.$arr[$0])
                        .textFieldStyle(PlainTextFieldStyle())
                        .padding(2)
                    
                
                .padding(.leading, 5)
                .padding(3)
            
            .background(Color(NSColor.alternatingContentBackgroundColors[0]))
            .frame(minWidth: 150, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
        
    


extension NSTextField 
    open override var focusRingType: NSFocusRingType 
        get  .none 
        set  
    

【讨论】:

【参考方案2】:

错误报告

我更新了项目以针对 MacOS 应用程序并找到您报告的错误。我已经用这个反馈更新了 Apple,因为它确实似乎是一个错误(欢迎使用 Beta)。

FB7174245 - 嵌入在列表中的 SwiftUI 文本字段不可编辑 当目标是 macOS 时

更新

那么下面所有关注状态和绑定的意义何在?一个变量应绑定到单个控件。这是working example。我将保留较旧的答案,因为它通过完整的应用程序向/从 CoreData 保存和检索数据的示例向前推进。

import SwiftUI

struct ContentView: View 
    @State var field1: String = ""
    @State var field2: String = ""
    @State var field3: String = ""
    @State var field4: String = ""
    @State var field5: String = ""

    var body: some View 
        List  // works if this is VStack or Form
            Section(header: Text("Form Header"), footer: Text("Form Footer")) 
                TextField("Line 1", text: $field1).environment(\.isEnabled, true)
                TextField("Line 2", text: $field2)
                TextField("Line 3", text: $field3)
                TextField("Line 4", text: $field4)
                TextField("Line 5", text: $field5)
            
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

顺便说一句 - 这适用于 Xcode Beta (11M392r) 和 MacOS Catalina - 10.15 测试版 (19A546d)。

示例项目

查看这个sample,其中包含一个可编辑的文本字段,它写入我在 Github 上构建的 CoreData。

看看@State 和@Binding 的区别,让你 可以从内容视图之外操作数据。 (60-sec video)

struct ContentView: View 
    // 1.
    @State var name: String = ""
    var body: some View 
        VStack 
            // 2.
            TextField(" Enter some text", text: $name)
                .border(Color.black)
            Text("Text entered:")
            // 3.
            Text("\(name)")
        
        .padding()
        .font(.title)
    

source

查看以下答案以了解 @State 的适当用例 (SO Answer)

【讨论】:

与状态的绑定工作正常。这不是关于 @Bindings/@State 如何工作的问题,而是关于为什么 TextField 在 macOS 上的 List 中时不可编辑的问题。 这不是答案。工作示例不起作用! 您好,我已尝试解决包装 NSTextField 的问题,但似乎 macOS 上的 List 中包含的任何视图都不可编辑。这是 macOS 上关于 SwiftUI 的最严重的错误 @JanKubny - 您可能希望使用反馈助手和此消息中的参考号,以便 Apple 给予更多关注。【参考方案3】:

SwiftUI 2.0

您可以在列表中使用 TextEditor 代替 TextField 以获得可编辑的文本字段。

TextEditor(text: $strings)
    .font(.body)
    .cornerRadius(5)
    .shadow(color: Color.black.opacity(0.18), radius: 0.8, y: 1)
    .frame(height: 20)

这样您将在 TextEditor 和 TextField 之间获得相似的方面。

【讨论】:

这在技术上确实有效,但 TextEditor 不能很好地替代 TextField:不支持格式化程序,也没有任何方法可以禁用多行编辑或启用选项卡-to-move-fields。【参考方案4】:

获得焦点——如果你点击得恰到好处

将 Xcode 12.4 和 SwiftUI 2 用于 ma​​cOS 应用程序,我似乎遇到了同样的问题:无法使 TextEdit 在列表中工作。阅读此处并进行更多尝试后,我意识到在我的情况下,TextField确实可靠地获得了焦点,但只有在您以正确的方式点击时。我认为并希望这不是应该的工作方式,因此我发布了问题TextField inside a List in SwiftUI on macOS: Editing not working well,解释了我的观察细节。

总而言之:在现有文本上单击确实会产生焦点(经过一小段恼人的延迟之后)。双击任何地方都不会集中注意力。在空白字段中的任意位置单击即可获得焦点。

【讨论】:

以上是关于SwiftUI 列表中的可编辑文本字段的主要内容,如果未能解决你的问题,请参考以下文章

UITableViewCell 中的可编辑文本字段

如何在 SwiftUI 中自动填充文本字段?

自定义表格视图单元格中的可编辑文本字段

Django 表单中的可编辑选择字段/下拉框

SwiftUI 列出多个自定义行编辑选项

SwiftUI - 检测文本字段中的变化