Realm + NSTableView + NSArrayController
You could try to add the following to your model object (the Realm object) :
override func setValue(_ value: Any?, forKey key: String) {
try! realm?.write {
self.mySetValue(value, forKeyPath: key) // bug in swift preventing directly calling super here
}
}
private func mySetValue (_ value: Any?, forKeyPath key: String) {
super.setValue(value, forKey: key)
}
Make NSTableView row editable when adding object with NSArrayController
The workaround that I found here worked for me.
You need to subclass NSArrayController and implement your own add: method
//
// MyArrayController.h
//
#import <AppKit/AppKit.h>
@interface MyArrayController : NSArrayController
@end
and
//
// MyArrayController.m
//
#import "MyArrayController.h"
@implementation MyArrayController
- (void)add:(id)sender {
[super add:sender] ;
[self performSelector:@selector(selectLastObject)
withObject:nil
afterDelay:0.0] ;
}
- (void)selectLastObject {
if ([self selectsInsertedObjects]) {
NSArray* arrangedObjects = [self arrangedObjects] ;
NSInteger nObjects = [arrangedObjects count] ;
if (nObjects > 0) {
[self setSelectionIndex:nObjects-1] ;
}
}
}
@end
Sorting a NSArrayController backed NSTableView
If you want to bind the array controller's sort descriptor, you have to bind it to something. You can put this in your application delegate, for example:
- (NSArray *)tracksSortDescriptors {
return [NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:@"albumName"
ascending:YES]];
}
Then you can set up the binding in IB as
Bind to: MyAppDelegate
Model Key Path: tracksSortDescriptors
EDITED. I forgot, when translating this from PyObjC, that I was returning a list. Oops.
NSTableView backed by NSArrayController: why does setContent: work while IB doesn't?
Your array controller is observing your App Delegate's "array" property. That means KVO notifications are sent only when the array object is set, not when objects are added to it. It sounds like you are using an NSMutableArray and adding objects to it, which explains why the Array Controller is not being notified of changes, because the underlying object is not changing.
The easy solution is to wrap your calls in a will/did change block like so:
[self willChangeValueForKey:@"array"];
[self.array addObject:[NSDictionary dictionaryWithObject:@"foo" forKey:@"name"]];
[self.array addObject:[NSDictionary dictionaryWithObject:@"bar" forKey:@"name"]];
[self didChangeValueForKey:@"array"];
This manually notifies observers that there has been a change to the "array" property.
Long answer: You're doing it wrong. The whole point of having an array controller is to shift the work of managing the array to the controller class itself, so it manages the underlying array, sends out the right notifications, maintains state, etc. without you having to sweat the implementation details. A better solution would be to unhook the content array binding and just add objects to the array controller directly like so:
[arrayController addObject:[NSDictionary dictionaryWithObject:@"foo" forKey:@"name"]];
[arrayController addObject:[NSDictionary dictionaryWithObject:@"bar" forKey:@"name"]];
This works because the array controller manages its own array internally.
The best solution is to use Core Data. NSArrayController is designed to be used with it. You also get a whole bunch of things for free, like persistentce, undo support, object relationsips, and the ability to add objects without writing code just by calling add:
on the array controller directly from your UI controls.
Saving NSTableView Reordering in Core Data with NSArrayController Binding
Only the rows between the first and last dragged rows and the drop row need reindexing. NSArrayController.rearrangeObjects()
sorts the data objects into the new order.
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
if dropOperation == .above {
return .move
}
return []
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
if let items = billablesArrayController?.arrangedObjects as? [BillableItem] {
NSAnimationContext.runAnimationGroup({(NSAnimationContext) -> Void in
// put the dragged row indexes in an IndexSet so we can calculate which rows need moving and reindexing
let rowArray = info.draggingPasteboard.pasteboardItems!.map{ Int($0.string(forType: .string)!)! }
let draggedIndexes = IndexSet(rowArray)
tableView.beginUpdates()
// rows above drop row
if draggedIndexes.first! < row {
let indexesAboveDropRow = IndexSet(draggedIndexes.first! ..< row)
// move the dragged rows down, start at the bottom to prevent the animated rows from tumbling over each other
var newIndex = row - 1
indexesAboveDropRow.intersection(draggedIndexes).reversed().forEach { oldIndex in
tableView.moveRow(at: oldIndex, to: newIndex)
items[oldIndex].sortOrder = Int16(newIndex)
newIndex -= 1
}
// reindex other rows
indexesAboveDropRow.subtracting(draggedIndexes).reversed().forEach { oldIndex in
items[oldIndex].sortOrder = Int16(newIndex)
newIndex -= 1
}
}
// rows below drop row
if row < draggedIndexes.last! {
let indexesBelowDropRow = IndexSet(row ... draggedIndexes.last!)
// move the dragged rows up
var newIndex = row
indexesBelowDropRow.intersection(draggedIndexes).forEach { oldIndex in
tableView.moveRow(at: oldIndex, to: newIndex)
items[oldIndex].sortOrder = Int16(newIndex)
newIndex += 1
}
// reindex other rows
indexesBelowDropRow.subtracting(draggedIndexes).forEach { oldIndex in
items[oldIndex].sortOrder = Int16(newIndex)
newIndex += 1
}
}
tableView.endUpdates()
}) {
// rearrange the objects in the array controller so the objects match the moved rows
// wait until the animation is finished to prevent weird or no animations
self.billablesArrayController.rearrangeObjects()
}
// save
}
return true
}
Filtering a single-column NSTableView using NSArrayController
I have uploaded a project, kindly check.
A rough idea how to do is as: (however understanding is easier by seeing the project)
Create an Array Controller.
Set for Array controller Object
Mode:Class
Class Name: Your custom ClassReceived Actions
add: to the button that will add new objects, typically labelled with +
remove:to the button that will add new objects., typically labelled with -Referencing Bindings(either from table or from here for each column of table).
For search field
Bindings, Predicate to Array Controller
ControllerKey : filterPredicate
Predicate Format : <class property> contains $value
(if to search in multiple table columns then <class property 1> contains $value || <class property 2> contains $value etc…. )
Related Topics
Realm Data Insertion Took Over 7Mins for Big Size JSON
How to Create a UIprintpaper to Test UIprintinteractioncontrollerdelegate
<= Is Not a Prefix Unary Operator
How Pass Data from Button in Tableviewcell to View Controller
Wkwebview on Macos Cuts Off Top
Realitykit Entity Synchronization Is Always Nil
Swift-Making an Sknode Simply "Move Forward" on an Angle
Watchkit Extension Cannot Read from Icloud
Use Tableviewcontroller Inside Skscene
Swift Dictionary Initialization of Custom Type Gives: '>' Is Not a Postfix Unary Operator Error
How to Use Mtlblitcommandencoder for Copying Interlaced Video Fields into a Mtlbuffer
Thread Safety of Method Calls on "Shared" Static Constant Property
Set Maximum Characters (To One) in a Nstextfield in Swift
Programmatically Setting Texture in Scene Generated by Reality Composer