Commits

Anonymous committed 7c8b933

Today's recipe details how to use View based TableViews in MacRuby / Xcode.

Comments (0)

Files changed (1)

Populate_a_view_based_TableView.rb

+class PopulateViewBasedTableView 
+	#-- 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)
+	#     @filename = filename
+	#     @file_uri = url
+	#   end
+	#
+	# 	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
+	# 	end
+	# end
+
+	#-- Properties
+	attr_accessor :array_of_objects
+
+	def awakeFromNib
+		#-- 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.
+		tmp_array = Array.new
+		['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)
+		end
+		#-- 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
+	end
+
+	#-- 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
+
+end