Reference a UI item by calling it a variable (to shorten code)?

In my UI i have a text view inside a tab view. I have to call the text view several times to get its contents or to set/change its contents. I have the reference to the text view spelled out in long form. But is there a way I can reduce the text to a much smaller code block?

I have:
set contents of text view “resultstextinner” of scroll view “resultstextouter” of tab view item “allresults” of tab view “resultstab” of window “main” to myCurrentResults

Can I do this earlier in the script?
set myTextView to text view “resultstextinner” of scroll view “resultstextouter” of tab view item “allresults” of tab view “resultstab” of window “main”
…and then…
set contents of myTextView to myCurrentResults

I tried that but I got an error saying something like “can’t get item id 28 of item id 27 of item id 26”.

Thanks, Chris

Yes, you can do this. In fact, I recommend it, if only to clean up your code. BUT, you can’t just throw that line in there wherever you want. There are two approaches to this that I use, and whether you use one (or both) depends on a handful of factors. The easiest way is to connect an ‘awake from nib’ handler to each object you want to store a reference to. This is an especially useful method if you want to catch a reference to the object and also perform other launch-time initializations. For example…

property resultstextinner : null
property someButton : null

on awake from nib theObject
	if name of theObject is "resultstextinner" then
		set resultstextinner to theObject
		set content of theObject to ""
	else if name of theObject is "someButton" then
		set someButton to theObject
		set enabled of theObject to false
	end if
end awake from nib

I tend to name my variables the same as my object names, which makes it even easier to identify them. Obviously, though, this can get pretty cumbersome if you have dozens of interface elements to create references to. It will clutter up your awake from nib handler and require a separate if/else block for every object, but it also completely eliminates the chance you’ll mess up the view hierarchy path of an object.

Another option is to create initializer handlers for certain launch-time tasks. For example, you could use a handler that would configure an object, a view, a window, a collection of objects or tasks, or for all of your app initializations at once. The number of handlers you use and what you put in them will obviously be determined by your preference and circumstances. You could then call these handlers from an appropriate event handler, such as the ‘launched’ handler of the application or an appropriate object’s ‘awake from nib’ handler. For example, to get a bunch of references in one neat block of code, you could create a handler and fire it from your application object’s ‘launched’ handler…

property windowMain : null
property resultstab : null
property allresults : null
property resultstextinner : null

on launched theObject
	initObjects()
end launched

to initObjects()
	set windowMain to window "main"
	set resultstab to tab view "resultstab" of windowMain
	set allresults to tab view item "allresults" of resultstab
	set resultstextinner to text view "resultstextinner" of scroll view "resultstextouter" of allresults
end initObjects

The biggest pitfall to watch out for when doing it this way is to make sure that your handler is called at the right time. You must wait until after the objects referenced have awakened from nib (otherwise they won’t technically exist to applescript yet). Check out the documentation for the ‘awake from nib’ handler for the order that launch-time handlers are called.

This is a very common task. Doing it this way will save you a lot of troubleshooting headaches and will make your code SOOO much clearer. Your fingertips will love you for it too. :smiley:

Thanks jobu! That’s awesome, I will try it out now.

Just for anybody else’s future reference, xCode 2.5 says this: