+ #-- Goal: Demonstrate the needed code to populate a OS X.7 (Lion) and newer view based table view.
+ #---- These tableviews are far more powerful and easier to use than cell based table views.
+ #-- View backed table views, combined with Cocoa bindings, enable you to embed multiple data-bound objects
+ #---- in the same "cell". This makes it blindingly easy to combine say, a NSProgressIndicator Object, a Text Label object
+ #---- and a NSImageView all within the same TableView column. Thus you can have this built out:
+ #---- | NSImageView | NSLables | NSProgressIndicator| ------------ as the contents of a column.
+ #-- Our contrived example will deal with a file download class, with a filename, icon and progress indicator
+ # class EmbededFileDownloadDemoClass
+ # attr_accessor :filename, :file_uri, :icon, :progress_indicator, :local_storage_name
+ # def initialize(filename, url, *stuff)
+ # def awesome_method_to_download_file_while_updating_progress_bar
+ # #do awesome stuff. See https://bitbucket.org/codefriar/macruby-recipes/src/8e4f01ddf9b2/Control_Mac_Apps_via_Scripting_Bridge.rb
+ attr_accessor :array_of_objects
+ #-- this object should be instantiated by IB, and therefore awakeFromNib works as the constructor
+ #-- We will utilize some odd, non-idiomatic ruby assignment opperations here for reasons that will be explained in a bit.
+ ['http://www.gutenberg.org/ebooks/132.epub.noimages', 'http://www.gutenberg.org/ebooks/38842.epub.images'].each do |link|
+ tmp_array << EmbededFileDownloadDemoClass.new("somename", link)
+ #-- Why this odd usage of a temp array and invocation of self? Cocoa Bindings rely on Key-Value Oriented computing conventions.
+ #---- This means the bindings are looking for valueWillChange / valueDidChange methods to be executed to notify the bindings
+ #---- that a value has changed. MacRuby ensures that attr_accessors are KVO compliant but that still requires you to set
+ #---- the value of the instance variable via the attr_accessor created accessor. Calling self.array_of_objects= invokes the KVO
+ #---- compliant setter, assigning it the value of our temp array (the temp array keeps us from calling this multiple times)
+ self.array_of_objects = tmp_array
+ #-- And thats it'. From a code perspective that's all you have to do. Generate your array of objects for the table view to
+ #---- draw from in a KVO compliant way and the rest is done in Interface builder. IB makes this easy, but it can be too
+ #---- easy. Until I'd done this a couple of times I kept trying to make it harder than it was. So here's the Step-By-Step
+ #---- Breakdown, of what to do in ib.
+ #-- In Xcode, click on MainMenu.xib and verify you see your main window / Interface builder screen.
+ #-- From the Object Library (usually the bottom right of your xCode / IB screen) Drag an "Array Controller Object" to the Object Dock
+ #---- That's the panel of objects and placeholders between the graph-paper IB window editor and the file's list on the left.
+ #-- In Xcode, drag an "Object" from the object library to the object dock. Click on the Object in the Object dock, then select the
+ #---- Identity Inspector (Looks like a tiny window) and for the Class text box type "PopulateViewBasedTableView" and save the project.
+ #-- Click on the Array controller in the object dock, and then click on the Bindings Inspector icon on the Right most panel of xCode
+ #---- The Bindings Inspector icon looks kind of like a Recycling symbol. Find the section heading titled "Controller Content"
+ #---- expand the triangle next to Content Array. Check the box labeled "Bind To" and choose "PopulateViewBasedTableView"
+ #---- In the Model Key Path input type "array_of_objects"
+ #---- What this does is tell xCode / Cocoa that our Array Controller is bound to the Object that controls our array_of_objects
+ #---- in this case, the object named PopulateViewBasedTableViews. Specifically, while the array controller is bound to the object
+ #---- ViewBasedTableView, the Model Key Path that we set specifies that the Array Controller will be bound to one Array property
+ #---- of our object, allowing us to have one class with multiple arrays each with independant array controllers.
+ #-- Now that we have a bound Array controller, we can introduce a table view. Drag a table view from the object library to the window.
+ #---- For our example, use the attributes inspector (looks like a margin indicator) to set the TableView Content Mode
+ #---- to "View Based" <--- This is VERY important!!!! and set the columns to 1.
+ #-- Switch over to the Bindings inspector for your table view and under the header Table Content check the box
+ #---- "Bind to" and select "Array Controller". Ensure that the controller key is set to "arrangedObjects"
+ #---- What this does is tell the tableview that it's content is bound to the array controller's arrangedObjects. This allows us
+ #---- to bypass the need for the table view informal protocols we needed to create in the Cell based table recipe.
+ #-- Now, using the object library drag an "Image & Text Table Cell View" into your table column listed in the object panel. Note:
+ #---- it will show up in the object dock as "Table Cell View"
+ #---- There will now be two "rows" of text saying "Table View Cell" in your xCode window. Using the object panel highlight the one
+ #---- that has a single "Static Text - Table View Cell" under it and delete it.
+ #-- Using the object library drag an NSProgressIndicator Object over to the object pannel. It will show up in the Object Library as
+ #---- an Indeterminate Progress Indicator. With the Progress Indicator still selected, use the Attributes Inspector to
+ #---- uncheck the "Indeterminate" checkbox.
+ #-- Now comes the confusingly simple bit. We're going to bind each of those objects in our view, the image, the text and the indicator
+ #---- to the objects that are properties of the object at "row" index of our Array controller's bound Array.
+ #-- If that sounds like a bit much to chew, hang with me. it's really easy.
+ #-- The object panel in the middle is going to be key to grabing ahold of the right object so be sure to use the disclosure triangles
+ #---- as needed to get down to the exact object.
+ #-- Using the Object panel navigate through disclosures until you get to:
+ #---- Table View -> Table Cell View -> Static Text - Table Cell View -> Text Field Cell
+ #---- With the Text Field Cell Highlighted, open the Bindings Inspector and under value, check "Bind To" and select "Table Cell View"
+ #---- In the Model Key Path box, type "objectValue.filename"
+ #-- This tells xCode / Cocoa that this object (the Text View) is bound to the object backing it's parent Text view
+ #---- (by binding it to Table Cell View!) and that it's bound value is property "filename" of objectValue. objectValue is cocoa
+ #---- magic, any bound object view has it. Effectively, what we've told Cocoa to do is display the property filename, of the
+ #---- object that to be displayed in that row. We'll now do the same thing, for the icon and the progress indicator.
+ #-- Using the Object panel navigate and highlight the Image Cell, and while highlighted, use the binding inspector to
+ #---- Check the Bind to box and choose Table Cell View. Under Model Key Path type objectValue.icon
+ #-- Lastly, usign the same steps above highlight the Horizontal Progress Indicator and bind it to Table Cell View with a model
+ #---- key path of objectValue.progress_indicator
+ #-- Now, just for fun, you can take an instance of your objects from the array and update it's progress using the bindings thusly
+ #-- self.array_of_objects.first.progress_indicator = 50