NSTableView doesn't show data until I click on a header

  • I have an NSTableView bound to an NSArrayController displaying some data in my app that are calculated when the user clicks a button. Initially no data is shown, but it appears once I click on one of the column headers. All the columns are filled with the correct data as I specified in the bindings.  When the user then chooses another item to calculate the data from, the table should refresh, but the new data is not shown at all, and the old data doesn't go away.

    I've looked several times over my tableview, arraycontroller and bindings settings, but must have missed something too obvious.

    Any idea which setting I missed that could cause this behavior?

    Thanks,

    - Koen.
  • Not sure if this will help, but under iOS, there is a refreshTable method and also (IIRC) a "table is ready for refresh" variable.

    One thing you could do after your table data is loaded is make sure that a refresh happens so that it gets displayed.

    Hope this helps.

    On Apr 28, 2012, at 5:42 PM, Koen van der Drift wrote:

    > I have an NSTableView bound to an NSArrayController displaying some data in my app that are calculated when the user clicks a button. Initially no data is shown, but it appears once I click on one of the column headers. All the columns are filled with the correct data as I specified in the bindings.  When the user then chooses another item to calculate the data from, the table should refresh, but the new data is not shown at all, and the old data doesn't go away.
    >
    > I've looked several times over my tableview, arraycontroller and bindings settings, but must have missed something too obvious.
    >
    > Any idea which setting I missed that could cause this behavior?
    >
    > Thanks,
    >
    > - Koen.
  • On Apr 28, 2012, at 3:42 PM, Koen van der Drift wrote:

    > I have an NSTableView bound to an NSArrayController displaying some data in my app that are calculated when the user clicks a button. Initially no data is shown, but it appears once I click on one of the column headers. All the columns are filled with the correct data as I specified in the bindings.  When the user then chooses another item to calculate the data from, the table should refresh, but the new data is not shown at all, and the old data doesn't go away.
    >
    > I've looked several times over my tableview, arraycontroller and bindings settings, but must have missed something too obvious.
    >
    > Any idea which setting I missed that could cause this behavior?

    Are you updating the model data in a KVO-compliant way?

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • On Apr 28, 2012, at 6:21 PM, Keary Suska wrote:

    > Are you updating the model data in a KVO-compliant way?

    Bingo!  Thanks for pointing that out, I kept looking at my connections in IB, and forgot about the actual code.

    So for future reference, my data model is an NSMutableArray.  Instead of adding objects by calling addObject, I need to call it as follows:

    - (void)addMyObject:(MyObject *)obj
    {
        NSMutableArray *temp = [self mutableArrayValueForKey: @"myArray"];
        [temp addObject: obj];
    }

    And the same for all the other array changing methods.

    It all works fine now.

    - Koen.
  • I have a follow up question.

    One of the columns in the table shows a number (a float), and I have added a min and max value property to my controller to filter the data in the table. I could filter the data every time the user changes the min and/or max values but that means I need to recreate the data every time which is not very efficient. So bindings to the rescue, I thought.

    However, NSTableColumn does not have min/max bindings to use.  I tried adding an NSNumberFormatter to the column, but there are no min/max bindings for that as well.

    Is there a way to do this?

    - Koen.

    On Apr 29, 2012, at 12:22 PM, Koen van der Drift wrote:

    >
    > On Apr 28, 2012, at 6:21 PM, Keary Suska wrote:
    >
    >> Are you updating the model data in a KVO-compliant way?
    >
    > Bingo!  Thanks for pointing that out, I kept looking at my connections in IB, and forgot about the actual code.
    >
    > So for future reference, my data model is an NSMutableArray.  Instead of adding objects by calling addObject, I need to call it as follows:
    >
    > - (void)addMyObject:(MyObject *)obj
    > {
    > NSMutableArray *temp = [self mutableArrayValueForKey: @"myArray"];
    > [temp addObject: obj];
    > }
    >
    > And the same for all the other array changing methods.
    >
    > It all works fine now.
    >
    >
    > - Koen.
  • On Apr 29, 2012, at 6:24 PM, Koen van der Drift wrote:

    > I have a follow up question.
    >
    > One of the columns in the table shows a number (a float), and I have added a min and max value property to my controller to filter the data in the table. I could filter the data every time the user changes the min and/or max values but that means I need to recreate the data every time which is not very efficient. So bindings to the rescue, I thought.
    >
    > However, NSTableColumn does not have min/max bindings to use.  I tried adding an NSNumberFormatter to the column, but there are no min/max bindings for that as well.

    Yes, it does, but only if the cell prototype has an NSNumberFormatter attached to it. How did you apply the formatter? Table columns cannot have formatters that I know of. Setting an outlet may not be sufficient, if you are using that approach.

    > On Apr 29, 2012, at 12:22 PM, Koen van der Drift wrote:
    >
    >>
    >> On Apr 28, 2012, at 6:21 PM, Keary Suska wrote:
    >>
    >>> Are you updating the model data in a KVO-compliant way?
    >>
    >> Bingo!  Thanks for pointing that out, I kept looking at my connections in IB, and forgot about the actual code.
    >>
    >> So for future reference, my data model is an NSMutableArray.  Instead of adding objects by calling addObject, I need to call it as follows:
    >>
    >> - (void)addMyObject:(MyObject *)obj
    >> {
    >> NSMutableArray *temp = [self mutableArrayValueForKey: @"myArray"];
    >> [temp addObject: obj];
    >> }
    >>
    >> And the same for all the other array changing methods.
    >>
    >> It all works fine now.

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • On Apr 29, 2012, at 9:22 AM, Koen van der Drift <koenvanderdrift...> wrote:

    > So for future reference, my data model is an NSMutableArray.  Instead of adding objects by calling addObject, I need to call it as follows:
    >
    > - (void)addMyObject:(MyObject *)obj
    > {
    > NSMutableArray *temp = [self mutableArrayValueForKey: @"myArray"];
    > [temp addObject: obj];
    > }
    >
    Is there any reason not to implement the appropriate KVC accessor methods for myArray?

    <https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Key
    ValueCoding/Articles/AccessorConventions.html#//apple_ref/doc/uid/20002174-
    BAJEAIEE
    >

    (See also <https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Key
    ValueObserving/Articles/KVOCompliance.html#//apple_ref/doc/uid/20002178-BAJ
    EAIEE
    >.)

    mmalc
  • On Apr 29, 2012, at 8:45 PM, Keary Suska wrote:

    > Yes, it does, but only if the cell prototype has an NSNumberFormatter attached to it. How did you apply the formatter?

    By dragging it in IB onto the cell of the column.

    - Koen.
  • Thanks for the link. I am confused now, since it says:

    "Indexed Accessor Pattern
    [...]
    There are indexed accessors which return data from the collection (the
    getter variation) and mutable accessors
    that provide an interface for mutableArrayValueForKey: to modify the
    collection."

    Which (I think) is what I am doing now. But it's too early and I
    haven't drank all my coffee yet :)

    - Koen.

    On Sun, Apr 29, 2012 at 10:56 PM, mmalc Crawford <mmalc_lists...> wrote:
    >
    > On Apr 29, 2012, at 9:22 AM, Koen van der Drift <koenvanderdrift...>
    > wrote:
    >
    > So for future reference, my data model is an NSMutableArray.  Instead of
    > adding objects by calling addObject, I need to call it as follows:
    >
    > - (void)addMyObject:(MyObject *)obj
    > {
    >    NSMutableArray *temp = [self mutableArrayValueForKey: @"myArray"];
    >    [temp addObject: obj];
    > }
    >
    > Is there any reason not to implement the appropriate KVC accessor methods
    > for myArray?
    >
    > <https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Key
    ValueCoding/Articles/AccessorConventions.html#//apple_ref/doc/uid/20002174-
    BAJEAIEE
    >
    >
    > (See also
    > <https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Key
    ValueObserving/Articles/KVOCompliance.html#//apple_ref/doc/uid/20002178-BAJ
    EAIEE
    >.)
    >
    > mmalc
    >
  • On Apr 30, 2012, at 04:53 , Koen van der Drift wrote:

    > Thanks for the link. I am confused now, since it says:
    >
    > "Indexed Accessor Pattern
    > [...]
    > There are indexed accessors which return data from the collection (the
    > getter variation) and mutable accessors
    > that provide an interface for mutableArrayValueForKey: to modify the
    > collection."
    >
    > Which (I think) is what I am doing now. But it's too early and I
    > haven't drank all my coffee yet :)

    There are multiple ways of getting more or less the same result. The one you are using is slightly (and unnecessarily) circuitous, so mmalc is suggesting you use the more fundamental mechanism.

    What's at stake here is KVO compliance. If you have a class MyClass with an indexed (array) property "things", you can implement the indexed mutable accessors -- basically 'insertObject:inThingsAtIndex:' and 'removeObjectFromThingsAtIndex:'. (Note that the property name should be a plural noun for best readability.) Then, if you maintain the underlying array's contents *only* by calling these two methods -- either internally in the MyClass implementation, or externally from client objects after adding the methods to the MyClass public interface -- your MyClass class is fully KVO-compliant for the "things" property.

    You can still have an "addMyObject:" public method if you want. In this scenario, it should just invoke 'insertObject:inThingsAtIndex:'.

    OTOH, if you don't implement the KVO-compliant accessors, but do use the mutable array proxy -- this is your current approach -- then the proxy realizes you haven't implemented the accessors in your class, so it generates the KVO notifications itself. The result is that updates going through the proxy are KVO compliant.

    (If you implement the accessors *and* use the proxy, the proxy object doesn't generate additional notifications. It knows that the accessors have that covered.)

    Implementing the KVO accessor methods is a slightly more efficient approach, since it doesn't require the creating of a short-lived proxy object for every addition to the array.
  • On Mon, Apr 30, 2012 at 12:03 PM, Quincey Morris
    <quinceymorris...> wrote:

    > What's at stake here is KVO compliance. If you have a class MyClass with an
    > indexed (array) property "things", you can implement the indexed mutable
    > accessors -- basically 'insertObject:inThingsAtIndex:' and
    > 'removeObjectFromThingsAtIndex:'. (Note that the property name should be a
    > plural noun for best readability.) Then, if you maintain the underlying
    > array's contents *only* by calling these two methods -- either internally in
    > the MyClass implementation, or externally from client objects after adding
    > the methods to the MyClass public interface -- your MyClass class is fully
    > KVO-compliant for the "things" property.

    Very nice explanation, thanks.

    I am using an NSMutableArray to store the myObjects, the table
    displays several properties of each object in seperate columns.  So
    the order in which the objects are stored in the array is not
    critical, they will be ordered once displayed in the table and the
    user can select which column to use for ordering. Therefore I don't
    think I need to use the 'indexed mutable accessors', but the 'mutable
    unordered accessors' as discussed in the same link from mmalc.  Am I
    correct here?

    - Koen.
  • On Apr 30, 2012, at 09:27 , Koen van der Drift wrote:

    > I am using an NSMutableArray to store the myObjects, the table
    > displays several properties of each object in seperate columns.  So
    > the order in which the objects are stored in the array is not
    > critical, they will be ordered once displayed in the table and the
    > user can select which column to use for ordering. Therefore I don't
    > think I need to use the 'indexed mutable accessors', but the 'mutable
    > unordered accessors' as discussed in the same link from mmalc.  Am I
    > correct here?

    No, you have to use the indexed accessors, because the NSArrayController that mediates between your data model and your UI (NSTableView) expects the property to be indexed -- i.e. to behave like an array -- even though the contents get re-sorted before display.

    [Confusingly, this is only true when you're using an array controller in "Class" (normal) mode. When you're using an array controller in "Entity" (Core Data) mode, it expects the property to be unordered, since that how Core Data to-many properties are modeled. Of course, in this case, you're not implementing the accessors yourself.]
  • Got it, it all works (I think :).

    The only thing that made me scratch my head was how to empty the whole
    array.  Right now I am using this, but any suggestions or improvements
    are welcome:

    NSRange range = NSMakeRange(0, [self countOfMyObjects]);
    NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange: range];

    [self removeMyObjectsAtIndexes: indexes];

    - (void) removeMyObjectsAtIndexes: (NSIndexSet *)indexes
    {
        [self.myArray removeObjectsAtIndexes: indexes];
    }

    Thanks,

    - Koen.

    On Mon, Apr 30, 2012 at 12:40 PM, Quincey Morris
    <quinceymorris...> wrote:
    > On Apr 30, 2012, at 09:27 , Koen van der Drift wrote:
    >
    > I am using an NSMutableArray to store the myObjects, the table
    > displays several properties of each object in seperate columns.  So
    > the order in which the objects are stored in the array is not
    > critical, they will be ordered once displayed in the table and the
    > user can select which column to use for ordering. Therefore I don't
    > think I need to use the 'indexed mutable accessors', but the 'mutable
    > unordered accessors' as discussed in the same link from mmalc.   Am I
    > correct here?
    >
    >
    > No, you have to use the indexed accessors, because the NSArrayController
    > that mediates between your data model and your UI (NSTableView) expects the
    > property to be indexed -- i.e. to behave like an array -- even though the
    > contents get re-sorted before display.
    >
    > [Confusingly, this is only true when you're using an array controller in
    > "Class" (normal) mode. When you're using an array controller in "Entity"
    > (Core Data) mode, it expects the property to be unordered, since that how
    > Core Data to-many properties are modeled. Of course, in this case, you're
    > not implementing the accessors yourself.]
  • On Wed, May 2, 2012 at 10:15 PM, Koen van der Drift
    <koenvanderdrift...> wrote:
    > Got it, it all works (I think :).
    >
    > The only thing that made me scratch my head was how to empty the whole
    > array.

    I am of course aware of [myArray removeAllObjects], but is that KVO
    compliant? I couldn't find that in the docs.

    - Koen.
  • On May 3, 2012, at 6:33 PM, Koen van der Drift wrote:

    > On Wed, May 2, 2012 at 10:15 PM, Koen van der Drift
    > <koenvanderdrift...> wrote:
    >> Got it, it all works (I think :).
    >>
    >> The only thing that made me scratch my head was how to empty the whole
    >> array.
    >
    > I am of course aware of [myArray removeAllObjects], but is that KVO
    > compliant? I couldn't find that in the docs.
    >

    It's not, no. If you want a KVO way of removing all the objects use the remove<key>AtIndexes: or just set<Key>: with an empty array. You'll get different types of KVO message (NSKeyValueChangeRemoval vs NSKeyValueChangeSetting).
  • On May 3, 2012, at 6:45 AM, Roland King wrote:

    > It's not, no. If you want a KVO way of removing all the objects use the remove<key>AtIndexes: or just set<Key>: with an empty array. You'll get different types of KVO message (NSKeyValueChangeRemoval vs NSKeyValueChangeSetting).

    Thanks! I was indeed using remove<key>AtIndexes: , but set<Key> seems a bit easier to do.

    - Koen.
  • On May 3, 2012, at 03:52 , Koen van der Drift wrote:

    > Thanks! I was indeed using remove<key>AtIndexes: , but set<Key> seems a bit easier to do.

    The setter is not quite so easy at it seems. You should declare the property with the 'copy' option, so that the parameter is copied by any @synthesized setter. If you write your own setter, then you need to make sure you make the copy yourself. Otherwise, the caller can (in effect) change your array ivar at will (by passing a NSMutableArray object to the setter, and mutating it later).

    Also, at the moment you make the copy, there are *three* arrays in existence: your current ivar value, the copy (your new ivar value), and the parameter to the setter. If the arrays are large or otherwise expensive to create, then the copying and memory consumption of the extra copies might be uncomfortably unperformant.

    In fact, depending on the expected sizes of the arrays involved, the "best" implementation of the setter might actually be one that avoids making the copy:

    - (void) setMyArray: (NSArray*) myArray {
      [myArrayIvar removeObjectsAtIndexes: … index set of all elements …];
      [myArrayIvar addObjectsFromArray: myArray];
    }

    IOW, it would use the wordier code technique that you started out to avoid. :)
previous month april 2012 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
Go to today