In my last blog post I did a tutorial on building a SearchView using UISearchBar and UITableView. This time around I’m going to talk about mixing together a UINavigationController, UITabBarController and UISearchBar together to see what we get. For this post I’m starting out with a brand new project and doing everything from there, but it should be trivial to do this in the same project as last time (that’s actually what I did the first time around). As always, if all you care about is the code be my guest, but be warned, we’re doing a lot more in Interface Builder this time.
Background
Okay, so what exactly are we going to build this time? We’re going to start with a Tab Bar application which is going to have a UINavigationController as one of its View Controllers. Once we get this all set up we’re going to add a UISearchBar as the navigationItem of the initial view.
When we’re all done we’ll have something like this:


So we have the UISearchBar in the navigationItem and when we push another View Controller onto the stack it slides over nicely to give you the back button.
Setting up the Project
As I said earlier I am starting with a Tab Bar application but you should be able to use any starting point for the application you would like. I wont go into the details of creating the initial project but once you’re ready to go and have a UITabBarController in there (the Tab Bar application gives one to you) open up the Main Window Nib and take a look at it.

This is pretty straight forward, we have the UITabBarController and it has a couple of View Controllers that it switches between. If you run the app right now this is what you would see:

Simple. Lets add the UINavigationController
Adding the Navigation Controller to the Mix
Okay, lets switch back to Interface Builder and drop the UINavigationController into the Nib inside of the UITabBarController. Here’s what we’re looking at after that:

Notice how it’s inside the UITabBarController and another button was added to the Tab Bar? Next we want to add another View for the UINavigationController to display. I just created a new UITableViewController (Xcode File → New File) from Xcode and added it to the project.

With that added we switch back to Interface Builder and change the class for the UIViewController that is inside the UINavigationController.

All we had to do is select the current View Controller in the Nib and then in the Identity Inspector select our new Class in the Class drop down.
Now when we run the project we see our Table View show up along with the Navigation Bar.

Okay, we’re just about done. Next we’re going to add that Search Bar to the Navigation Bar.
Pushing the Search Bar into the Navigation Bar
Alright, time to actually get in and add some code! Lets get into Xcode and open up that Table View Controller we created and add a few lines to the viewDidLoad method.
// TableViewController.m
#import "TableViewController.h"
@implementation TableViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Creating the UISearchBar and adding it to the
// UINavigationController
UISearchBar *theSearchBar = [[UISearchBar alloc]
initWithFrame:CGRectMake(0.0f,0.0f,320.0f,0.0f)];
[theSearchBar sizeToFit];
self.navigationItem.titleView = theSearchBar;
self.navigationItem.titleView.autoresizingMask =
UIViewAutoresizingFlexibleWidth;
[theSearchBar release];
}
@end
The main thing to take note of here is that we’re adding the Search Bar to the navigationItem.titleView and then setting the autoresizingMask on it to be flexible. This might seem like a bit of a hack, and it very well might be, but it does the job. I also called sizeToFit on my SearchBar so that it would expand to fit the whole bar. Let’s take a look.

There we have it! Now when you select an item from the Table View and push another View Controller onto the stack the Search Bar slides out of the way for the back Bar Item. If you take a look at the full code below you’ll see how to do this.
Something to note, if you want to change the color of the Navigation Bar you will also want to set the tint on the Search Bar so it fits in.
theSearchBar.tintColor = [UIColor colorWithRed:0.0f green:0.0f
blue:0.0f alpha:0.5f];
This will set the tint to be black, very much like the ‘Black Opaque’ style that can be set on may UI elements.
Conclusion
There we go, we have a Tab Bar with a Navigation Bar for one of the items and we’ve pushed a Search Bar into the Navigation Bar. I’ve included the code below along with some of the generated methods for the Table View Controller that I hooked up. If you have any suggestions or feedback please leave a comment!
//
// TableViewController.m
#import "TableViewController.h"
#import "FirstViewController.h"
@implementation TableViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Creating the UISearchBar and adding it to the UINavigationController
UISearchBar *theSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f,0.0f,320.0f,0.0f)];
[theSearchBar sizeToFit];
self.navigationItem.titleView = theSearchBar;
self.navigationItem.titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[theSearchBar release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = @"first cell";
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
FirstViewController *anotherViewController = [[FirstViewController alloc] initWithNibName:@"SecondView" bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
[anotherViewController release];
}
- (void)dealloc {
[super dealloc];
}
@end
March 09, 2010 | View Comments
|
programming,
iphone,
objective-c,
uinavigationcontroller,
uitabbarcontroller,
uisearchbar
Background
I’ve been working away on some iPhone programming lately and one of this first things I wanted to add to my app was a view for searching. What I wanted was to have a view that loads with the curser in the UISearchBar ready to go and to only load the results when the user presses the ‘Search’ button. The rest of this blog post will go through putting all this together. At the end you will have a Search View very similar to the one in the Flixster App and hopefully the knowhow to customize it further. If you would rather skip the step by step and jump right to the code be my guest.
Search View Requirements
- On load have the curser in the UISearchBar ready for user input.
- Search is only done once the ‘Search’ button is pressed.
- When entering the query any previous results should not be clickable.
Here’s a look at what we’re going to end up with:

As a side note, if you’re just looking for a simple type-ahead style search (kind of like what is used in the App Store or Contacts App) you might want to take a look at the Search Bar and Search Display Controller that can be added from Interface Builder. I found it super easy to use for the simple case just described, but very difficult to customize for anything else.
Getting Started
I’ll leave it up to you to do the basic setup for your app, I started with a Tab Bar application for mine but you can use whatever you like. I then created a Nib for the Search View and a corresponding SearchViewController.h/m that extends from UIViewController (more on the code for these files later on). With that done we jump into Interface builder to drop our UISearchBar and UITableView into our Nib. It should look something like what I have here:

Next up we want to add some IBOutlets in our SearchViewController.h so that we can refer to the UI elements we just added from our code. I am going to make these properties as per the Memory Management Guidelines from Apple. I’m also going to define a NSMutableArray to hold the data to display in the UITableView.
//
// SearchViewController.h
//
#import <UIKit/UIKit.h>
@interface SearchViewController : UIViewController{
NSMutableArray *tableData;
UITableView *theTableView;
UISearchBar *theSearchBar;
}
@property(retain) NSMutableArray *tableData;
@property (nonatomic, retain) IBOutlet UITableView *theTableView;
@property (nonatomic, retain) IBOutlet UISearchBar *theSearchBar;
@end
If you didn’t get XCode to generate this file for you make sure you include the import for UIKit.h.
Now we will quickly jump back to Interface Builder and hook those IBOutlets up before we forget. You’ll also want to make the UISearchBar and UITableView delegate to the SearchViewController so that we can handle the callbacks there.
With the Nib all set up lets make sure we synthesize those properties in SearchViewController.m and release the variables in our dealloc method.
//
// SearchViewController.m
//
#import "SearchViewController.h"
@implementation SearchViewController
@synthesize tableData;
@synthesize theSearchBar;
@synthesize theTableView;
- (void)viewDidLoad {
[super viewDidLoad];
self.tableData =[[NSMutableArray alloc]init];
}
- (void)viewDidAppear:(BOOL)animated {
[self.theSearchBar becomeFirstResponder];
[super viewDidAppear:animated];
}
- (void)dealloc {
[theTableView release], theTableView = nil;
[theSearchBar release], theSearchBar = nil;
[tableData dealloc];
[super dealloc];
}
@end
I also snuck a little bit of code into viewDidLoad and ViewDidAppear; the former just initializing the tableData NSMutableArray and the later making our UISearchBar first responder. Making it the first responder causes the UISearchBar to get focus as soon as the view loads so that the user can start entering their query right away.
That’s a pretty good start, lets make sure it builds and see what we get when we run our app before going on.

Implementing the Protocols
We made the SearchViewController the delegate for both the UISearchBar and the UITableView so are going to implement the UISearchBarDelegate and UITableViewDataSource Protocols. First we need to add the Protocols to the SearchViewController.h header file.
//
// SearchViewController.h
//
#import <UIKit/UIKit.h>
@interface SearchViewController : UIViewController
<UISearchBarDelegate, UITableViewDataSource> {
// Instance variables defined earlier
}
// Properties defined earlier
@end
Now we implement the methods in SearchViewController.m. There are a number of callback methods defined by the UISearchBarDelegate Protocol but we’re only interested in three: searchBarTextDidBeginEditing, searchBarCancelButtonClicked and searchBarSearchButtonClicked.
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
[searchBar setShowsCancelButton:YES animated:YES];
self.theTableView.allowsSelection = NO;
self.theTableView.scrollEnabled = NO;
}
This method is called whenever the UISearchBar gets focus. When this happens we want to show the ‘Cancel’ button (animated:YES makes it slide in nicely) and disable the UITableView.
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
searchBar.text=@"";
[searchBar setShowsCancelButton:NO animated:YES];
[searchBar resignFirstResponder];
self.theTableView.allowsSelection = YES;
self.theTableView.scrollEnabled = YES;
}
When the ‘Cancel’ button is pressed we want to clear the UISearchBar text and hide the button. We also want the UISearchBar to resignFirstResponder status so that the keyboard hides and then enable the UITableView.
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// You'll probably want to do this on another thread
NSArray *results = [SomeService doSearch:searchBar.text];
[searchBar setShowsCancelButton:NO animated:YES];
[searchBar resignFirstResponder];
self.theTableView.allowsSelection = YES;
self.theTableView.scrollEnabled = YES;
[self.tableData removeAllObjects];
[self.tableData addObjectsFromArray:results];
[self.theTableView reloadData];
}
The last UISearchBarDelegate Protocol method is the callback for the ‘Search’ button being pressed. This is where we actually perform the search and add the results to the UITableView. The rest of this method implementation is the same as when the ‘Cancel’ button is pressed.
Finally we need to implement the UITableViewDataSource Protocol methods. I’m not going to go into a whole lot of detail here since there’s lots of info out there on implementing a UITableView.
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = @"SearchResult";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
id *data = [self.tableData objectAtIndex:indexPath.row];
cell.textLabel.text = data.name;
return cell;
}
We’ve done lots of work now and it’s probably a good idea to make sure everything still builds. You should see something like this when you run the app:

At this point we’ve hit all three marks in the requirements I outlined earlier and we could call it quits. We could, but I think we could do a little bit more to help make the UITableView actually seem like it’s disabled when you go to search.
Fading out the UITableView
If you take another look at the Flixster App or even the initial screenshot I have in this post you will notice that when you are in ‘search mode’ the background of the screen is faded black. This effect helps to show the user that the UITableView is in fact disabled.
To get this behaviour I add and remove another UIView and adjust the opacity in an Animation so that in fades in smoothly. The first thing we need to do is create an instance variable and property for this disableViewOverlay as I called it and initialize it when the view loads.
#import "SearchViewController.h"
@implementation SearchViewController
@synthesize disableViewOverlay;
// Additional variable synthesizes
- (void)viewDidLoad {
[super viewDidLoad];
self.tableData =[[NSMutableArray alloc]init];
self.disableViewOverlay = [[UIView alloc]
initWithFrame:CGRectMake(0.0f,44.0f,320.0f,416.0f)];
self.disableViewOverlay.backgroundColor=[UIColor blackColor];
self.disableViewOverlay.alpha = 0;
}
I’ll leave it to you to figure out what to put in SearchViewController.m. Now that we have this UIView all set up all we have to do is fade it in when the UISearchBar gets focus. This was actually really easy to do, I just needed to add the disableViewOverlay as a subview and then use beginAnimations/commitAnimations for the fading.
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
// Existing code
// Fading in the disableViewOverlay
self.disableViewOverlay.alpha = 0;
[self.view addSubview:self.disableViewOverlay];
[UIView beginAnimations:@"FadeIn" context:nil];
[UIView setAnimationDuration:0.5];
self.disableViewOverlay.alpha = 0.6;
[UIView commitAnimations];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
// Existing code
// Removing the disableViewOverlay
[disableViewOverlay removeFromSuperview];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// Existing code
// Removing the disableViewOverlay
[disableViewOverlay removeFromSuperview];
}
At the end of searchBarTextDidBeginEditing we added the code to fade in the disableViewOverlay, then at the end of searchBarCancelButtonClicked and searchBarSearchButtonClicked we remove the disableViewOverlay.
If you’ve been following along you might notice a bit of a code smell coming from these last three methods. There’s a lot of code duplication which I don’t really like so I actually re-factored these three methods and created another method that all of them can call. The resulting re-factored code looks like this:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
[self searchBar:searchBar activate:YES];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
searchBar.text=@"";
[self searchBar:searchBar activate:NO];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
NSArray *results = [SomeService doSearch:searchBar.text];
[self searchBar:searchBar activate:NO];
[self.tableData removeAllObjects];
[self.tableData addObjectsFromArray:results];
[self.theTableView reloadData];
}
- (void)searchBar:(UISearchBar *)searchBar activate:(BOOL) active{
self.theTableView.allowsSelection = !active;
self.theTableView.scrollEnabled = !active;
if (!active) {
[disableViewOverlay removeFromSuperview];
[searchBar resignFirstResponder];
} else {
self.disableViewOverlay.alpha = 0;
[self.view addSubview:self.disableViewOverlay];
[UIView beginAnimations:@"FadeIn" context:nil];
[UIView setAnimationDuration:0.5];
self.disableViewOverlay.alpha = 0.6;
[UIView commitAnimations];
// probably not needed if you have a details view since you
// will go there on selection
NSIndexPath *selected = [self.theTableView
indexPathForSelectedRow];
if (selected) {
[self.theTableView deselectRowAtIndexPath:selected
animated:NO];
}
}
[searchBar setShowsCancelButton:active animated:YES];
}
That cleans things up nicely. Now when you run the app you should have the main view fade out smoothly when you enter the UISearchBar just like mine below:

Conclusion
There we have it, a nice Search View that you have full control over. I’ve included the full code with inline comments below, have at it! If you have any suggestions or feedback please leave a comment!
SearchViewController.h
//
// SearchViewController.h
//
#import <UIKit/UIKit.h>
@interface SearchViewController : UIViewController
<UISearchBarDelegate, UITableViewDataSource> {
NSMutableArray *tableData;
UIView *disableViewOverlay;
UITableView *theTableView;
UISearchBar *theSearchBar;
}
@property(retain) NSMutableArray *tableData;
@property(retain) UIView *disableViewOverlay;
@property (nonatomic, retain) IBOutlet UITableView *theTableView;
@property (nonatomic, retain) IBOutlet UISearchBar *theSearchBar;
- (void)searchBar:(UISearchBar *)searchBar activate:(BOOL) active;
@end
SearchViewController.m
//
// SearchViewController.m
//
#import "SearchViewController.h"
@implementation SearchViewController
@synthesize tableData;
@synthesize disableViewOverlay;
@synthesize theSearchBar;
@synthesize theTableView;
// Initialize tableData and disabledViewOverlay
- (void)viewDidLoad {
[super viewDidLoad];
self.tableData =[[NSMutableArray alloc]init];
self.disableViewOverlay = [[UIView alloc]
initWithFrame:CGRectMake(0.0f,44.0f,320.0f,416.0f)];
self.disableViewOverlay.backgroundColor=[UIColor blackColor];
self.disableViewOverlay.alpha = 0;
}
// Since this view is only for searching give the UISearchBar
// focus right away
- (void)viewDidAppear:(BOOL)animated {
[self.theSearchBar becomeFirstResponder];
[super viewDidAppear:animated];
}
#pragma mark -
#pragma mark UISearchBarDelegate Methods
- (void)searchBar:(UISearchBar *)searchBar
textDidChange:(NSString *)searchText {
// We don't want to do anything until the user clicks
// the 'Search' button.
// If you wanted to display results as the user types
// you would do that here.
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
// searchBarTextDidBeginEditing is called whenever
// focus is given to the UISearchBar
// call our activate method so that we can do some
// additional things when the UISearchBar shows.
[self searchBar:searchBar activate:YES];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
// searchBarTextDidEndEditing is fired whenever the
// UISearchBar loses focus
// We don't need to do anything here.
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
// Clear the search text
// Deactivate the UISearchBar
searchBar.text=@"";
[self searchBar:searchBar activate:NO];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// Do the search and show the results in tableview
// Deactivate the UISearchBar
// You'll probably want to do this on another thread
NSArray *results = [SomeService doSearch:searchBar.text];
[self searchBar:searchBar activate:NO];
[self.tableData removeAllObjects];
[self.tableData addObjectsFromArray:results];
[self.theTableView reloadData];
}
// We call this when we want to activate/deactivate the UISearchBar
// Depending on active (YES/NO) we disable/enable selection and
// scrolling on the UITableView
// Show/Hide the UISearchBar Cancel button
// Fade the screen In/Out with the disableViewOverlay and
// simple Animations
- (void)searchBar:(UISearchBar *)searchBar activate:(BOOL) active{
self.theTableView.allowsSelection = !active;
self.theTableView.scrollEnabled = !active;
if (!active) {
[disableViewOverlay removeFromSuperview];
[searchBar resignFirstResponder];
} else {
self.disableViewOverlay.alpha = 0;
[self.view addSubview:self.disableViewOverlay];
[UIView beginAnimations:@"FadeIn" context:nil];
[UIView setAnimationDuration:0.5];
self.disableViewOverlay.alpha = 0.6;
[UIView commitAnimations];
// probably not needed if you have a details view since you
// will go there on selection
NSIndexPath *selected = [self.theTableView
indexPathForSelectedRow];
if (selected) {
[self.theTableView deselectRowAtIndexPath:selected
animated:NO];
}
}
[searchBar setShowsCancelButton:active animated:YES];
}
#pragma mark -
#pragma mark UITableViewDataSource Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = @"SearchResult";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
id *data = [self.tableData objectAtIndex:indexPath.row];
cell.textLabel.text = data.name;
return cell;
}
#pragma mark -
#pragma mark Memory Management Methods
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[theTableView release], theTableView = nil;
[theSearchBar release], theSearchBar = nil;
[tableData dealloc];
[disableViewOverlay dealloc];
[super dealloc];
}
@end
March 01, 2010 | View Comments
|
programming,
iphone,
objective-c,
uisearchbar,
uitableview
I’d like to thank everyone who came out to the latest meeting, we had quite the turnout! A few new faces as well which is always great.
For anyone who doesn’t know “OGRE” stands for “Ottawa Group of Ruby Enthusiasts”. We’re a local group that gets together on the fourth Tuesday of each month to talk about Ruby, programming, cool projects and sometimes even Python. We’re a fairly diverse group from professional Ruby developers, to tinkerers, people just starting out and everything in between. Check out the website and the google group for more info.
This past Tuesday we had Don Kelly and Julie Hache giving a couple presentations. To recap:
- Don Kelly talked about TreeTop (http://treetop.rubyforge.org/) and LLVM (http://llvm.org/) giving us a short example of writing a very simple language with both. He also spent a bit of time showing us the SEXP parser and interpreter (http://github.com/karfai/sexp) that he was working on with both of those tools. Very cool stuff.
- Julie Hache gave a presentation on some of the stuff that has changed with Rails 3 and what the upgrade process is going to look like. She also walked through how we can get setup and playing with Rails 3 right now with the beta (http://weblog.rubyonrails.org/2010/2/5/rails-3-0-beta-release/). Julie also mentioned a book that Jeremy McAnally just released with over 100 pages of info on the Rails 3 upgrade process (http://www.railsupgradehandbook.com/). Jeremy knows his stuff so I’m sure it will be $12 well spent.
At the end of the night Bryan Larsen gave another mention of the recent Hobo release (http://hobocentral.net/blog/2010/02/23/hobo-1-0-released/) so we should all check that out.
Once again, thanks to Julie and Don for doing the presentations and thanks for everyone who came out. We look forward to seeing all of you next month!
February 24, 2010 | View Comments
|
ogre,
meetup,
ottawa,
ruby,
rails,
treetop,
llvm
In the last couple months I’ve been thinking a bit about expanding the skills I have into some different areas. I’ve been working with Ruby on Rails almost exclusively for over three years and have gotten to know the ins and outs pretty well. I love Ruby and Rails, but I’m getting that itch to play with something new. I’ve been playing around with iPhone development a bit (though I’ve been slacking) and find that to be really interesting, but I decided the other day to try something different to fill this need (I will continue to play with iPhone stuff, it’s just too cool to let go).
Javascript will be my next conquest. Writing Javascript with jQuery to be exact. I’ve had my love for jQuery reignited and it complements the other web development skills I already have. I’d like to think I’m already pretty good with jQuery, I’ve been using it for various projects for awhile now, but my goal is to learn it in and out; all the best practices and neat tricks as well as the limitations and how to overcome them.
To do this I’m going to start out by trying my hand at writing a few jQuery plugins of my own and try to integrate jQuery into any work I might be doing. To start out I’ve been reading a few blogs and have come across a really great plugin pattern that I’m going to follow for most of my stuff.
jQuery Plugin Pattern: Giving it jQuery
First thing we want to start with is giving our plugin access to jQuery. This sounds like a no brainer but you have to be careful since the jQuery helper method ($) can be redefined and therefore isn’t guaranteed to exist. To overcome this the pattern below is usually used.
(function($) {
// plugin code here
})(jQuery);
This might look a little scary, but all we’re doing is defining a function that takes a parameter. We’re then calling the function right away passing in jQuery. Now we can use $ in our plugin without worry!
Playing Nice: Returning jQuery
When using a jQuery plugin there are a couple of things that users come to expect. The two main things is that the plugin should be able to work on an array of objects and at the end it should return the jQuery object to allow chain ability. Accomplishing this is very easy with the code below.
(function($) {
// define the plugin
$.fn.template = function(options) {
// iterate through the matched elements
// returning this at the end
return this.each(function() {
// plugin logic here
});
};
})(jQuery);
Giving the User Control: Plugin Options and Defaults
The next thing we’re going to want to give our plugins is a way for the user to pass in options to override the defaults. I also like the way the jQuery UI plugins give you public access to the defaults so that you can set them once instead of every time the plugin is called. Learning jQuery has some code to do just that and looks something like what I have below.
(function($) {
// define the plugin
$.fn.template = function(options) {
// extend the default options with those provided
// extending an empty object prevents overriding of
// our defaults object
var opts = $.extend({}, $.fn.template.defaults, options);
// ...
};
// plugin defaults
$.fn.template.defaults = {
color: 'red'
};
})(jQuery);
This code lets our plugin users do a couple interesting things. First thing is they can pass options to plugin like below.
$('h1').template( { color: 'black' } )
What is more interesting is the fact that instead of having to pass the same options in every time they could set the defaults at the start and be done with it, only passing in options that differ from their own defaults.
$.fn.template.defaults.color = 'black';
$('h1').template(); // plugin default color is black
$('h1').template( { color: 'orange' } ); // override the defaults
This gives the users a lot of flexibility and helps them to keep their code nice and DRY.
Giving the User Control: Exposing Public Functions
Sometimes it makes sense to expose plugin functions to the user so that they can override the implementation with their own. This might not be needed for all plugins, but I think it would be useful for most.
// ...
return this.each(function() {
// use the helper function
var something = $.fn.template.helper();
// plugin logic here
});
};
// public helper function that can be overridden by the user
$.fn.template.helper = function () {
// do something that helps
};
// ...
This gives the user a lot of control over how the plugin behaves. Again, it doesn’t make sense for all plugins but when sometimes it can be a real life saver. All the user has to do to provide their own implementation to the helper function is to redefine it like below.
$.fn.template.helper = function () {
// do something different
}
The big benefit to this that I see over having a callback is that you could save the original function and use it in your implementation, something like this.
var helper = $.fn.template.helper;
$.fn.template.helper = function () {
var results = helper();
// do something different with the results
}
This lets you build upon the original implementation if you want to, meaning less repeated code.
Wrapping Up
Well, that pretty much does it for how I want my jQuery plugins to be structured. I’ve left out a bunch of stuff that beginners should probably take a look at, these links should help if you’re interested.
I’ve got the full template below and I’ve also put it up on Github with a few more comments, feel free to fork it and send me updates.
(function($) {
// define the plugin
$.fn.template = function(options) {
// extend the default options with those provided
// extending an empty object prevents overriding
// of our defaults object
var opts = $.extend({}, $.fn.template.defaults, options);
// iterate through the matched elements
// returning this at the end
return this.each(function() {
// use the helper function
var something = $.fn.template.helper();
// plugin logic here
});
};
// public helper function that can be overridden by the user
$.fn.template.helper = function () {
// do something that helps
};
// plugin defaults
$.fn.template.defaults = {
color: 'red'
};
})(jQuery);
November 23, 2009 | View Comments
|
programming,
random,
skills,
javascript
In the last little while I started working on a Rails project that had been targeting an older version of mySQL. I ran into a few confusing problems while getting started so I thought I’d bring together all the various mySQL and Rails related info I came across to fix my problems into one place.
Problem: ActiveRecord failing with a default field value of blank
This is a problem that a few people have posted on ruby-forum without getting much of a response, but what is there pointed me in the right direction. The main problem is that older versions of mySQL (pre 5.0.45 I believe) treat a NULL value inserted into a string column as a blank string. Newer versions of mySQL fix this issue but this fix causes some confusing behaviour.
What happens in the older version is maybe you forget to pass a value for that NOT NULL string; mySQL happily treats that as a blank string and gives you no errors. When you upgrade mySQL to a version that ‘fixes’ this you end up with all kinds of errors because the constraint isn’t being met anymore.
Solution
Well, if you don’t actually need the constraint remove it, otherwise explicitly send a value so the constraint is met. Update your tests and validations so you’re passing and checking that the correct values are being passed.
Problem: Error: uninitialized constant MysqlCompat::MysqlRes
Another one that also shows up on a ruby forum post. The root of this problem is that on OS X the mysql gem has to be told where the installed mySQL libraries are and which architecture your machine is.
Solution
I found the solution here but had to modify the gem install slightly for my 64 bit machine.
sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- \
--with-mysql-dir=/usr/local/mysql --with-mysql-lib=/usr/local/mysql/lib \
--with-mysql-include=/usr/local/mysql/include
This might be slightly different depending on where mysql ended up on your machine and your machines architecture.
Problem: mySQL can’t install because a newer version exists
Or, removing mySQL for real. While dealing with my first problem I tried installing and uninstalling different versions of mySQL along the way the mySQL install got angry because some files weren’t removed and wouldn’t install the version I wanted.
Solution
I managed to get mySQL off my machine by combining the approaches from a blog post and an answer found on Stack Overflow. Before doing this I did run the mySQL uninstaller, but it seems to leave some goodies behind.
In the end it boiled down to running the following commands:
sudo rm /usr/local/mysql
sudo rm -rf /usr/local/mysql*
sudo rm -rf /Library/StartupItems/MySQLCOM
sudo rm -rf /Library/PreferencePanes/My*
edit /etc/hostconfig and removed the line MYSQLCOM=-YES-
rm -rf ~/Library/PreferencePanes/My*
sudo rm -rf /Library/Receipts/mysql*
sudo rm -rf /Library/Receipts/MySQL*
sudo rm -rf /var/db/receipts/com.mysql.*
I know it’s a lot of mumbo jumbo, but mySQL seems to hook itself in all over the place and this, in the end, did seem to get rid of everything.
Conclusion
Well, that’s all I’ve got for mySQL right now. It was a pretty frustrating trip, but in the end I managed to find a solution to all the problems and now it’s all in one place for all of time.
As a side note I’m really starting to find Stack Overflow to be an incredibly useful resource. Sometimes it might even save time trying a quick search there before hitting up Google.
October 28, 2009 | View Comments
|
programming,
mysql,
ruby,
rails,
osx