Cocoa Diversions; More on Views
Pages: 1, 2, 3
In Project Builder
Now this is where the fun really starts! In Project Builder let's start out with the awakeFromNib method in Controller. Since we didn’t put an interface in the main window in Interface Builder, we want to set one of the views to be the initial content view of the window at start up. A content view in a window is the view that contains the window’s contents, much the same as the content view of a drawer. To set a content view for a window we use NSWindow’s -setContentView: method (fancy that), with the view we want to set as the content view supplied as the argument. We also have to resize the window to fit the view we’re setting as the content view, otherwise the window will be sized as it was in Interface Builder.
Another thing we want to do is obtain and store in instance variables the sizes of the frame rectangles of the two views we’re working with. To support this add the instance variables in Controller.h, shown below:
NSSize smallSize, largeSize;
Finally, we’re going to create a view that is blank with no controls. This will be used as an intermediate view to display while the window is resizing. Add the instance variable NSView *blankView to Controller.h as well. Controller.h should now look like the following:
@interface Controller : NSObject
{
IBOutlet NSView *largeView;
IBOutlet NSWindow *mainWindow;
IBOutlet NSView *smallView;
NSSize smallSize, largeSize;
NSView *blankView;
}
- (IBAction)goLarge:(id)sender;
- (IBAction)goSmall:(id)sender;
@end
Now let's take a look at –awakeFromNib in Controller.m :
- (void)awakeFromNib
{
smallSize = [smallView frame].size;
largeSize = [largeView frame].size;
[mainWindow setContentSize:smallSize];
[mainWindow setContentView:smallView];
blankView = [[NSView alloc] init];
}
In the first two lines we use the NSView method--a frame to return the receiver’s frame rectangle--then using the structure member operator, we obtain the size member of the NSRect and assign it to the appropriate variable. This is done for both the small and large views. In the next two lines we set the window’s content size to smallSize, and we set the contentView of mainWindow to be smallView. Finally, before leaving awakeFromNib we create and initialize an instance of NSView and assign it to blankView. With these preliminaries out of the way lets get into the meat of this class.
We’ll now create a method called – resizeWindowToSize:. The argument of this method is an NSSize datatype-—the size that we want to make the new window. Let's dive right into this method and then we’ll go through it piece by piece.
- (void)resizeWindowToSize:(NSSize)newSize
{
NSRect aFrame;
float newHeight = newSize.height;
float newWidth = newSize.width;
aFrame = [NSWindow contentRectForFrameRect:[mainWindow frame]
styleMask:[mainWindow styleMask]];
aFrame.origin.y += aFrame.size.height;
aFrame.origin.y -= newHeight;
aFrame.size.height = newHeight;
aFrame.size.width = newWidth;
aFrame = [NSWindow frameRectForContentRect:aFrame
styleMask:[mainWindow styleMask]];
[mainWindow setFrame:aFrame display:YES animate:YES];
}
The first thing we do after declaring a local NSRect variable to work with is to set the variables newHeight and newWidth to the values of the height and width members of the newSize argument. These values will be used in calculating the new dimensions and location of the window’s frame.
The next line is an important piece of code. What is does, in effect, is return the NSRect for the frame of the window’s current content view. Why don’t we just work with the rect returned by [mainWindow frame]? Because -frame returns the NSRect that defines the entire window-—we want to neglect the title bar and any other "style" elements of the window. We invoke in NSWindow the class method +contentRectForFrameRect:styleMask:. The NSWindow class object takes the frame of mainWindow as the first argument, and then the style mask of mainWindow. The style mask tells us what style elements the window uses, such as a title bar. We don’t have to worry about the particulars of style masks here—we just ask mainWindow what its style mask is using –styleMask and pass that along to +contentRect….In the image below –frame returns the rectangle drawn in green, and +contentRect… takes that rectangle and returns the rectangle drawn in red.

What the offsets mean for a drawer attached to the left or right of the parent window.
In the next four lines we do some clever math on the dimensions of the frame returned by +contentRectForFrameRect:styleMask: to resize it to newWidth and newSize. The purpose of this is to ensure that the upper-left corner of the window doesn’t change when we resize the window.
|
Also in Programming with Cocoa |
The next method, +frameRectForContentRect:styleMask: does the exact opposite of what +contentRectForFrameRect:styleMask: did. That is, it takes a content rectangle (red rect in the image above), and calculates the frame for a window (green rect above) by taking into account the style mask for the particular window.
Finally, we make the magic happen in the last line of code with - setFrame:display:animate:. This method takes aFrame—-the frame we want to resize the window to-—as the first argument and two booleans as the last two arguments. The display: argument tells the window whether it should redraw at each step of the animation, and animate: tells the receiver whether the resize operation should be animated.
So why all the hubbub just to set the frame of a window? Couldn’t we just have passed the frame of the desired view to setFrame:…? The reason is that frames, as NSRects, are more than sizes; they specify a location on the screen as well. Thus, blindly passing a the frame of smallView or largeView to this method would do more than resize the window; the window would fly across the screen as well, which is definitely what we don’t want. We want to maintain the position of the top-left corner of the window.
Now, let's get to those two long-forgotten action methods, goSmall: and goLarge:. With the creation of -resizeWindowToSize:, implementing these two methods will not be difficult. The implementations for these two methods are the same, except for using two different views. The following is the code for these two methods:
- (IBAction)goLarge:(id)sender
{
[mainWindow setContentView:blankView];
[self resizeWindowToSize:largeSize];
[mainWindow setContentView:largeView];
}
- (IBAction)goSmall:(id)sender
{
[mainWindow setContentView:blankView];
[self resizeWindowToSize:smallSize];
[mainWindow setContentView:smallView];
}
Notice that the first thing we do in each of these methods is to set the content view of mainWindow to blankView. The reason we do this is to make the resize transition a little easier on the eyes. If we didn’t do this we would see the old content view stay where it started while the resize happens around it.
System Preferences does something similar by having a transition view show the icon of the preference pane and a message indicating that the pane is loading. After you get things working, try commenting out these lines to see what happens. With blankView set as the contentView of mainWindow, we now invoke our –resize… method with the desired NSSize instance variable passed along as the argument. Finally, we set the content view of the window to the desired view.
So that’s the story. Give it a go and see how it works. With the drawer open you will see the effects of those offset variables-—the drawer resizes along with the window. I hope you’ve enjoyed learning about these two Aqua enhancements. Next time we're going to look into some more new stuff from Aqua as we learn about toolbars and how to add one to your application. The project folder can be downloaded here. I hope you’ve enjoyed learning about these two Aqua enhancements.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 15 of 15.
-
Project File?
2003-07-28 16:20:13 anonymous2 [Reply | View]
-
Project File?
2003-08-09 23:21:46 anonymous2 [Reply | View]
What you should do is go back to some of the first articles. They show you how to make connections in Project Builder. It's really as simple as drawing the connection. Go to the article about "Giving Your Text Editor Legs". Mike shows you how to make connections there, in a far better way than I could explain in this short area.
-
animating windows triggered from AppleScript?
2003-07-27 07:04:01 anonymous2 [Reply | View]
hi there,
nice article! i ever wondered if Apple already provided us a function that does these nice animated window resizes.
how about using that from inside AppleScript? is there a way to trigger this function using an AppleScript command? how would that work?
any help would be greatly appreciated.
thanks in advance!
-
setting preferred edge
2003-06-24 00:17:50 anonymous2 [Reply | View]
Hello. I wrote a sample program that changes the preferred edge of a drawer. Initially, the preferred one is the bottom, set with the IB.
When I press a button, it opens a drawer and rotate the edge clockwise by setting the preferred edge.
However the setPreferred doesn't seem to work.
Actually it changes the value, however the open method doesn't reflect the change.
What should I do?
-
Multiple Drawers??
2003-05-10 20:46:39 anonymous2 [Reply | View]
I'm making my first program.... which uses three drawers and three buttons, all of which are set to toggle (open/close).
Say, I click on a button, I want it to close any drawer that might be open before opening it's own drawer. How do I go about making this possible.
Thanks
Kuzey
-
Toolbar
2003-05-01 03:17:20 anonymous2 [Reply | View]
Really great article, but ... I have a app with tollbar and I have to left free space in the custom views for toolbar and this space is visble when I customize toolbar (choose "Text only". If I do not have any free space the content part of the custom view is placed "under" the toolbar. How do I avoid it ?
-
Am I having a stroke?
2002-02-20 16:51:42 bhittle [Reply | View]
Every time I click on the page 2 link I get the page 1 content. Is it just me? -
Never mind...
2002-02-20 16:55:16 bhittle [Reply | View]
It seems to be working all of a sudden. -
somehow this reminds me...
2002-02-21 23:11:43 psheldon [Reply | View]
I had a boss when I programmed at an aerospace firm. He'd look at me somberly and ask "Is you program working yet?"
I'd say "no".
He'd say, "Well then, I guess you must be working."
He was just joking but, that day, I had a lousy sense of humor. Now what he said seems very funny, the paradox of programming employment. At the time I thought badly of the joke and feel sort of sad I didn't just smile when he said that. Had the blues today, maybe I was supposed to remember that boss. OK. Maybe I feel better now.
-
windows don't fly ?
2002-02-18 23:47:22 psheldon [Reply | View]
I commented out some of the lines of "the clever math", the two doing origin resetting, and the windows didn't fly.
I believe I recognize what is happening with and without comments, quite different from what Mike said.
With the two lines the origin expanded about was upper left, for a os 9.x look and feel,
while,
with the two lines commented out, the origin expanded about was lower left, for a Next Step window look and feel.
That's what I thought I saw, but, in the context of that apparent observation, I don't understand the two lines of code and need help to understand . -
preliminary comments on 2 origin lines
2002-02-19 22:50:55 psheldon [Reply | View]
The first line reflects a shift from local next step coordinates to global coordinates on the screen, while the second shifts back to local coordinates referenced to the top edge of the new window size so it expands about that new origin. Mike, thanks for the confidence you gave me in my observation that the mathematically clever code was choosing what corner to hold fixed on the window. I got my first bit of confidence to try to thrash out the commenting of the two lines of origin code.
What I just wrote isn't clear to me, I sort of know it is true and I bet Carolyn Rose of Inside Mac fame could write it a whole lot better. Or maybe some of you guys reading this could take the pass and run with the ball and write it better.
We're here to help each other, right?
;-) -
no Carolyn Rose with her global and local
2002-02-20 09:18:43 psheldon [Reply | View]
Agenda :
***** 1. -----Some stories----- to smooth the notion of students writing comment statements in general (off the syllabus); why this homework should be respected by all as work and how noone can do your homework for you with same effect. I tried to muster up my courage for this stage with a phone call to my relativity professor and formal thesis advisor. He was busy preparing his comments or notes for a class...
;-)
***** 2. What might still be, after this introduction, the -----sordid details----- behind two lines of origin code. I hope they would not be considered sordid details and I hope that none might feel humiliated that they hadn't done it themselves, because that shouldn't be the purpose of teaching, to humiliate. The general association of humiliation by a brilliant teacher stuck up on himself made Wheeler not choose Oppenheimer for a mentor...
1. Some stories:
***** A programmer has an intuition from a lot of trial and error that can get him things that work. Once things work, he might take the whole history of his experience (including windows flying). He may merely note that once he was blind and now he has vision, as did Mike.
***** It is hard to revisit that history (of trauma), but that is where "the fundamental" lies; that is what must constantly be revisited to do great stuff. We do that everytime we read a column and get through some confusion in it.
***** I find great difficulty in composing the beginnings of algebraic statements that solve problems. After much time, everything is simple in hindsight, embarrassingly so. But, the trick was that it was me that made it embarrassingly simple, so I can't blame myself.
***** Many people probably get blocked going through the confusion stage of creativity because someone who knows the answer undercuts them. They associate their confusion, not with personal victory (as they should) but with extrapersonal humiliation. For example, many of us might be forced to write comment statements explaining our code to bosses who think they can purchase organization with money rather than blood sweat and tears. Their higher salary ceiling seems to support this hypothesis of theirs. They haven't read Starship Troopers course in moral philosophy, nor have their girlfriends. Oh well.
***** I had a professor, Elliezer Dekel, who, in a Yiddish accent, would read us computer listings. I'd be dying to break in with a comment or question, but somehow that was perceived as wasting everyone's time or flowing against the syllabus.
***** The textbook on data structures would also have computer listings. The publishers would invariable paginate in a most irritating manner. They would have the explanation always on the next page. I'd feel guilty turning a page before understanding what was on that page. Finally, when I couldn't stand trying to understand the listing anymore, I'd turn the page.
***** So, here is a staged introduction to writing comments to two lines of code. It felt a bit like pulling my own teeth, but I know comment statements amplify my power to think later faster. It is the way to keep or own my vision that transcends the blindness. It is the notes written upon the notes from the teacher.
2. The sordid details :
***** The window origin in a next step os x machine is, not upper left (as in an os 9.x), but rather lower left. This is the origin that is fixed in an os x window size change.
***** We wish, instead, to fix the upper left origin to make behaviors look familiar.
***** We could say, give this upper left corner a name, "os 9.x origin" or better os9xo.
***** Now, we also have an os x origin, osxo = aframe.origin, the one that would be fixed if we didn't add the two lines of origin code.
***** Let's write two equations for the two origins at two different times before and after the window size change. Only the y coordinates are different, so that means we, in effect, have, not two systems of equations of two, two equations. Surpress the y for momentary economy of writing.
***** Call the times before and after, tb4 and ta.
***** 1. os9xo(tb4)=osxo(tb4)+height(tb4)
***** 2. os9xo(ta)=osxo(ta)+height(ta)
***** Now, how must the os x origin move to keep the other origin fixed?
***** 3. os9xo(tb4)=os9xo(ta)
***** Eliminate left and right hand sides with 1 and 2 :
***** osxo(tb4)+height(tb4)=osxo(ta)+height(ta)
***** Solve for the new os x origin :
***** osxo(ta)=osxo(tb4)+height(tb4)-height(ta)
***** We have finished generating the commentary once external reference is made to c's syntax abbreviating change to a variable.
-
Resizing the window
2002-02-17 14:46:07 michele [Reply | View]
All works fine when the window is created without the resize flag set.
But, when the resize flag is set, and I try to resize the window to the smallest size, there is a difference (12 pixels) between the size of the window obtained with goSmall and the size of the window obtained by dragging the bottom right corner.
It seems that the goSmall method overrides the resizing mechanism, not taking into account the 12 pixels right border needed to draw the resize flag.
At the moment, I've not found a solution.
Michele -
not too smart an answer (bunch of questions)
2002-02-22 07:46:43 psheldon [Reply | View]
****** I'm not looking at the program now and maybe my being blind will help you see.
"All works fine when the window is created without the resize flag set."
***** Am I correct that the resize flag is a bit about the window type that tells whether you can resize the window by dragging the bottom corner and also makes the green button appear which acts in concert. I have some intuitive feel that The resize box in os 9.x gets information from the drags on the lower right hand corner, not the interface standard, but rather some particular smarts in particular programs.
"... I try to resize the window to the smallest size, there is a difference (12 pixels) between the size of the window obtained with goSmall and the size of the window obtained by dragging the bottom right corner. "
***** The smallest and largest size with the go methods was from the views. There is also a smallest and largest size from inspector that can be slaved to the view, I believe, by the springs. I recall inside and outside springs, but don't recall whether that was in the windows or the views. Those are bits that can be fiddled with and the column suggested some experimentation with the words like "set springs as appropriate".
"It seems that the goSmall method overrides the resizing mechanism, not taking into account the 12 pixels right border needed to draw the resize flag."
***** Now that I have tried to actively respond to each of your paragraphs, I finally understand what you are saying. That phenomenon in my brain is very interesting! Could you put conditional code (say in an if statement) in that notes the size of the resize flag (which I suppose is the way things are written : you identify the bit with the drag place that appears or does not appear depending on whether that bit is set). I had a similar experience where a window dissappeared because the size of an mp3 window size was zero and then I put in a condition don't resize it to that window size. What is going on when we both thought we were doing something wrong was we thought something should have been afforded by an automatic mechanism of Apple, a confusion of responsiblity whose distinction (ours from Apple's) requires a much deeper knowledge than we are ready for. Sort of exhilirating when you pull out of the "power dive" though.
***** I had a brilliant supervisor at Rockwell Space Division and when I came to him with problems he'd catch my discussion at critical places and simply say "Is that so?" Fritz Perls said that sometimes our flustered feelings of being confused disguise intelligence; maybe that has to do with the confusion of responsibility giving a fuzzy pointer to much deeper things.
***** Well, that's as good as it gets. Maybe now, I've given you some company and you will get that obvous hindsight and thank me for keeping you company as you had done me in my rebriefed experience above.
-
not too smart an answer (bunch of questions)
2002-02-27 11:51:33 gmeece [Reply | View]
sheldon -
So where at the Rockwell space division did you work - Downey, Seal Beach...? I was at the Seal Beach facility (GPS program) and got one of the first Macs there (1985).
You can reply to:
<gmeece@bungo.com>






i just started developing on the Mac and got some problems in the connection section of the resize tutorial (connect buttons to actions goSmall/goLarge - how?). could someone please provide us the final project file?
thanks in advance!