简体   繁体   English

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.选择其中的任何人时,它将其孩子在第二列中显示为列表。

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”属性作为文本编辑器,我们可以在其中编辑它。

Now I need help how to do that when edit the 'ClipAttr' then it automatically update in SampleDataModel collection.现在我需要帮助在编辑“ClipAttr”时如何做到这一点,然后它会在 SampleDataModel 集合中自动更新。 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")
        }
    }
}

First use custom Binding.首先使用自定义绑定。

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]
}

Inside the SampleEditorCoordinator class and SampleTextEditor struct use optional binding.在 SampleEditorCoordinator class 和 SampleTextEditor 结构内部使用可选绑定。 And change your textDidChange methods.并更改您的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")
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM