Yesterday we talked about how to make nifty selectable toolbars like this:
Now let's look at the finishing touch for our Preferences window; Animating the panel changes. We'll be flying through this at a pretty good clip, but don't worry. I'll provide the full source for your inspection.
First off, let's add some new outlets to our window controller:
ib_outlets :generalPrefsView,
:advancedPrefsView
Now in Interface Builder, we'll create the views for each preference pane by dragging Custom Views from the Library onto our Preferences.nib.
Note: Be sure to drop the Custom Views on the main nib window in IB, not on the Preferences NSWindow
. Your project (in IB) should look something like this:
Hook up the outlets to the new views, and edit your preference panels to your heart's desire. From here, we go back to the code.
Tip: Be sure to set the auto-sizing on your preference panels (the NSView
s) so that it matches the NSWindow
's contentView
.
Next up are some helper methods for our window controller. I won't spend too much time explaining these, but they're pretty straight forward.
case tag
when 0: [@generalPrefsView, "General"]
when 1: [@advancedPrefsView, "Advanced"]
end
end
#viewForTag
actually returns our NSView
and a title string.
= window.frameRectForContentRect(view.frame)
oldFrameRect = window.frame
newSize = newFrameRect.size
oldSize = oldFrameRect.size
frame = window.frame
frame.size = newSize
frame.origin.y = frame.origin.y - (newSize.height - oldSize.height)
frame
end
newFrameRect
#newFrameForNewContentView
calculates the new frame rectangle for the window based on the new view (preference pane).
Now we're ready to fill out our selectPrefPanel
action:
ib_action :selectPrefPanel do |sender|
tag = sender.tag
view, title = self.viewForTag(tag)
previousView, prevTitle = self.viewForTag(@currentViewTag)
@currentViewTag = tag
newFrame = self.newFrameForNewContentView(view)
window.title = " Preferences"
# Using an animation grouping because we may be changing the duration
NSAnimationContext.beginGrouping
# Call the animator instead of the view / window directly
window.contentView.animator.replaceSubview_with(previousView, view)
window.animator.setFrame_display newFrame, true
NSAnimationContext.endGrouping
end
Right on! Now we setup the initial pane when the window loads:
.setContentSize @generalPrefsView.frame.size
window.contentView.addSubview @generalPrefsView
window.title = "General Preferences"
@currentViewTag = 0
# Will use CoreAnimation for the panel changes:
window.contentView.wantsLayer = true
end
window
That pretty much does it. Now you have a professional looking preferences window. So enough of those dang blasted NSTabView
s!
Here's the completed PreferencesController.rb. Or, you can download the full Xcode project. (Requires Leopard, Xcode 3, and Interface Builder 3)
Happy coding!
Hi Matt,
ReplyDeleteThanks for a great tutorial. Have you noticed that if you have an NSPopUpButton in one of the views then it renders really badly? Is there something that can be fixed in the code to stop that happening?
Thanks,
Jon
[Jonathan Dann:1] Renders badly? Sorry, I’m not quite sure what you mean… When it’s static, or as it’s moving between panels?
ReplyDeletewould be nice to have it translated to obj-c :)
ReplyDeleteMy attempt:
ReplyDeletehttp://pastie.org/283115