Created
March 7, 2014 02:40
-
-
Save keicoder/9404097 to your computer and use it in GitHub Desktop.
objective-c : making custom SearchBar
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//making custom SearchBar | |
//SSAppDelegate.h | |
@interface SSAppDelegate : UIResponder <UIApplicationDelegate> | |
@property (strong, nonatomic) UIWindow *window; | |
@end | |
//SSAppDelegate.m | |
@implementation SSAppDelegate | |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions | |
{ | |
return YES; | |
} | |
@end | |
//SSSearchBar.h | |
#import <UIKit/UIKit.h> | |
@protocol SSSearchBarDelegate; | |
//A clean, easy to use, awesome replacement for UISearchBar. | |
@interface SSSearchBar : UIView | |
//Wrappers around the Textfield subview | |
@property (nonatomic) NSString *text; | |
@property (nonatomic) UIFont *font; | |
@property (nonatomic) NSString *placeholder; | |
//The text field subview | |
@property (nonatomic) UITextField *textField; | |
@property (nonatomic, getter = isCancelButtonHidden) BOOL cancelButtonHidden; //NO by Default | |
@property (nonatomic, weak) id <SSSearchBarDelegate> delegate; | |
@end | |
@protocol SSSearchBarDelegate <NSObject> | |
@optional | |
- (void)searchBarCancelButtonClicked:(SSSearchBar *)searchBar; | |
- (void)searchBarSearchButtonClicked:(SSSearchBar *)searchBar; | |
- (BOOL)searchBarShouldBeginEditing:(SSSearchBar *)searchBar; | |
- (void)searchBarTextDidBeginEditing:(SSSearchBar *)searchBar; | |
- (void)searchBarTextDidEndEditing:(SSSearchBar *)searchBar; | |
- (void)searchBar:(SSSearchBar *)searchBar textDidChange:(NSString *)searchText; | |
@end | |
//A rounded view that makes up the background of the search bar. | |
@interface SSRoundedView : UIView | |
@end | |
//SSSearchBar.m | |
#import "SSSearchBar.h" | |
//I like these values; Feel free to play around though :-) | |
#define kXMargin 8 | |
#define kYMargin 4 | |
#define kIconSize 16 | |
#define kSearchBarHeight 32 | |
@interface SSSearchBar () <UITextFieldDelegate> | |
{ | |
BOOL _cancelButtonHidden; | |
} | |
@property (nonatomic) UIButton *cancelButton; | |
@property (nonatomic) UIImageView *searchImageView; | |
@property (nonatomic) SSRoundedView *backgroundView; | |
@property (nonatomic) UIImage *searchImage; | |
@end | |
@implementation SSSearchBar | |
#pragma mark - Initializers | |
- (void)setDefaults { | |
UIImage *searchIcon = [UIImage imageNamed:@"search-icon"]; | |
_searchImage = searchIcon; | |
self.backgroundColor = [UIColor clearColor]; | |
NSUInteger boundsWidth = self.bounds.size.width; | |
NSUInteger textFieldHeight = self.bounds.size.height - kYMargin; | |
//Background Rounded White Image | |
self.backgroundView = [[SSRoundedView alloc] initWithFrame:CGRectMake(0, 0, boundsWidth, self.bounds.size.height)]; | |
[self addSubview:self.backgroundView]; | |
//Search Image | |
self.searchImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kIconSize, kIconSize)]; | |
self.searchImageView.image = self.searchImage; | |
self.searchImageView.contentMode = UIViewContentModeScaleAspectFit; | |
self.searchImageView.center = CGPointMake(kIconSize/2 + kXMargin, CGRectGetMidY(self.bounds)); | |
[self addSubview:self.searchImageView]; | |
//TextField | |
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(2*kXMargin + kIconSize, kYMargin, boundsWidth - 4*kXMargin - 2*kIconSize, textFieldHeight)]; | |
self.textField.delegate = self; | |
self.textField.returnKeyType = UIReturnKeySearch; | |
self.textField.autocapitalizationType = UITextAutocapitalizationTypeNone; | |
self.textField.autocorrectionType = UITextAutocorrectionTypeNo; | |
UIFont *defaultFont = [UIFont fontWithName:@"Avenir Next" size:14]; | |
self.textField.font = defaultFont; | |
self.textField.textColor = [UIColor blackColor]; | |
[self addSubview:self.textField]; | |
//Cancel Button | |
self.cancelButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, kIconSize, kIconSize)]; | |
[self.cancelButton setImage:[UIImage imageNamed:@"close-icon"] forState:UIControlStateNormal]; | |
self.cancelButton.contentMode = UIViewContentModeScaleAspectFit; | |
self.cancelButton.center = CGPointMake(boundsWidth - (kIconSize/2 + kXMargin), CGRectGetMidY(self.bounds)); | |
[self.cancelButton addTarget:self action:@selector(pressedCancel:) forControlEvents:UIControlEventTouchUpInside]; | |
[self addSubview:self.cancelButton]; | |
//Listen to text changes | |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextFieldTextDidChangeNotification object:self.textField]; | |
} | |
- (id)initWithCoder:(NSCoder *)aDecoder | |
{ | |
self = [super initWithCoder:aDecoder]; | |
if (self) { | |
[self setDefaults]; | |
} | |
return self; | |
} | |
- (id)initWithFrame:(CGRect)frame | |
{ | |
CGRect newFrame = frame; | |
frame.size.height = kSearchBarHeight; | |
frame = newFrame; | |
self = [super initWithFrame:frame]; | |
if (self) { | |
[self setDefaults]; | |
} | |
return self; | |
} | |
- (id)init | |
{ | |
return [self initWithFrame:CGRectMake(10, 20, 300, 32)]; | |
} | |
#pragma mark - Properties and actions | |
- (NSString *)text { | |
return self.textField.text; | |
} | |
- (void)setText:(NSString *)text { | |
self.textField.text = text; | |
} | |
- (NSString *)placeholder { | |
return self.textField.placeholder; | |
} | |
- (void)setPlaceholder:(NSString *)placeholder { | |
self.textField.placeholder = placeholder; | |
} | |
- (UIFont *)font { | |
return self.textField.font; | |
} | |
- (void)setFont:(UIFont *)font { | |
self.textField.font = font; | |
} | |
- (BOOL)isCancelButtonHidden { | |
return _cancelButtonHidden; | |
} | |
- (void)setCancelButtonHidden:(BOOL)cancelButtonHidden { | |
if (_cancelButtonHidden != cancelButtonHidden) { | |
_cancelButtonHidden = cancelButtonHidden; | |
self.cancelButton.hidden = cancelButtonHidden; | |
} | |
} | |
- (void)pressedCancel: (id)sender { | |
if ([self.delegate respondsToSelector:@selector(searchBarCancelButtonClicked:)]) | |
[self.delegate searchBarCancelButtonClicked:self]; | |
} | |
#pragma mark - Text Delegate | |
- (void)textChanged: (id)sender { | |
if ([self.delegate respondsToSelector:@selector(searchBar:textDidChange:)]) | |
[self.delegate searchBar:self textDidChange:self.text]; | |
} | |
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { | |
if ([self.delegate respondsToSelector:@selector(searchBarShouldBeginEditing:)]) | |
return [self.delegate searchBarShouldBeginEditing:self]; | |
return YES; | |
} | |
- (void)textFieldDidBeginEditing:(UITextField *)textField { | |
if ([self.delegate respondsToSelector:@selector(searchBarTextDidBeginEditing:)]) | |
[self.delegate searchBarTextDidBeginEditing:self]; | |
} | |
- (void)textFieldDidEndEditing:(UITextField *)textField { | |
if ([self.delegate respondsToSelector:@selector(searchBarTextDidEndEditing:)]) | |
[self.delegate searchBarTextDidEndEditing:self]; | |
} | |
- (BOOL)textFieldShouldReturn:(UITextField *)textField { | |
if ([self.delegate respondsToSelector:@selector(searchBarSearchButtonClicked:)]) | |
[self.delegate searchBarSearchButtonClicked:self]; | |
return YES; | |
} | |
- (BOOL)isFirstResponder { | |
return [self.textField isFirstResponder]; | |
} | |
- (BOOL)becomeFirstResponder { | |
return [self.textField becomeFirstResponder]; | |
} | |
- (BOOL)resignFirstResponder { | |
[self.textField resignFirstResponder]; | |
return YES; | |
} | |
#pragma mark - Cleanup | |
- (void)dealloc { | |
[[NSNotificationCenter defaultCenter] removeObserver:self]; | |
} | |
@end | |
@implementation SSRoundedView | |
- (id)initWithFrame:(CGRect)frame { | |
self = [super initWithFrame:frame]; | |
if (self) { | |
self.opaque = NO; | |
self.backgroundColor = [UIColor clearColor]; | |
} | |
return self; | |
} | |
- (void)drawRect:(CGRect)rect { | |
CGContextRef contextRef = UIGraphicsGetCurrentContext(); | |
CGContextSetFillColorWithColor(contextRef, [UIColor whiteColor].CGColor); | |
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:18]; | |
[path fill]; | |
} | |
@end | |
//SSViewController.h | |
#import <UIKit/UIKit.h> | |
@interface SSViewController : UIViewController | |
@end | |
//SSViewController.m | |
#import "SSViewController.h" | |
#import "SSSearchBar.h" | |
@interface SSViewController () <SSSearchBarDelegate> | |
@property (weak, nonatomic) IBOutlet SSSearchBar *searchBar; | |
@property (weak, nonatomic) IBOutlet UITableView *tableView; | |
@property (nonatomic) NSArray *data; | |
@property (nonatomic) NSArray *searchData; | |
@end | |
@implementation SSViewController | |
- (void)viewDidLoad | |
{ | |
[super viewDidLoad]; | |
self.searchBar.cancelButtonHidden = NO; | |
self.searchBar.placeholder = NSLocalizedString(@"Search text here!", nil); | |
self.searchBar.delegate = self; | |
[self.searchBar becomeFirstResponder]; | |
self.data = @[ @"Hey there!", @"This is a custom UISearchBar.", @"And it's really easy to use...", @"Sweet!" ]; | |
self.searchData = self.data; | |
} | |
- (UIStatusBarStyle)preferredStatusBarStyle { | |
return UIStatusBarStyleLightContent; | |
} | |
#pragma mark - SSSearchBarDelegate | |
- (void)searchBarCancelButtonClicked:(SSSearchBar *)searchBar | |
{ | |
self.searchBar.text = @""; | |
[self filterTableViewWithText:self.searchBar.text]; | |
} | |
- (void)searchBarSearchButtonClicked:(SSSearchBar *)searchBar | |
{ | |
[self.searchBar resignFirstResponder]; | |
} | |
- (void)searchBar:(SSSearchBar *)searchBar textDidChange:(NSString *)searchText | |
{ | |
[self filterTableViewWithText:searchText]; | |
} | |
#pragma mark - UITableViewDataSource | |
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView | |
{ | |
return 1; | |
} | |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section | |
{ | |
return [self.searchData count]; | |
} | |
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; | |
[self customiseTableViewCell:cell atIndexPath:indexPath]; | |
cell.textLabel.text = self.searchData[indexPath.row]; | |
cell.textLabel.font = [UIFont fontWithName:@"Avenir Next" size:14]; | |
cell.textLabel.textColor = [UIColor whiteColor]; | |
return cell; | |
} | |
#pragma mark - UITableViewDelegate | |
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
[tableView deselectRowAtIndexPath:indexPath animated:YES]; | |
} | |
#pragma mark - Helper Methods | |
- (void)filterTableViewWithText:(NSString *)searchText | |
{ | |
if ([searchText isEqualToString:@""]) { | |
self.searchData = self.data; | |
} else { | |
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self CONTAINS[cd] %@", searchText]; | |
self.searchData = [self.data filteredArrayUsingPredicate:predicate]; | |
} | |
[self.tableView reloadData]; | |
} | |
- (void)customiseTableViewCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { | |
//Some fancy stuff - This really isn't needed and isn't the best way to do it. Subclass UITableViewCell if you want something like this. | |
UIView *backgroundColorView = [cell.contentView viewWithTag:10]; | |
CGRect backgroundFrame = CGRectMake(2, 2, cell.bounds.size.width - 4, cell.bounds.size.height - 2 - (indexPath.row == self.searchData.count - 1? 2:0)); | |
if (!backgroundColorView) { | |
backgroundColorView = [[UIView alloc] initWithFrame:backgroundFrame]; | |
backgroundColorView.tag = 10; | |
backgroundColorView.backgroundColor = self.view.backgroundColor; | |
[cell.contentView insertSubview:backgroundColorView atIndex:0]; | |
cell.textLabel.backgroundColor = [UIColor clearColor]; | |
} | |
else { | |
backgroundColorView.frame = backgroundFrame; | |
} | |
} | |
@end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment