Swift UI 在集合中绑定 TextField
Posted
技术标签:
【中文标题】Swift UI 在集合中绑定 TextField【英文标题】:Swift UI Binding TextField in Collection 【发布时间】:2021-01-26 12:00:33 【问题描述】:我有两列包含嵌套数据(父/子)。第一列中的每个项目都是父项。选择其中的任何人时,它将其子于第二列显示为列表。
当从第二列中选择任何项目时,它必须在第三列中显示“clipAttr”属性作为文本编辑器,我们可以在其中编辑它。
现在我需要帮助如何在编辑“ClipAttr”时执行此操作,然后它会在 SampleDataModel 集合中自动更新。下面是完整的代码。
struct SampleClip: Identifiable, Hashable
var uid = UUID()
var id :String
var itemType:String?
var clipTitle: String?
var creationDate: Date?
var clipAttr:NSAttributedString?
struct SampleClipset: Identifiable, Hashable
var id = UUID()
var clipsetName :String
var isEditAble:Bool
init( clipsetName:String, isEditAble:Bool)
self.clipsetName = clipsetName
self.isEditAble = isEditAble
struct SampleClipItem: Identifiable, Hashable
var id = UUID()
var clipsetObject: SampleClipset
var clipObjects: [SampleClip]
class SampleDataModel: ObservableObject
@Published var dict:[SampleClipItem] = []
@Published var selectedItem: SampleClipItem?
didSet
if self.selectedItem != nil
if( self.selectedItem!.clipObjects.count > 0)
self.selectedItemClip = self.selectedItem!.clipObjects[0]
@Published var selectedItemClip: SampleClip?
didSet
if self.selectedItemClip != nil
struct SampleApp: View
@ObservedObject var vm = SampleDataModel()
@State var clipText = NSAttributedString(string: "Enter your text")
var body: some View
VStack
//Button
HStack
//Clipset button
VStack
Text("Add Parent data")
.padding(10)
Button("Add")
let clipset1 = SampleClipset(clipsetName: "Example clipset\(self.vm.dict.count)", isEditAble: false)
var clip1 = SampleClip(id: "0", itemType: "", clipTitle: "Clip 1")
clip1.clipAttr = NSAttributedString(string: clip1.clipTitle!)
clip1.creationDate = Date()
var clip2 = SampleClip(id: "1", itemType: "", clipTitle: "Clip 2")
clip2.clipAttr = NSAttributedString(string: clip2.clipTitle!)
clip2.creationDate = Date()
let item = SampleClipItem(clipsetObject: clipset1, clipObjects: [clip1, clip2] )
self.vm.dict.append(item)
Button("Update")
let index = self.vm.dict.count - 1
self.vm.dict[index].clipsetObject.clipsetName = "Modifying"
Divider()
//Clip button
VStack
Text("Add Child data")
.padding(10)
Button("Add")
let object = self.vm.dict.firstIndex(of: self.vm.selectedItem!)
if( object != nil)
let index = self.vm.selectedItem?.clipObjects.count
var clip1 = SampleClip(id: "\(index)", itemType: "", clipTitle: "Clip \(index)")
clip1.clipAttr = NSAttributedString(string: clip1.clipTitle!)
clip1.creationDate = Date()
self.vm.dict[object!].clipObjects.append(clip1)
self.vm.selectedItem = self.vm.dict[object!]
Button("Update")
let index = (self.vm.selectedItem?.clipObjects.count)! - 1
self.vm.selectedItem?.clipObjects[index].clipAttr = NSAttributedString(string:"Modifying")
.frame(height: 100)
//End button frame
//Start Column frame
Divider()
NavigationView
HStack
//Clipset list
List(selection: self.$vm.selectedItem)
ForEach(Array(self.vm.dict), id: \.self) key in
Text("\(key.clipsetObject.clipsetName)...")
.frame(width:200)
.listStyle(SidebarListStyle())
Divider()
VStack
//Clip list
if(self.vm.selectedItem?.clipObjects.count ?? 0 > 0)
List(selection: self.$vm.selectedItemClip)
ForEach(self.vm.selectedItem!.clipObjects, id: \.self) key in
Text("\(key.clipTitle!)...")
.frame(minWidth:200)
//TextEditor
Divider()
SampleTextEditor(text: self.$clipText)
.frame(minWidth: 300, minHeight: 300)
struct SampleApp_Previews: PreviewProvider
static var previews: some View
SampleApp()
//New TextView
struct SampleTextEditor: View, NSViewRepresentable
typealias Coordinator = SampleEditorCoordinator
typealias NSViewType = NSScrollView
let text : Binding<NSAttributedString>
func makeNSView(context: NSViewRepresentableContext<SampleTextEditor>) -> SampleTextEditor.NSViewType
return context.coordinator.scrollView
func updateNSView(_ nsView: NSScrollView, context: NSViewRepresentableContext<SampleTextEditor>)
if ( context.coordinator.textView.textStorage != text.wrappedValue)
context.coordinator.textView.textStorage?.setAttributedString(text.wrappedValue)
func makeCoordinator() -> SampleEditorCoordinator
let coordinator = SampleEditorCoordinator(binding: text)
return coordinator
class SampleEditorCoordinator : NSObject, NSTextViewDelegate
let textView: NSTextView;
let scrollView : NSScrollView
let text : Binding<NSAttributedString>
init(binding: Binding<NSAttributedString>)
text = binding
textView = NSTextView(frame: .zero)
textView.autoresizingMask = [.height, .width]
textView.textStorage?.setAttributedString(text.wrappedValue)
textView.textColor = NSColor.textColor
//Editor min code
textView.isContinuousSpellCheckingEnabled = true
textView.usesFontPanel = true
textView.usesRuler = true
textView.isRichText = true
textView.importsGraphics = true
textView.usesInspectorBar = true
textView.drawsBackground = true
textView.allowsUndo = true
textView.isRulerVisible = true
textView.isEditable = true
textView.isSelectable = true
textView.backgroundColor = NSColor.white
//
scrollView = NSScrollView(frame: .zero)
scrollView.hasVerticalScroller = true
scrollView.autohidesScrollers = false
scrollView.autoresizingMask = [.height, .width]
scrollView.documentView = textView
super.init()
textView.delegate = self
func textDidChange(_ notification: Notification)
switch notification.name
case NSText.didChangeNotification :
text.wrappedValue = (notification.object as? NSTextView)?.textStorage ?? NSAttributedString(string: "")
default:
print("Coordinator received unwanted notification")
//os_log(.error, log: uiLog, "Coordinator received unwanted notification")
【问题讨论】:
【参考方案1】:首先使用自定义Binding。
SampleTextEditor(text: Binding(get:
return self.vm.selectedItemClip?.clipAttr
, set:
self.vm.selectedItemClip?.clipAttr = $0
))
其次,更新您对子更新按钮的看法。
Button("Update")
guard let mainIndex = self.vm.dict.firstIndex(where: (data) -> Bool in
if let selectedId = self.vm.selectedItem?.id
return data.id == selectedId
return false
),
let subIndex = self.vm.dict[mainIndex].clipObjects.firstIndex(where: (data) -> Bool in
if let selectedId = self.vm.selectedItemClip?.id
return data.id == selectedId
return false
),
let obj = self.vm.selectedItemClip
else
return
self.vm.dict[mainIndex].clipObjects[subIndex] = obj
self.vm.selectedItem = self.vm.dict[mainIndex]
在 SampleEditorCoordinator 类和 SampleTextEditor 结构中使用可选绑定。并更改您的 textDidChange
方法。
struct SampleTextEditor: View, NSViewRepresentable
typealias Coordinator = SampleEditorCoordinator
typealias NSViewType = NSScrollView
let text : Binding<NSAttributedString?>
func makeNSView(context: NSViewRepresentableContext<SampleTextEditor>) -> SampleTextEditor.NSViewType
return context.coordinator.scrollView
func updateNSView(_ nsView: NSScrollView, context: NSViewRepresentableContext<SampleTextEditor>)
if ( context.coordinator.textView.textStorage != text.wrappedValue)
if let value = text.wrappedValue
context.coordinator.textView.textStorage?.setAttributedString(value)
// Other code
class SampleEditorCoordinator : NSObject, NSTextViewDelegate
let textView: NSTextView;
let scrollView : NSScrollView
var text : Binding<NSAttributedString?>
init(binding: Binding<NSAttributedString?>)
text = binding
// Other code
func textDidChange(_ notification: Notification)
switch notification.name
case NSText.didChangeNotification :
self.text.wrappedValue = NSAttributedString(attributedString: textView.attributedString())
default:
print("Coordinator received unwanted notification")
//os_log(.error, log: uiLog, "Coordinator received unwanted notification")
【讨论】:
谢谢,但它没有更新收藏。 @MajidKhan 你需要按下更新按钮。 是的,我尝试按更新按钮,但应用程序崩溃了。测试 Xcode 12.3以上是关于Swift UI 在集合中绑定 TextField的主要内容,如果未能解决你的问题,请参考以下文章
Swift 中的 Xcode:导航栏未显示在 UI 集合视图(模拟器)中