集合中的Swift UI绑定TextField [英] Swift UI Binding TextField in Collection
问题描述
我有两列带有嵌套数据(父/子).第一列中的每个项目都是父项.选择其中任何一个时,它将在第二列中显示其子项作为列表.
I have two columns with nested data(Parent/child). Each item in first column is parent. When selecting anyone of them then it shows its child in second column as list.
从第二列中选择任何项目时,它必须在屏幕上显示"clipAttr".属性作为文本编辑器在第三列中进行编辑.
When selecting any item from second column then it must show "clipAttr" attribute in third column as text editor where we can edit it.
现在,当编辑"ClipAttr"时,我需要帮助,然后在SampleDataModel集合中自动更新.下面是完整的代码.
Now I need help how to do that when edit the 'ClipAttr' then it automatically update in SampleDataModel collection. Below is the complete code.
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")
}
}
}
推荐答案
首先使用自定义绑定.
SampleTextEditor(text: Binding(get: {
return self.vm.selectedItemClip?.clipAttr
}, set: {
self.vm.selectedItemClip?.clipAttr = $0
}))
第二,在子更新按钮上更新视图.
Second, update your view on child update button.
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
方法.
Inside the SampleEditorCoordinator class and SampleTextEditor struct use optional binding. And change your textDidChange
methods.
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")
}
}
}
这篇关于集合中的Swift UI绑定TextField的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!