集合中的Swift UI绑定TextField [英] Swift UI Binding TextField in Collection

查看:57
本文介绍了集合中的Swift UI绑定TextField的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两列带有嵌套数据(父/子).第一列中的每个项目都是父项.选择其中任何一个时,它将在第二列中显示其子项作为列表.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆