Thursday, September 21, 2017

#1 2009-09-02 02:17:50 pm

Craig Williams
Administrator
From:: Ft. Smith, AR
Registered: 2006-12-07
Posts: 888

AppleScriptObjC in Xcode Part 2 - TableView

UPDATE
GUI Building Video Complete!
Watch it here

Creating a "Book List" Project
In this tutorial we are going to create an application that displays a list of books with title, author and read or unread status. We will be using mostly Objective-C methods written in AppleScriptObjC. Here is a look at the finished project.

http://files.macscripter.net/unscripted/_cwilliams/AppleScriptObjC_Part2/final_proj.png

Final Project


Assumptions
a) You have read AppleScriptObjC Release Notes.
b) You have read AppleScriptObjC Part 1

About this Tutorial
I am going to list out the entire code upfront and go through it a line at a time. You can watch a short video, a little over eight minutes, that walks you through creating the GUI, making all the connections, setting the bindings, adding the images to the project and adding the image cell to the table view.

WATCH VIDEO HERE

Download Source Code

On to the Code!

Applescript:


(*
   We will be using both NSMutableArray and NSImage classes
   so we create a reference to them here.
   
   Any Objective-C class you wish to access in your code
   must be set up as a property.
*)

property NSMutableArray : class "NSMutableArray"
property NSImage : class "NSImage"

script PartTwoAppDelegate
   -- Inheritance
   -- Our class is a sub-class of NSObject
   property parent : class "NSObject"
   
   -- IBOutlets
   -- Interface Builder considers an outlet as any
   -- property with "missing value" as its initial value
   property aTableView : missing value
   property aWindow : missing value
   property bookTitleField : missing value
   property authorField : missing value
   property statusField : missing value
   
   -- Bindings
   (*
       We connect these in the bindings inspector
       to the corresponding text field values.
       
       Note:
           You cannot connect bindings directly
           to the NSTextFields above so we create
           new properties to hold the "value" of
           the NSTextFields.
   *)

   property theAuthor : ""
   property bookTitle : ""
   property theStatus : ""
   
   -- Other properties
   (*
       theDataSource will be an NSMutableArray
       We set this up in the awakeFromNib handler
   *)

   property theDataSource : {}
   
   -- IBActions (button clicks)
   -- Interface Builder considers an action as any
   -- single parameter method ending with an underscore
   on addData_(sender)
       
       (*
           Here we are creating a normal AppleScript record but we are using
           bindings to gather the information from text fields and
           radio buttons.
           
           Observation:
               I don't know if this is a bug or not.
               When you launch the application the radio
               button for value "Read" is checked but its
               value is "". Only after you click "Unread"
               and then "Read" again does it have the value
               of 0.
               
               This poses a problem when we want to sort by
               theStatus field and one or more of the items
               contains an empty string instead of an integer.
               
               We fix that by setting the value here before
               adding it to our array.
       *)

       if theStatus is "" then
           set theStatus to 0
       end if
       set newData to {bookTitle:bookTitle, theAuthor:theAuthor, theStatus:theStatus}
       (*
           This next method is calling the addObject_() method of NSMutableArray.
           We are adding the newData record to the NSMutableArray "theDataSource"
           as an array of dictionaries.
       *)

       theDataSource's addObject_(newData)
       
       (*
           Here we call the reloadData method of NSTableView
       *)

       aTableView's reloadData()
       
       -- Clear fields
       (*
           Clear the text fields for next data entry
       *)

       bookTitleField's setStringValue_("")
       authorField's setStringValue_("")
       
       (*
           If you comment out the following line, the author field
           will have focus after entering data. This is not the
           expected behavior.
           
           In IB I set bookTitleField as first
           responder by control dragging from the
           window to the text field and choosing
           "initialFirstResponder" but this method is
           required here if we want this behavior
           to continue.
           
           Note:
               This function requires a pointer to the
               window and ours is "aWindow."
       *)

       aWindow's makeFirstResponder_(bookTitleField)
   end addData_
   
   
   ##################################################
   # TableView
   
   (*
       Below are three NSTableView methods of which two are mandatory.
       
       Mandatory methods:
           These can be found in NSTableViewDataSource.
               tableView_objectValueForTableColumn_row_
               numberOfRowsInTableView_
       
       Optional method:
           This is found in NSTableViewDelegate.
               tableView_sortDescriptorsDidChange_
   *)

   
   on tableView_objectValueForTableColumn_row_(aTableView, aColumn, aRow)
       
       (*
           Check theDataSource's array size. If it is 0 then no need
           to go further in the code.
           
           Notice the use of "|" around count. Count is an AppleScript
           reserved word so we surround it with vertical bars.
       *)

       if theDataSource's |count|() is equal to 0 then return end
       
       (*
           The column identifier is the value you set IB
       *)

       set ident to aColumn's identifier
       
       (*
           NSMutableArray methods
               objectAtIndex_ returns the "record" from the array at
               the current iteration of aRow.
           
               objectForKey_ returns the specified item from the
               record. If ident is "age" then what is returned
               is the value of age.
               
           Note:
               The nice thing about this is that it is dynamic.
               We can add or remove as many columns as we like
               and never have to visit this code. The only
               unique area is where we are inserting an image.
               Take that out and this is boiler plate code.
       *)

       set theRecord to theDataSource's objectAtIndex_(aRow)
       set theValue to theRecord's objectForKey_(ident)
       
       (*
           isEqualToString_ is required to test the equivalency
           of ident against "theStatus."
       *)

       if ident's isEqualToString_("theStatus") then
           
           (*
               Same thing goes with testing theValue against 1.
               We need the intValue of theValue. Radio button
               groups return integer values.
               
               Note:
                   How many of you have wanted to insert images
                   into your AppleScript Studio table views?
                   Look how easy it is to do now!
                   
               Don't forget:
                   To use an Objective-C class you must make
                   a property reference first. We did this at
                   the top with the following:
                   
                   property NSImage : class "NSImage"
                   
               Images:
                   You can find the "red.tiff", and "green.tiff"
                   inside the project source folder.
                   
                   To add the images to your project right click
                   on the "Resources" folder, choose "Add..." then
                   "Existing files..."
                   
                   After choosing the files make sure the
                   check box at the top of the window saying
                   "Copy items into destination group's folder (if needed)"
                   is checked.
           *)

           if theValue's intValue() = 0 then
               set theValue to NSImage's imageNamed_("green")
           else
               set theValue to NSImage's imageNamed_("red")
           end if
           
       end if
       
       (*
           Return the "value" of theValue to the table view for display.
       *)

       return theValue
   end tableView_objectValueForTableColumn_row_
   
   on numberOfRowsInTableView_(aTableView)
       
       (*
           This is a mess but it works. When we get sample projects
           from Sal we will see a better way.
       *)

       try
           if theDataSource's |count|() is equal to null then
               return 0
           else
               
               (*
                   Required method. Simply returns the integer value
                   representing the number of items in our array "theDataSource."
               *)

               return theDataSource's |count|()
           end if
       on error
           return 0
       end try
   end numberOfRowsInTableView_
   
   on tableView_sortDescriptorsDidChange_(aTableView, oldDescriptors)
       
       (*
           When a user clicks on the table column headers this method
           is called. You can find it in the documentation under NSTableViewDelegate.
           
           For this to work you must set the "Sort Key" and "Selector" in the
           Table Column Attributes in IB.
           
           Note:
               Common Selectors are "compare:" and "caseInsensitiveCompare:"
               The colon is part of the name.
       *)

       set sortDesc to aTableView's sortDescriptors()
       theDataSource's sortUsingDescriptors_(sortDesc)
       aTableView's reloadData()
   end tableView_sortDescriptorsDidChange_
   
   
   
   ##################################################
   # Application
   
   on awakeFromNib()
       
       (*
           To use NSMutableArray we must initialize an instance of it.
           In Objective-C it looks like this:
               NSMutableArray *theDataSource = [[NSMutableArray alloc] init];
               
           Note:
               Remember, we can do this because we set a property at the top
               providing us access to the NSMutableArray class.
               
               property NSMutableArray : class "NSMutableArray"
       *)

       set theDataSource to NSMutableArray's alloc()'s init()
       
       (*
           Create a normal AppleScript list of records object
       *)

       set theData to {{bookTitle:"A Christmas Carol", theAuthor:"Charles Dickens", theStatus:1}, {bookTitle:"The Adventures of Huckleberry Finn", theAuthor:"Mark Twain", theStatus:1}, {bookTitle:"The Adventures of Tom Sawyer", theAuthor:"Mark Twain", theStatus:0}, {bookTitle:"War and Peace", theAuthor:"Leo Tolstoy", theStatus:0}}
       
       (*
           Call NSMutableArray's method addObjectsFromArray_() and
           add our list of records to it.
           
           Note:
               Once we add our "list of records" to theDataSource
               it is no longer a list of records, it is an
               Objective-C mutable array of dictionary objects.
       *)

       theDataSource's addObjectsFromArray_(theData)
       aTableView's reloadData()
   end awakeFromNib
   
   on applicationWillFinishLaunching_(aNotification)
       -- Insert code here to initialize your application before any files are opened
   end applicationWillFinishLaunching_
   
   on applicationShouldTerminate_(sender)
       (*
           Since we allocated memory for the NSMutableArray
           we clean up after ourselves when we are done.
       *)

       theDataSource's release()
       
       (*
           As pointed out by Shane Stanley
           my NSTerminateNow causes an error
           Easy fix it to return true instead
       *)

       return true
   end applicationShouldTerminate_
   
end script

Conclusion
Writing AppleScriptObjC is basically writing Objective-C using AppleScript syntax. My first suggestions is get "Cocoa Programming for Mac OS X Third Edition" by Aaron Hillegass and learn how to read the documentation. I really don't see a way to create meaningful applications using AppleScriptObjC without knowing Objective-C. I reserver the right to be wrong on this point though after we see some sample projects from Sal. smile

Until next time. Happy coding!

Download Source Code

Offline

 

#2 2009-09-02 09:29:00 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5125

Re: AppleScriptObjC in Xcode Part 2 - TableView

> I really don't see a way to create meaningful applications using AppleScriptObjC without knowing Objective-C.

I'm not convinced. It will require some Obj-C, but bindings really can avoid the need for lots of UI glue code.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#3 2009-09-03 06:58:01 pm

robdut
Member
Registered: 2009-09-02
Posts: 297

Re: AppleScriptObjC in Xcode Part 2 - TableView

Hi Craig,

Nice video too. Your'e the pioneer so far. I tried using a list of lists rather than a record to add to the datasource with no luck. Does the NSMutableArray only accept records. Ok if so, I am just used to using the list approach in ASS.

Looking forward to User defaults and saving of the book list. Normally I would try to figure all this out but am still over my head. I know the basics only of OBJ-C  but am reading up on it and Cocoa....

Thanks, Rob

Offline

 

#4 2009-09-03 08:57:22 pm

Craig Williams
Administrator
From:: Ft. Smith, AR
Registered: 2006-12-07
Posts: 888

Re: AppleScriptObjC in Xcode Part 2 - TableView

robdut wrote:

I tried using a list of lists rather than a record to add to the datasource with no luck. Does the NSMutableArray only accept records. Ok if so, I am just used to using the list approach in ASS.


No, NSMutableArray will accept objects, other arrays, dictionaries, etc. The reason you use an array of dictionaries ( "records" ) is that the columns always have the same data even when the user rearranges their columns. That is why you match up the column identifier with the "key" in the record. As an example, say the user moved the "Status" column in our app to the far right. If you were using an array instead of a record, the app would crash. Column 1 no longer has an NSImage cell to receive our image. Column 1 is now the "Book Title." Make sense?

Looking forward to User defaults and saving of the book list. Normally I would try to figure all this out but am still over my head. I know the basics only of OBJ-C  but am reading up on it and Cocoa....


Part 4 will include saving the book list but I am not sure yet whether it will have User Defaults. I think User Defaults will be one on its own.

Offline

 

#5 2009-09-04 02:59:27 am

MRoig
Member
Registered: 2008-04-27
Posts: 111

Re: AppleScriptObjC in Xcode Part 2 - TableView

Cool. I noticed you had to release the memory at the end. Does that mean AppleScriptObjC can't work with garbage collection? Or is that just your choice of programming-style?

Offline

 

#6 2009-09-04 07:59:04 am

Craig Williams
Administrator
From:: Ft. Smith, AR
Registered: 2006-12-07
Posts: 888

Re: AppleScriptObjC in Xcode Part 2 - TableView

MRoig wrote:

Cool. I noticed you had to release the memory at the end. Does that mean AppleScriptObjC can't work with garbage collection?


I believe AppleScriptObjC does work with Garbage Collection.

MRoig wrote:

Or is that just your choice of programming-style?


Yep, that's my style. cool

Offline

 

#7 2009-10-11 02:51:57 pm

Legionary
Member
From:: Bristol, UK
Registered: 2009-10-02
Posts: 3

Re: AppleScriptObjC in Xcode Part 2 - TableView

I have not been able to get this to work - the Author's name fails to show up in the TextView when I add new books.

When I tried building the project downloaded from MacScripter there was the same defect. I am wondering if there is something wrong with the way I have XCode set up, because I suspect the code, bindings etc. is correct.

Any ideas would be very welcome - I don't want to move on to the next part until I can get this to work.

Model: MacMini
AppleScript: 2.1
Browser: Safari 6531.9
Operating System: Mac OS X (10.6)

Offline

 

#8 2009-10-12 05:51:42 am

Heiner
Member
From:: Germany
Registered: 2007-08-25
Posts: 147

Re: AppleScriptObjC in Xcode Part 2 - TableView

Have a look at Craig's Part 4. There is the bug fixed.

Heiner

Offline

 

#9 2009-10-12 01:05:06 pm

Legionary
Member
From:: Bristol, UK
Registered: 2009-10-02
Posts: 3

Re: AppleScriptObjC in Xcode Part 2 - TableView

Thanks Heiner,

My head was hurting after building this example so many times, and I will go on to the next tutorial with relief.

Matthew

Offline

 

#10 2009-10-12 01:11:58 pm

Craig Williams
Administrator
From:: Ft. Smith, AR
Registered: 2006-12-07
Posts: 888

Re: AppleScriptObjC in Xcode Part 2 - TableView

Legionary wrote:

When I tried building the project downloaded from MacScripter there was the same defect.


I just downloaded the source and it works fine. Not sure what issue is causing your error.
Are there any errors being logged to the console?

Offline

 

#11 2009-10-16 02:11:03 pm

Legionary
Member
From:: Bristol, UK
Registered: 2009-10-02
Posts: 3

Re: AppleScriptObjC in Xcode Part 2 - TableView

In the downloaded example, when I entered a new book title, then the author, the Author name did not get carried into the text view. The problem is that the app was not "getting" the title until I had exited the second Text Field.

When I added the line
  tell bookTitleField to selectText_(me)
(taken from PartFour) the change made the PartTwo project behave as it should.

An alternative fix (which Craig gave me offline) is to adjust the bindings of the Text Field to 'Continuously Updates Value'.

This sequence of tutorials is very helpful - it's pretty much all new to me - and Craig's willingness to keep helping is welcome and generous. For my part, I am used to being an expert contributor (Excel VBA) and the switch to total bozo here is awkward.

Offline

 

#12 2009-12-10 10:59:50 am

pharmerjohn
Member
Registered: 2007-09-07
Posts: 24

Re: AppleScriptObjC in Xcode Part 2 - TableView

I'm confused. The "WATCH VIDEO HERE" link takes you to a video about project "read from file".

Carl

Offline

 

#13 2009-12-10 12:28:38 pm

Craig Williams
Administrator
From:: Ft. Smith, AR
Registered: 2006-12-07
Posts: 888

Re: AppleScriptObjC in Xcode Part 2 - TableView

pharmerjohn wrote:

I'm confused. The "WATCH VIDEO HERE" link takes you to a video about project "read from file".

Carl


Hi Carl,

I created a new website since this tutorial was made and my re-direct is not working properly. I have updated the links in this tutorial to the new location so please try again.

Regards,

Craig

Offline

 

#14 2009-12-10 02:15:02 pm

pharmerjohn
Member
Registered: 2007-09-07
Posts: 24

Re: AppleScriptObjC in Xcode Part 2 - TableView

OK, now it works. Thanks

Carl

Offline

 

#15 2011-03-25 11:32:06 am

47211
Member
Registered: 2008-01-04
Posts: 23

Re: AppleScriptObjC in Xcode Part 2 - TableView

I'm getting the following from console log, though this does compile pretty much ok

2011-03-25 12:24:27.900 ColorTable[59123:a0f] *** -[ColorTableAppDelegate tableView:objectValueForTableColumn:row:]: The variable theValue is not defined. (error -2753)

The Status column doesn't set the picture. At one point it stopped compiling and I narrowed it down to this section

Applescript:

on tableView_objectValueForTableColumn_row_(aTableView, aColumn, aRow)
       
       if theDataSource's |count|() is equal to 0 then return end
       
       
       set ident to aColumn's identifier
       
       set theRecord to theDataSource's objectAtIndex_(aRow)
       set theValue to theRecord's objectForKey_(ident)
       if ident's isEqualToString_("theStatus") then
           if theValue's intValue() = 0 then
               set theValue to NSImage's imageNamed__("green")
           else
               set theValue to NSImage's imageNamed__("red")
           end if
           
       end if
       
       return theValue

but then it started compiling again hmm

Any help?

AppleScript: xcode 3.2.6
Browser: Safari 534.16
Operating System: Mac OS X (10.6)

Offline

 

#16 2011-08-02 12:21:09 pm

carlcasca
Member
Registered: 2011-06-19
Posts: 8

Re: AppleScriptObjC in Xcode Part 2 - TableView

Hi,

Apparently Lion and xcode 4.1 aren't playing well with this script.  It crashes out of the box now.

See posts about this here: http://macscripter.net/viewtopic.php?pid=142738#p142738


cheers,
    Carl

Offline

 

#17 2014-07-08 04:53:01 am

tempistraven
Member
Registered: 2014-05-02
Posts: 4

Re: AppleScriptObjC in Xcode Part 2 - TableView

Is the video still being updated?

Offline

 

#18 2014-07-08 12:01:14 pm

Craig Williams
Administrator
From:: Ft. Smith, AR
Registered: 2006-12-07
Posts: 888

Re: AppleScriptObjC in Xcode Part 2 - TableView

@tempistraven - That site is also no longer maintained and I have not updated the video or located the original. If I do locate the original, I will post a link here.

Offline

 

#19 2014-12-26 04:27:08 am

tskamath
Member
From:: Bangalore
Registered: 2012-12-23
Posts: 1
Website

Re: AppleScriptObjC in Xcode Part 2 - TableView

Hi, Craig Williams..

found a Video http://www.dailymotion.com/video/xci9lr … d-par_tech
which links to  http://allancraig.net/ i am not sure if this is your missing Original..? unfortunatly its just part I - First part..

Merry Xmas and Happy New Year

Browser: Safari 537.36
Operating System: Mac OS X (10.8)

Last edited by tskamath (2014-12-26 04:28:30 am)

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)