I'm making a custom view that I want to be bindings/core data compatible and represent a collection of data (a la NSTableView)
Is there any way my view can refer to a specific subset of the elements in the collection (other than the current selection) after a change by the user?
A bit of context:
The view is going to display a number of user-moveable boxes in a 2D space. Each box corresponds to a record in the model. Several can be moved at once, and I can't rely on the delta value being the same for each box (so no adding a delta to each selected object).
I guess I'm looking for something like an id assigned to each element of the content array by NSArrayController, so that the view can associate that id with each box. My first thought was to use the the index in a content array, but this could be messed up by undo/redo. I could subclass NSArrayController and get it to auto-generate an id for each model item, but does cocoa already do something like this already? Feels like I might be missing something.
I should have mentioned that I originally tried keeping each of the content array's elements stored in the view (as Peter suggests), but had them stored as keys in an dictionary.
The objects in the view didn't match the keys in the dictionary, so I assumed this meant that NSArrayController changed the proxy objects it uses to stand for model objects.
However, it turned out that NSDictionary copies its keys, so it seems to be no good for situations where you want to associate a particular instance of an object with another.
NSMapTable is its more flexible cousin, and can be configured not to copy its keys.
Why not just refer to the objects themselves? You can keep them in a set or array, whichever is appropriate.
If you really need an identifier of some sort: What for? What are you going to do with it?
Related
I have a NSTableView and I binded my Array Controller to it. The Array Controller is binded to my Managed Object Context. Everything in the NSTableView works but the objects in my TableView rearranges randomly when I start up my app. For example, the order of [1,2,3] will suddenly change to [3,1,2] and this happens occasionally. I want it so that things stay in the order they were put when making them. How do I stop the rearranging?
The rows in the CoreData data store are not sorted! If you want to view them by creation date you need to add your own timestamp to every object and pass a sort descriptor to the fetch request.
Edit: If you are using SQLite as your CoreData store I recommend to have a look at the SQLite file. This will give you an idea on how CoreData works and explain why certain things (like unordered rows) are the way they are.
P.S. I use Base to inspect SQLite databases. It's not cheap but quite nice.
I have a pretty straightforward MVC design question.
I've got a class with a bunch of properties, and a form to present an instance of the class. Several of the class properties are arrays - some are arrays of NSStrings to be presented in a one-dimensional table view, and some are arrays of sub-objects to be presented in a two-dimensional table view (one column per sub-object property). I don't actually want to do anything with the data in any of these tables - just present the contents in a scrollable, read-only table view.
During my first attempt at bindings, I added an object controller bound to the class instance. Then, I tried to bind each column of each table view to the "selection" member of the class, with a model key path specifying the array property of the instance (and, for the two-dimensional tables, a member of the sub-object). I was surprised that this didn't work for the columns of the one-dimensional tables.
Next, I added one array controller for every table, binding it to the "selection" member of the object controller. For the one-dimensional tables, I bound the column to the array controller with no model key path; for the two-dimensional tables, I bound the column to the array controller with a model key path specifying a property of the sub-object. This works - but for a window with seven tables, I have seven array controllers! That feels like overkill, since the tables aren't doing anything other than presenting data.
My question is simple: Is my design in line with good MVC practice - do I really need all of these array controllers? Or is there a simpler way to specify my bindings (for one-dimensional and/or two-dimensional tables) that will enable me to eliminate some array controllers? When I have an array of strings in an object to be displayed in a one-column table, it feels like overkill to use an array controller bound to the object and the table.
As an ancillary question - do I really need to worry about excessive array controllers? Are they lightweight objects that I should use liberally, or resource-intensive objects that I should conserve, especially in limited resource contexts like iOS?
I have a working system that lets me build a database containing instances of various entities , all linked together nicely.
Before I knew I would care, I came across a tutorial on using Core Data and bindings, and it went through a complete case where you get a table showing all the entities of some type with a column for each property. It showed both the UI side and the Data model side - not that I need the data model part at this point. Now, darned if I can find it. This is one of those things that is supposed to be easy, and requires virtually no code, but getting exactly the right connections in UIBuilder is not going to happen if I can't find instructions.
Also, I thought I came across an example of something like a query editor where the user could select which properties to sort on, which to match on, etc. Did I imagine that?
Anyone out there know where I can find such?
Sure, you can do this without code:
Add an array controller to your nib.
Bind or connect an outlet for its managed object context
Set the array controller to Entity mode, fill in the entity name, and select Prepares Content.
Bind your table view columns to array controller's arranged objects, and fill in the key name for the model key.
Regarding the query editor, open up the model, and on the Editor menu click Add Fetch Request.
I found at least a partial answer to the query editor question, in this apple tutorial. Not sure how far it will get me, as I prefer to write code where possible, since then I can leave a trail of comments.
Here's the scenario. There are two CoreData model objects, A and B, and the relationship between them is that A has-many B, represented by the property setOfBs. I'd like to display two tables, one listing all the As (Table 1), another listing all the Bs (Table 2). As the user selects items in Table 1, the selection in Table 2 changes to reflect the value of setOfBs of the A selected in Table 1. The content of the table doesn't change, only the selection changes. And if the selection in Table 1 changes, it would change the setOfBs to reflect that.
Can this be accomplished using bindings? Or would custom logic be required?
I believe you will have to write additional logic to get this approach to work. Here's why: The selection bindings for NSArrayControllers (and all the UI objects that bind to them) are based on selection indexes but when you have an object of type A, it vends a set of B objects. Assuming you have an array controller for As and an array controller for Bs, you need a way to get from those objects to their indexes in the array of Bs in order to set the selection of the array controller for B. This isn't hard code to write, but I don't believe you'll be able to do this with bindings alone.
That said, as you speculated in your comment, this doesn't seem like a good way to edit this relationship. In the common case, TableView selection is UI state, and not model state. If you build a UI like you describe, UI state and model state become the same thing. I'm not saying it's impossible, or inherently bad, but it's not really a "standard" way to do this sort of thing. One common pattern looks like this:
Even an approach like this will require additional logic, because there appears to not be a way (out of the box) to bind to "All Bs not in the selected A's setOfBs" without writing code. Again, not difficult code to write, but it's not clear to me that this can be done with bindings alone. I could be wrong, but that's my reading of the situation.
Having a list of data object and something visual to represent each, where would you code the sorting/filtering logic? Why?
Edit : All the answers so far are good, but I forgot to add another constraint. What if I don't want to reconstruct the view each time?
The answer lies in the data. The model delivers the data. If all the data is in the view, the filtering and sorting can be contained within the view. If the data is chunked, the model must deliver the data and contain some of filtering/sorting (the view may still contain filtering/sorting as well).
The controller should not contain these functions, since it is a routing mechanism and should not have any idea of how to interpret the data.
Depends on the complexity of the sort/filter operation and whether the view control offers those services natively. If the view control offers filtering and it's simply reformatting the in-memory data then leave it in the view. If the sort/filter requires another trip to the data source then keep it all in the controller.
I would put in the sorting and filtering methods in the controller, and call these methods from the view.
Your View should only handle displaying the output. Put any filtering/sorting into your business logic and return it to the view.
I believe the sorting should be something separate. You should not sort in the model because you want to keep it as-is. Basically, a change in the model implies a re render of the view and you probably do not want that (if you want to animate a transition between the pre and post filter states, for example).
What I would suggest is that the model provides the data to create both a list of visual objects for the view and a sorter object. The sorter object would output a render list which would simply be a list of some identifier linked to the visual objects (index in objects list or other). The order in which the IDs appear represents the order of the sorting and any ID not in the render list is hidden. Every time the view receives a render list, it would update it's display.