Realm + Nstableview + Nsarraycontroller

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)

  1. Create an Array Controller.

  2. Set for Array controller Object

      Mode:Class

    Class Name: Your custom Class
  3. Received 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 -
  4. Referencing Bindings(either from table or from here for each column of table).

  5. 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



Leave a reply



Submit