簡體   English   中英

Swift UI 在集合中綁定 TextField

[英]Swift UI Binding TextField in Collection

我有兩列包含嵌套數據(父/子)。 第一列中的每個項目都是父項。 選擇其中的任何人時,它將其孩子在第二列中顯示為列表。

從第二列中選擇任何項目時,它必須在第三列中顯示“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")
        }
    }
}

首先使用自定義綁定。

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 class 和 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")
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM