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.
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")
}
}
}
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. 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")
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.