I'm new to Cocoa, and trying to figure out how to build custom widgets (views?). I'm particularly interested in something like Safari's URL bar that acts and looks like a text field, but has buttons, or at least clickable components, at either end.
I've been doing some research – mostly looking at Hillegass's book and Cocoa and Objective-C: Up and Running from O'Reilly – on custom views and trying to build things with NSCells. It's still all pretty fuzzy to me... Am I on the right track with this line of research?
Can anyone give a quick break down of what widgets the URL bar is probably made up of?
Can anyone give me an idea of how this is done or, failing that, point me to some resources that would be particularly good at explaining this kind of thing?
You're on the right track. You want to subclass NSView. From there you can either implement the -(void)drawRect method to do your own custom drawing, or you can add subviews, or both.
//add some subviews
NSTextField * textField = [[[NSTextField alloc] initWithFrame:self.bounds] autorelease];
[self addSubview:textField];
NSButton * button = ...
[self addSubview:button];
You can also use the Interface Builder (now built in to XCode 4) to design the widget, and then hook up the parts with code.
Related
What is the best way to customize the look of UIButtons in an app? I am working on an app where we programatically update UIButtons to look the way we want. Most of the buttons are created in Interface Builder and we want to execute our customizeButton-method on them in an efficient way. I only see two ways of achieving this:
Create the buttons in IB, assign them to an outlet and in code call customizeButton for each button.
Problem: way too much hazzle
Subclass UIButton, in the constructor initWithCoder: call customizeButton. Then use this class for buttons in IB.
Problem: allthough this works for buttons created in IB and requires less boilerplate code than point 1, I have read all the warnings against subclassing UIButton because of their class cluster.
Clearly there must be some better way, what is point 3?
Have a look at UIAppearance. It is available since iOS 5 and is especially for customizing the standard UI elements.
For ever class that is customizable is a class method called +appearance: and you can use it like this.
[[UIButton appearance] setBackgroundColor:[UIColor redColor]];
Every button will have now a red background color.
To get started have a look at Ray Wenderlichs Tutorial User Interface Customization in iOS 5 and/or session 114 - Customizing the Appearance of UIKit Controls of the WWDC11 videos.
How can I draw a simple BAR?
Like this:
Thank you.
You'll want to make a UIToolbar, put it at the bottom of your XIB, and put some buttons on it. Now, this will by default give you a regular black bar, with that standard black gloss. If you want to have a custom image, there are two solutions.
First is simply subclass UIToolbar and override its drawRect to draw a custom image. The code for overriding drawRect would look something like this:
//in some other code somewhere, preferably on init
self.image = [UIImage imageNamed: #"image"];
- (void)drawRect:(CGRect)rect {
[self.image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
Now, in iOS 5.0 there is something very useful called appearance but you have to be willing to only target 5.x+ iOS versions.
The second, which is simpler but less versatile, is to simply stick a UIImageView at the bottom of the XIB, and place some buttons on top of it. This is much easier than above, but is less extensible, is harder to modify, and is frowned upon in iOS for good reason - it breaks general iOS conventions (and you lose some very useful functionality).
Crimson has a nice idea but since his/her answer requires using UIToolbar, I'll offer an alternative:
Basically what you are showing there is some kind of progress bar, so ultimately whatever you want to display should be something subclassed from UIControl or even better, UIProgressView or maybe a UISlider.
Google around for custom progress view indicators or sliders. There are also good examples available here on Stackoverflow.
I have looked at the sample generated by xcode when creating a new UISplitView app on the iPad along with countless other tutorials and the documentation from the apple developer site. I have not seen an example where the UISplitView used was not the root of the application. Is this even possible?
What I am trying to accomplish: I have a UITableView to start out and once an item in the list is selected I would like to display a splitview with two different sets of information that is based on the item that was selected.
I curious if this type of implementation is even possible, or just frowned upon, and why. If it is possible, how would I go about implementing and hooking up a UISplitView to behave in this way?
Edit: I'm updating this with what I have. I can now switch to my UISplitView, though the transition is not animated. What is the way to correctly switch to a UISplitView so the transition is animated?
Code for switching right now:
[appDelegate.window addSubview:appDelegate.splitViewController.view];
appDelegate.window.rootViewController = appDelegate.splitViewController;
EDIT 2: In hopes of bumping this back up so more people see it, I have managed to switch from my navigationController to my splitViewController, but when I add the button to be able to navigate back, nothing I do makes a difference and I seem to be locked in. I tried reverse mirroring the code to switch to the splitViewController, but that had no affect, and I am completely out of ideas. Can anyone shed some light on this?
You should always use SplitViewController as a rootViewController: Split view controller must be root view controller
There may be some hacks around it, but when Apple have a strong recommendation and design guidance, I suggest to try to re-think your design before going against the platform -it should save you effort in the long term.
I recommend using the MGSplitViewController, it also works as a non-rootViewController, even nested into an another MGSplitViewController, and there's i.e. a one-liner for the animation to blend in the Master-View, if that is what you want.
In your UITableView didSelectRowAtIndexPath method you would have something like:
UISplitViewController *mySplitView = [[UISplitViewController alloc] init];
[self.navigationController pushViewController:mySplitView animated:YES];
[mySplitView release];
Probably you'll want to subclass UISplitViewController just like you would other view controllers and set in there the master and detail views and so on.
I'm just not getting this, I want to display a view in my app when a user clicks a button.
I've gotten this far:
NSView* unitMarker = [[NSView alloc] initWithFrame: NSMakeRect( 20.0, 20.0, 80.0, 50.0 ) ];
How would I display this view with a red background?
Thanks
#Aaron: he says he wants to do this programmatically. Also he's using NSView, not UIView, so an iPhone tutorial would be almost irrelevant.
#Mike: Your description of what you want to do is a bit vague. We have a bit of code creating, a view, but no context to tell us exactly what you are trying to do. Do you want the view to come up in a new window or the same? Do you want to replace a view that's already there? We don't even really know your skill level.
I suggest you go check out the documentation for NSView: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/Reference/NSView.html
I've gotten this far:
NSView* unitMarker = [[NSView alloc] initWithFrame: NSMakeRect( 20.0, 20.0, 80.0, 50.0 ) ];
How would I display this view with a red background?
You wouldn't, because a plain NSView doesn't draw anything.
First, you need to subclass NSView, implement drawRect: in that subclass to fill its bounds with red, and instantiate that subclass instead of NSView directly.
You should read the View Programming Guide.
How would I display this view …
You wouldn't. The view displays itself when it is appropriate to do so.
It won't ever be appropriate for it to draw itself until you add it to a view hierarchy. Every window has one, rooted at its content view. You need to add this view either to a content view or to some descendant view (subview, subview of a subview, etc.) of a content view.
You normally should not tell a view to display from your controller. That's the window's job. When you do change a property or properties of the view that affect what it draws, set the view as needing display, and let the window tell the view to display when it's appropriate to do that.
Say this out loud 10 times repeatedly:
Interface Builder is my friend.
I was able to put together a sample project doing exactly what you want to do in about 5 minutes by leveraging Interface Builder as part of the process. http://www.markdouma.com/developer/ShowWindowWithRedView.zip.
You can't show a view without placing it in a window first; by far the easiest way to do this kind of a thing is to drag out a second window in Interface Builder, set it to not be visible on launch, drag a generic NSView custom view onto the Window, set its class to be SRRedView (your red view subclass). As d11wtq posted, you override NSView's primitive drawing method like he shows (though personally I prefer NSBezierPath :-P).
In your controller class, you define IBOutlets, and then hook those up in Interface Builder. These provide you with a way to reference the important parts of your interface so that you can manipulate them programmatically.
I added one IBAction method, which the button in the main window is hooked up to call. That method simply tells the second window to show itself.
- (IBAction)showWindowWithRedView:(id)sender {
[windowWithRedView makeKeyAndOrderFront:nil];
}
In the 8 years or more that I've been doing Cocoa programming, I don't think I've ever needed to resort to manually creating windows and views. It has always been much faster to simply load another nib file that contains the windows or views I need to display. Using Interface Builder along with Xcode to create your app is quite a bit different than how other IDEs work. (Specifically, when you arrange stuff in Interface Builder you're not generating code as much as you are creating instances of UI objects and then "freeze-drying" them in their current arrangement into a .nib archive file. When you launch the app, they are brought back to life. Or at least that's the way I think about it).
You need to subclass NSView and implement drawRect:.
In this case you're just going to fill the entire rect with red.
#interface MyView : NSView {
}
#end
#implementation MyView
-(void)drawRect:(NSRect)dirtyRect {
[[NSColor redColor] set];
NSRectFill(dirtyRect);
}
#end
That's an extremely basic example that does what you want, but if you want to do more complex things (adding borders (strokes) or drawing curves (paths) etc), then you need to learn all the drawing classes available to you.
Scott Stevenson has written some easy to follow tutorials on this:
http://cocoadevcentral.com/d/intro_to_quartz/ and;
http://cocoadevcentral.com/d/intro_to_quartz_two/
It seems like more and more OS X apps these days are doing all kinds of fancy drawing stuff for custom controls. Apps like Twitterific, Things, EventBox, Versions just to name a few....
So basically I'm looking for any information on how to get started doing this kind of thing. Not sure if it is just done by subclassing controls and using custom drawing or if it is something entirely different.
Any help is greatly appreciated. THanks!
It depends entirely on what you want to do.
The "Show Raw Properties" button in Versions for instance is an NSButton subclass, because basically what we needed is standard button behavior with our own look. One way to subclass a button is to simply implement your own -drawRect:(NSRect)rect method in the NSButton subclass, but we decided to stick with the way NSButton is implemented in Cocoa, meaning most drawing is done by the button's cell, so the implementation looks like this:
In the NSButton subclass:
+ (Class) cellClass
{
return [OurButtonCell class];
}
- (void)drawRect:(NSRect)rect
{
// first get the cell to draw inside our bounds
// then draw a focus ring if that's appropriate
}
In the NSButtonCell subclass (OurButtonCell):
- (void)drawInteriorWithFrame: (NSRect) rect inView: (NSView *) controlView
{
// a bunch of drawing code
}
The Timeline view in Versions is actually a WebView, the page that you see in it uses javascript to collapse headers you click on.
The rule of thumb I use for where to start out with a custom control is:
To customize the look of a standard Cocoa control:
subclass the appropriate control (like e.g. NSButton and NSButtonCell)
stick as close as makes sense to the way the default control is implemented (e.g. in a buttoncell, start from the existing attributedTitle instance method to draw the button title, unless you always want to draw with the same attributes regardless of what's set up in IB or if you need to draw with different attributes based on state, such as with the trial expiration button in Versions' main window)
Creating an entirely new UI element:
subclass NSView and implement pretty much all mouse and key event handling (within the view, no need to redo "hitTest:") and drawing code yourself.
To present something that's complex, of arbitrary height, but isn't a table:
See if you can do it in HTML, CSS and JS and present it in a WebView. The web is great at laying out text, so if you can offload that responsibility to your WebView, that can be a huge savings in pain in the neck.
Recommended reading on learning how to draw stuff in your own custom view's drawing methods: Cocoa Drawing Guide
Customizing the look of for instance an NSTableView is an entirely other cup of tea, thanks to the complexity of a tableview, that can happen all over the place. You'll be implementing your own custom cells for some things you want to do in a table, but will have to change the way rows are highlighted in a subclass of the actual NSTableView object itself. See for instance the source code for iTableView on Matt Gemmell's site for a clear example of where to draw what.
Finally, I think Abizer's suggestion to go check out the code of BWToolkit is a great idea. It might be a bit overwhelming at first, but if you can read and understand that code you'll have no trouble implementing your own custom views and controls.
Have a look at some excellent example code: BWToolkit