Tag Archives: UITableView

How to create a searchable UITableView in iOS 9

TableSearch

Creating searchable table views was always a bit of a nightmare before iOS 8. Until then we had to use a UISearchDisplayController, and sometimes a separate UISearchBar object that needed to be scrolled in and out of vision to make it look elegant. We also had to conform to several protocols. All this was cumbersome and error prone.

Since iOS 8 we can use something much easier called the UISearchController. Sadly the Apple documentation isn’t exactly forthcoming on how to use this thing. Let me show you how to do it in this article.

I’m using Objective-C, Xcode 7, a bit of KVO magic and a simple array for dummy values (no Core Data). If you prefer video instructions, check out my 4 part screencast on building this project over here.

Continue reading





Screencast: Building a searchable UITableView in iOS 9

In this 4 part course I’ll show you how to build a searchable UITableView using iOS 9.1 and Xcode 7.1.

  • Part 1 will talk you through how to build a UITableView with dummy data
  • In Part 2 I’ll show you how to use a UISearchController to display a second table view
  • In Part 3 we’ll discuss how to filter results using an NSPredicate
  • And in Part 4 we’ll see how to communicate those search results to the second table view via KVO.

The whole course is about 50 minutes long, and I’ll explain details step by step. Part 1 is free, and the remainder of the course is available to members of this website.

All code is Objective-C, and the the full project I’m building is available on GitHub.

Sounds interesting? Join me now and I’ll see you on the other side 😉

This content is for members only.

Demo Project

Here’s the code I’ve built during the screencast. As promised, I’ve also implemented the UISearchControllerDelegate methods – just in case you need them:

Further Reading

The Apple Class References aside, I’ve also put together written instructions on how to create this project in my next article. If you prefer written instructions, go check it out here – it covers the same steps as these videos 🙂





How to tint the sections in your UITableVIew

TableSectionsTo make those sections in a UITableView stand out a bit, you can give them a different colour tint.

Add a new method in your table view delegate and paint with numbers:

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
    
    UIColor *pinkTint = [UIColor colorWithRed:0.7 green:0.5 blue:0.8 alpha:0.7];
    view.tintColor = pinkTint;
}

The above will create colours in RGB, and values between 0.0 and 1.0 are allowed.

Or, if you’d rather show a picture instead (as a pattern or because you have a nice colour swatch), grab it from your project and turn it into a colour like so:

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
    
    UIColor *pinkTint = [UIColor colorWithRed:0.7 green:0.5 blue:0.8 alpha:0.7];
    view.tintColor = pinkTint;
}

– (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {

UIColor *niceColour = [UIColor colorWithPatternImage:[UIImage imageNamed:@”NiceColour”]];
view.tintColor = niceColour;
}





How to implement Table Headers with NSFetchedResultsController in Core Data

iOS Simulator Screen Shot 28 Aug 2015 15.48.03To use sections in a table view that’s powered by Core Data and a fetched results controller, we need to specify a property called sectionNameKeyPath. It’s the key to making those header titles show up. Reading Apple’s ever so colourful documentation however doesn’t give us a clue what this means in human terms.

Here’s how to implement those with a simple example.

Starting out from the Master/Detail template in Xcode, create a new project and choose Core Data. This will setup the fetched results controller along with the rest of the Core Data stack. We’ll also get one handy Event entity with a single attribute: an NSDate called timeStamp. Run the app, click the plus button at the top and we’ll see a new date is inserted into the table.

But the table has no sections. Let’s assume we’d like to create a new section for every minute that has been logged in the database. So 18:07 would have a section, 18:08, and whatever else is available.

Creating Managed Object Subclasses

First we’ll create a managed object subclass from our model so that we can write some code for this. Choose the .xcdatamodeld file in Xcode and select Editor – Create NSManagedObject Subclass. Stash the two resulting files in your project folder.

In Event.h, add another property. It won’t be saved, it’ll be calculated on the fly. We already have the timeStamp property for the date object, so let’s call the new property minuteSection. Here’s the Even.h file:

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.>


@interface Event : NSManagedObject

@property (nonatomic, retain) NSDate * timeStamp;
@property (nonatomic, retain) NSString *minuteSection;

@end

In the Event.m file we’ll create a custom initialiser, which will turn our timeStamp into time string. Since this is a Core Data property we’ll have to mark it as dynamic, much like all the other Core Data properties in managed object subclasses:

#import "Event.h"


@implementation Event

@dynamic timeStamp;
@dynamic minuteSection;

- (NSString *)minuteSection {
    
    NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
    formatter.timeStyle = NSDateFormatterShortStyle;
    NSString *sectionTitle = [formatter stringFromDate:self.timeStamp];
    
    return sectionTitle;
}

@end

Here we use an NSDateFormatter that turns the long date object into something like 18:07. Every event in our database logged at 18:07 for example will get its own section with that title.

Tweaking the Fetched Results Controller

Next, let’s turn our attention to the NSFetchedResultsController. In the Xcode template it’s in MasterViewController.m. There’s a custom initialiser with a method that by default looks like this:

// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
                                                         initWithFetchRequest:fetchRequest
                                                         managedObjectContext:self.managedObjectContext
                                                         sectionNameKeyPath:nil
                                                         cacheName:@"Master"];

Take a look at the sectionNameKeyPath property here: it’s set to nil, meaning we’re not displaying any sections. To tell the controller which property we want to use as section criteria, all we have to do is provide the string of the property we’d like to use. In our case, that’s minuteSection. Quick modification:

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
                                                         initWithFetchRequest:fetchRequest
                                                         managedObjectContext:self.managedObjectContext
                                                         sectionNameKeyPath:@"minuteSection"
                                                         cacheName:nil];

I’ve also taken the liberty to remove the cache and set it to nil, we don’t really need it here.

Showing the Section Titles

Right now the app will run as usual, but we still won’t see any new sections. Even though they are displayed, we don’t see them separated from the cells. What we need to do is add a method to our table view delegate and make those sections show up with the right titles.

In MasterViewController.m, hunt for a pragma mark called Table View. You’ll find methods to show how many rows and sections there are, both of which are being read from the fetched results controller. Examine the code to spot some similarities to what we’re doing next.

Add a method called titleForHeaderInSection, it comes up as part of the automatic code completion as soon as you type “- table” (it’s a long list). Here’s what we need to add to it for those section headers to show up:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    
    id  sectionInfo = [self.fetchedResultsController.sections objectAtIndex:section];
    return [sectionInfo name];
}

It’s only two lines of code, but they’re rather long. Here we grab the controller’s current section and call it’s name method, which returns the string we’re looking for (i.e. 18:07, the hour and minute we’d like to use).

Run the app and add a few values to it. On every new minute, a new section is created. When you remove values, empty sections are automatically removed as well.

Further Reading

Demo Project

I’ve got a working demo project on GitHub – feel free to check it out:

iOS Simulator Screen Shot 28 Aug 2015 15.48.03





How to create a searchable UITableView (2014)

In this course I will show you how to create a searchable table view in Xcode 5.1 and iOS 7. The app will also be compatible with iOS 6.

There are six parts in total:

  • Part 1: an overview of what we’d like to achieve
  • Part 2: we’ll build the table view and populate it with dummy data
  • Part 3: hooking up the UISearchDisplayController
  • Part 4: displaying search results
  • Part 5: hiding the UISearchBar when the app launches (and activating it with a button)
  • Part 6: hiding the search bar in iOS 6

The first two videos are available to watch for free. You can see the rest of the course (about 35 minutes in total) as a member of this website. Sign up here if you’re interested!

This content is for members only.



How to attach custom actions to an Edit Button Item (or The Dark Secrets of the Edit/Done Button)

You can create an editButtonItem in a UITableViewController like this:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.leftBarButtonItem = self.editButtonItem;
}

If your view controller is embedded into a navigation controller, an edit/done button will show up and you can toggle its state by pressing it.

Edit-Done

That’s great, but it takes a deeper understanding of how this happens to make full use of it in your own projects.

What this button actually does is set the “editing” property of your view controller to YES or NO. If it’s YES, the button turns into “Done” (and equivalent translations), and if it’s NO then it turns into “Edit”. You won’t notice this on a standard view controller, but on derivatives (such as a UITableViewController) this has a substantial effect: the table view enters edit mode and changes appearance.

You can therefore test the state of the button by querying your view controller:

    if (self.editing) {
        // we're in edit mode
        
    } else {
        // we're not in edit mode
        
    }

But that’s not an action, that’s just a test. If you’d like for something to happen when that edit button is pressed, you must override the following function which is called every time that button is pressed:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    
    // overriding this method means we can attach custom functions to the button
    [super setEditing:editing animated:animated];
    
    // attaching custom actions here
    if (editing) {
        // we're in edit mode
        
    } else {
        // we're not in edit mode
        
    }
}

This method is called by the table view when that button is pressed. We’re calling the super method first, then add our own bits at the end. You can also call this method from anywhere and put the table view into or out of edit mode like so:

[self setEditing:NO animated:YES];