Post

Replies

Boosts

Views

Activity

Reply to Observe a tree of Core Data relationships?
Yes, there was an option I missed. NSTreeController seems to handle this really well, with a few small quirks. I had to add a 'rules' property to my rule. Simple extension: extension Rule { @objc public var rules: NSSet? { return self.passToLayer?.rules } } Setting up the binding was a little weird, as I've never done that in code before. Wound up with roughly this: @IBOutlet var ruleViewContainer:NSView! var ruleOutlineView:NSOutlineView = NSOutlineView() private var layerToShow:Layer private var treeController:NSTreeController = NSTreeController() ... override func viewWillAppear() { ... let outlineContainer = NSScrollView(frame:ruleViewContainer.bounds) outlineContainer.autoresizingMask = [.width, .height] outlineContainer.documentView = ruleOutlineView outlineContainer.hasVerticalScroller = true outlineContainer.hasHorizontalScroller = true ruleViewContainer.addSubview(outlineContainer) ruleOutlineView.usesAlternatingRowBackgroundColors = true ruleOutlineView.usesAutomaticRowHeights = true ruleOutlineView.delegate = self ruleOutlineView.dataSource = self ruleOutlineView.doubleAction = #selector(self.doubleClick) ruleOutlineView.target = self treeController.childrenKeyPath = "rules" treeController.bind(NSBindingName.content, to: layerToShow, withKeyPath: "rules", options: nil) ruleOutlineView.bind(NSBindingName.content, to: treeController, withKeyPath: "arrangedObjects", options: nil) } Now all the items in the NSOutlineView are of type NSTreeNode. This broke my existing drag-and-drop and outlineView(_ outlineView:, viewFor tableColumn:, item:). Easy enough to fix. Just wrap all the references to item like this: let representedItem = (item as! NSTreeNode).representedObject Then use representedItem just like you used item before. I handle the updates in my API interaction object, which then updates my Core Data store if the server confirms the attempted change was successful. The UI then updates. I still have a few weird glitches. Moved objects are sometimes out of order. That should be much easier to figure out, though.
Aug ’21
Reply to Color swatches in SwiftUI picker?
Figured it out. I used a modified version of this NSImage+TintColor extension to create a swatch dictionary: let namedColors:[String] = [ "aquamarine", "black", "blue", ... } let myColorSwatches:[String:NSImage] = { (colorNames:[String]) -> [String:NSImage] in var toReturn:[String:NSImage] = [:] for colorName in colorNames { let image = NSImage(systemSymbolName: "rectangle.fill", accessibilityDescription: nil)! image.isTemplate = false image.lockFocus() NSColor(named: colorName)!.set() let imageRect = NSRect(origin: .zero, size: image.size) imageRect.fill(using: .sourceIn) image.unlockFocus() toReturn[colorName] = image } return toReturn }(namedColors) ... which I can then reference in my SwiftUI Picker: Picker("Color:", selection: pickerColor) { ForEach(namedColors, id: \.self) { colorName in HStack { Image(nsImage: myColorSwatches[colorName]!) Text(colorName) } } } And just as a reminder in case anybody else wants to do this, I have a color set defined in my Assets.xcassets for each named color.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jun ’21
Reply to NSOutlineView.insertItems(at:inParent:withAnimation) not drawing new items
Figured it out. My supercategories were defined as a struct with two properties: name and frc. While reading over some forum hits for the umpteenth time, this one - https://developer.apple.com/forums/thread/68477 finally clicked. Switched them over to use a class instead, and now everything is happy. From: Swift struct sidebarCategoryItem { let name:String let frc:NSFetchedResultsControllerCPMObject_CD } to this instead: Swift class sidebarCategoryItem { let name:String let frc:NSFetchedResultsControllerCPMObject_CD init(name initName:String, frc initFRC:NSFetchedResultsControllerCPMObject_CD) { self.name = initName self.frc = initFRC } } +5 lines of code, and -a million lines added for troubleshooting.
Topic: UI Frameworks SubTopic: AppKit Tags:
Apr ’21
Reply to NSOutlineView.insertItems(at:inParent:withAnimation) not drawing new items
Okay, this is definitely something weird about the parent object. I tried this: Swift let parent = myOutlineView.child(topLevelSectionIndex!, ofItem: nil)! print("parent: \(String(describing: parent))") print("parent row: \(myOutlineView.row(forItem: parent))") let row2Parent = myOutlineView.item(atRow: 2)! print("row2Parent: \(String(describing:row2Parent))") print("row2Parent row: \(myOutlineView.row(forItem: row2Parent))") and I get this in my log: text parent: supercategory parent row: -1 row2Parent: supercategory row2Parent row: 2 The String(describing:) of both objects is identical, though the objects are clearly different internally. And if I tell insertItems to insert something into row2Parent, it works! The problem is I only know the row indexes when my application has no data downloaded. As I fetch stuff and as the user expands and collapses sections, the rows for the supercategories will change. I clearly can't get it just by the row number, and it isn't telling me the row number for the object I got from its child(_:ofItem:) method. I've tried getting the object directly from the NSOutlineViewDataSource method outlineView(_:child:ofItem:). No change. Debug descriptions of the objects are identical, but it still shows row -1. Tried getting the object directly from the array I use for the supercategories. No change. Does anybody know what sources insertItems(_:at:inParent:) accepts for the object in the inParent?
Topic: UI Frameworks SubTopic: AppKit Tags:
Apr ’21
Reply to NSOutlineView.insertItems(at:inParent:withAnimation) not drawing new items
Realized a bit ago I forgot to include the code which sets topLevelSectionIndex. It was in a block of debug prints which I trimmed from the code above: Swift let topLevelSectionIndex:Int? = { for (index, section) in objectSections.enumerated() { if controller == section.frc { return index } } return nil }() let myOutlineView:NSOutlineView = myScrollView.documentView! as! NSOutlineView switch type { ... I tried adding this code to my case .insert: just above the insertItems: Swift print("Parent is expandable? \(myOutlineView.isExpandable(parent))") print("Parent is expanded? \(myOutlineView.isItemExpanded(parent))") myOutlineView.expandItem(parent) print("Parent is expanded? \(myOutlineView.isItemExpanded(parent))") and it gave me this in my logs: text Parent is expandable? true Parent is expanded? false Parent is expanded? false I get the same results whether the supercategory is expanded or collapsed before I download my data and my code is called. This makes me think the parent object I'm getting isn't the parent object the NSOutlineView wants me to use.
Topic: UI Frameworks SubTopic: AppKit Tags:
Apr ’21
Reply to Drag from NSOutlineView to SwiftUI view?
Further update. I tried simply ignoring the NSItemProvider objects and instead finding the drag pasteboard and reading the objects from it directly. This feels really awkward, but it works. My drag source is the same as my original post. Here is the working drop target: .onDrop(of: ["public.text"], isTargeted: nil) { _ in mainViewLogger.trace("Entered drop handler block.") let dragBoard = NSPasteboard(name: .drag) guard let draggedItems = dragBoard.readObjects(forClasses: [NSString.self], options: nil) else { return false } print("Pulled \(draggedItems.count) items from the drag pasteboard: \(String(describing: draggedItems))") return false } This code obviously doesn't do anything with the dropped data, and it always reports an unsuccessful drop, but the draggedItems list does actually contain the dragged objects.
Topic: UI Frameworks SubTopic: AppKit Tags:
Jan ’21
Reply to Drag from NSOutlineView to SwiftUI view?
I realized this morning I had not tried dragging from TextEdit into the SwiftUI view. Again, the green orb with the + shows up, and again when I drop the text, I get a provider with no registered types. If I change the NSOutlineDataSource method to this: func outlineView( _ outlineView: NSOutlineView, pasteboardWriterForItem item: Any)> NSPasteboardWriting? { sidebarControllerLogger.trace("Drag operation started.") guard let workingItem = item as? MyEntity else { sidebarControllerLogger.debug("Dragging something which isn't an object! Description: \(String(describing: item), privacy: .public)") return nil } let provider = NSItemProvider(object: workingItem.name! as NSString) print("Provider created for types \(String(describing:provider.registeredTypeIdentifiers))") return workingItem.name! as NSString } then I get this in my logs when I start a drag: Provider created for types ["public.utf8-plain-text"] This seems to me to reinforce the idea that "public.text" is an appropriate UTType for my test drop target code. The provider still doesn't offer it, though. If I try to return the provider I made above, I get a build-time error: "Type of expression is ambiguous without more context".
Topic: UI Frameworks SubTopic: AppKit Tags:
Jan ’21