Customizing search bar in iPhone application Development - iphone

In my application I have to add a search bar at the head of the tableview. I am able to add the searchbar but problem is without adding default search bar of ios can i add my customize search bar?? I am giving an image to see what types of search bar will be there...

you can subclass the UISearchBar and override the layoutSubviews method :
- (void)layoutSubviews {
UITextField *searchField;
NSUInteger numViews = [self.subviews count];
for(int i = 0; i < numViews; i++) {
if([[self.subviews objectAtIndex:i] isKindOfClass:[UITextField class]]) { //conform?
searchField = [self.subviews objectAtIndex:i];
}
}
if(!(searchField == nil)) {
searchField.textColor = [UIColor whiteColor];
[searchField setBackground: [UIImage imageNamed:#"yourImage.png"] ];
[searchField setBorderStyle:UITextBorderStyleNone];
}
[super layoutSubviews];
}
Also you can :
//to clear searchbar backgraound
- (void) clearSearchBarBg
{
for (UIView *subview in theSearchBar.subviews)
{
if ([subview isKindOfClass:NSClassFromString(#"UISearchBarBackground")])
{
[subview removeFromSuperview];
break;
}
}
}
//display showSearchButtonInitially in a keyboard
- (void)showSearchButtonInitially
{
UIView * subview;
NSArray * subviews = [theSearchBar subviews];
for(subview in subviews)
{
if( [subview isKindOfClass:[UITextField class]] )
{
NSLog(#"setEnablesReturnKeyAutomatically");
[((UITextField*)subview) setEnablesReturnKeyAutomatically:NO];
((UITextField*)subview).delegate=self;
[((UITextField*)subview) setEnabled:TRUE];
((UITextField*)subview).borderStyle = UITextBorderStyleNone;
break;
}
}
}

Look for Apple DOC for UISearchBar
You have bunch of methods there to get whatever you want
You can get UITextView Inside the search bar by
UITextField *textField = [searchBar.subviews objectAtIndex:2];
if ([textField isKindOfClass:[UITextField class]]) {
//Do your customization
}
Again look for AppleDoc for UITextField. You have bunch of methods for that also.

Yeah definitely. You can make your custom search bar (which is a sub-class of UIView) and add it as subview to the tableHeaderView.

[[searchBarDesign.subviews objectAtIndex:0] removeFromSuperview];
here searchBarDesign is my searchBar name.

I think it's better just set all properties of UISearchBar when it is loaded.
#interface MySearchBar : UISearchBar
#end
#implementation MySearchBar
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self myInitialize];
}
return self;
}
-(void)awakeFromNib
{
[super awakeFromNib];
[self myInitialize];
}
-(void)myInitialize
{
self.backgroundImage = [UIImage imageNamed:#"image.png"];
for (UIView* subview in self.subviews) {
if ([subview isKindOfClass:[UITextField class]]) {
//customize text field
UITextField* textfield = (UITextField*) subview;
}
}
}
#end

Related

UItextField within UISearchbar failing after iOS 7 upgrade

I have a UITextField for the UISearchBar which this was working until iOS 7 upgrade and now it fails at this line: UITextField *textfield=(UITextField*)[[searchBar subviews] objectAtIndex:1];
any idea how to fix this? thanks
// search bar
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 190.0, 44.0)];
searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
UIView *searchBarView = [[UIView alloc] initWithFrame:CGRectMake(90.0, 0.0, 230.0, 44.0)];
searchBarView.autoresizingMask = 0;
searchBar.delegate = self;
searchBar.layer.borderColor=[UIColor whiteColor].CGColor;
UITextField *textfield=(UITextField*)[[searchBar subviews] objectAtIndex:1];
[searchBarView addSubview:searchBar];
self.navigationItem.titleView = searchBarView;
try this , it's work in Both IOS6 and IOS7+ and safe approch
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setFont:[UIFont fontWithName:#"ArialMT" size:10]];
It's not a good idea to assume that second searchBar's subview will be UITextField.
I printed out subviews for UISearchBar, that's what I got on iOS 7:
<__NSArrayM 0x17d141f0>(
<UIView: 0x17d34f40; frame = (0 0; 320 44); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x17d34fa0>>
)
Only one subview, so your ... objectAtIndex:1] will definitely crash.
You can use the following category for UIView to find UITextField in your searchBar:
#interface UIView(Utils)
-(UIView*)findSubviewRecursivelyOfClass:(Class)subviewClass;
#end
#implementation UIView(Utils)
-(UIView*)findSubviewRecursivelyOfClass:(Class)subviewClass
{
if( [self isKindOfClass:subviewClass] ) {
return self;
} else {
for( UIView* child in self.subviews ) {
UIView* result = [child findSubviewRecursivelyOfClass:subviewClass];
if( result ) {
return result;
}
}
return nil;
}
}
#end
Try this one for iOS7.
TESTED
for (id object in [searchBar subviews])
{
for (id subObject in [object subviews])
{
if ([subObject isKindOfClass:[UITextField class]])
{
UITextField *textfield=(UITextField*)subObject;
}
}
}
iOS6
for (id object in [searchBar subviews]) {
if ([object isKindOfClass:[UITextField class]]) {
UITextField *textfield=(UITextField*)object;
}
}
How about a recursive method that can trick to work in any version
UITextField *searchBarTextField = [self findTextFieldFromControl:self.placeSearchBar];
- (UITextField *) findTextFieldFromControl:(UIView *) view
{
for (UIView *subview in view.subviews)
{
if ([subview isKindOfClass:[UITextField class]])
{
return (UITextField *)subview;
}
else if ([subview.subviews count] > 0)
{
return [self findTextFieldFromControl:subview];
}
}
return nil;
}

How can I make a custom view of the 'delete-button' when performing commitEditingStyle within a UITableView

I would like to customize the delete button which is shown when performing the 'swipe to left'-action on a tableview cell. I currently set up a subclass of a UITableViewCell but also want to customize the delete-button which is being shown.
My goal is to place three buttons when swiping.
I choose for another implementation where I was using a UIScrollview in each cell.
http://www.teehanlax.com/blog/reproducing-the-ios-7-mail-apps-interface/
Accepted answer will not work on iOS 7, as there is now UITableViewCellContentView in between. So subviews loop now should look like this(if you want to support older iOS versions too, use currently accepted answer for iOS 6.1-)
for (UIView *subview in self.subviews) {
for (UIView *subview2 in subview.subviews) {
if ([NSStringFromClass([subview2 class]) rangeOfString:#"Delete"].location != NSNotFound) {
// Do whatever you want here
}
}
}
This might help you.
- (void)willTransitionToState:(UITableViewCellStateMask)state
{
[super willTransitionToState:state];
if ((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableViewCellStateShowingDeleteConfirmationMask)
{
for (UIView *subview in self.subviews)
{
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellDeleteConfirmationControl"])
{
UIImageView *deleteBtn = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 64, 33)];
[deleteBtn setImage:[UIImage imageNamed:#"arrow_left_s11.png"]];
[[subview.subviews objectAtIndex:0] addSubview:deleteBtn];
}
}
}
}
Referenced from:
Customize the delete button in UITableView
create custom delete button for uitableview
Custom Delete button On Editing in UITableView Cell
-(void)willTransitionToState:(UITableViewCellStateMask)state{
NSLog(#"EventTableCell willTransitionToState");
[super willTransitionToState:state];
if((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableViewCellStateShowingDeleteConfirmationMask)
{
UIImageView *deleteBtn = [[UIImageView alloc]initWithFrame:CGRectMake( 320,0, 228, 66)];
[deleteBtn setImage:[UIImage imageNamed:#"BtnDeleteRow.png"]];
[[self.subviews objectAtIndex:0] addSubview:deleteBtn];
[self recurseAndReplaceSubViewIfDeleteConfirmationControl:self.subviews];
[self performSelector:#selector(recurseAndReplaceSubViewIfDeleteConfirmationControl:) withObject:self.subviews afterDelay:0];
}
}
The solutions above didn't work for me for iOS 7, at - (void)willTransitionToState:, the delete button wasn't in the view heirarchy so I wasn't able to manipulate anything. I ended up doing everything on - (void)didTransitionToState:. The example below was specifically for when my cells had some spacing at the top so I'm altering the frame of the delete button. If you want to customize the delete button, you can just add a view on top of the delete button or replace it with your own UIButton
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
//your own stuff
//for some reason, editingAccessoryView cannot be nil and must have a non-CGRectZero frame
self.editingAccessoryView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
}
return self;
}
- (void)didTransitionToState:(UITableViewCellStateMask)state
{
[super didTransitionToState:state];
if ((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableViewCellStateShowingDeleteConfirmationMask)
{
UIView *deleteButton = [self deleteButtonSubview:self];
if (deleteButton) {
CGRect frame = deleteButton.frame;
frame.origin.y += defined_padding;
frame.size.height -= defined_padding;
deleteButton.frame = frame;
}
}
}
- (UIView *)deleteButtonSubview:(UIView *)view
{
if ([NSStringFromClass([view class]) rangeOfString:#"Delete"].location != NSNotFound) {
return view;
}
for (UIView *subview in view.subviews) {
UIView *deleteButton = [self deleteButtonSubview:subview];
if (deleteButton) {
return deleteButton;
}
}
return nil;
}

Xcode: How to change all fonts at once in an app?

I was wondering if it was possible to change the fonts on about 100 different viewcontrollers all at once? It would be a lot eisier than going through one by one and changing them. Any ideas? Thank you!
The user interface files (*.xib) are plain text and you can load them into an editor.
In Xcode4 on the left pane you can right-click > open as > source.
This will give you the XML source any you can find/replace there.
Warning: doing something wrong may render the whole file useless, so unless you have source control anyway, make copies of the XIB before attempting changes.
you cant change all the fonts at once....
But i have find one more varient that will help you...
I have made some recursive functions thy can help you..
follow following steps..
First create a class(BaseViewController) extended from UIViewController like in BaseViewController.h file
#interface BaseViewController : UIViewController
And in BaseViewController.m file write following code.
- (void)viewDidLoad
{
[super viewDidLoad];
[self changeFontsOfViewController];
}
-(void)changeFontsOfViewController
{
UIViewController * vv = [self viewControllerOfView:self.view];
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([vv class]) owner:vv options:nil];
for (id object in objects)
{
[self changeFontOfView:object];
}
}
-(void)changeFontOfView:(UIView *)aView
{
for (UIView *vv in [aView subviews])
{
if ([vv isKindOfClass:[UIButton class]])
{
UIButton *btn = (UIButton *)vv;
CGFloat fontSize = btn.titleLabel.font.pointSize;
btn.titleLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:fontSize];
}
else if ([vv isKindOfClass:[UILabel class]])
{
UILabel *lbl = (UILabel *)vv;
CGFloat fontSize = lbl.font.pointSize;
[lbl setFont:[UIFont fontWithName:#"Helvetica-Bold" size:fontSize]];
}
else if ([vv isKindOfClass:[UITextView class]])
{
UITextView *txt = (UITextView *)vv;
CGFloat fontSize = txt.font.pointSize;
[txt setFont:[UIFont fontWithName:#"Helvetica-Bold" size:fontSize]];
}
else if ([vv isKindOfClass:[UITextField class]])
{
UITextField *txt = (UITextField *)vv;
CGFloat fontSize = txt.font.pointSize;
[txt setFont:[UIFont fontWithName:#"Helvetica-Bold" size:fontSize]];
}
else if ([vv isKindOfClass:[UIView class]]||[vv isKindOfClass:[UIScrollView class]])
{
if (aView.subviews.count == 0)return;
[self changeFontOfView:vv];
}
}
}
Now your every viewController(RootViewController) will be extended from BaseViewController class like in RootViewController.h..
#import "BaseViewController.h"
#interface RootViewController : BaseViewController
{
}
And make sure that you have written following in your .m file of your UIViewController(RootViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
}
Please follow above steps carefully you will rock.......
How about this little recursive method on UIView?
#implementation UIView (JPCSetFont)
- (void)jpc_setAllFonts:(UIFont *)font
{
if ([self respondsToSelector:#selector(setFont:)]) {
UIFont *oldFont = [self valueForKey:#"font"];
UIFont *newFont = [font fontWithSize:oldFont.pointSize];
[self setValue:newFont forKey:#"font"];
}
for (UIView *subview in self.subviews) {
[subview jpc_setAllFonts:font];
}
}
#end
Code by John Cromartie is a good stuff. Simple and laconic.
Just keep in mind some fonts are bold or italic. So you probably have to pass 2 (or 3) params: for regular font (as it is now), for bold font and for the italic one. Besides you have to detect if the current font is bold, etc. So there is a bit enhanced piece of code below.
However, it can work wrong on iOS7 because user can set his own fonts and, as a result, weight / style detection won't work properly.
#implementation UIView (JPCSetFont)
- (void)jpc_setAllFonts:(UIFont*)regular bold:(UIFont*)bold
{
if ([self respondsToSelector:#selector(setFont:)]) {
UIFont *oldFont = [self valueForKey:#"font"];
UIFont *newFont;
// for iOS6
NSRange isBold = [[oldFont fontName] rangeOfString:#"Bold" options:NSCaseInsensitiveSearch];
// for iOS7 (is device owner didn't change it!)
NSRange isMedium = [[oldFont fontName] rangeOfString:#"MediumP4" options:NSCaseInsensitiveSearch];
if (isBold.location==NSNotFound && isMedium.location==NSNotFound) {
newFont = [regular fontWithSize:oldFont.pointSize];
} else {
newFont = [bold fontWithSize:oldFont.pointSize];
}
// TODO: there are italic fonts also though
[self setValue:newFont forKey:#"font"];
}
for (UIView *subview in self.subviews) {
[subview jpc_setAllFonts:regular bold:bold];
}
}
#end
https://github.com/iutinvg/ZZLib/blob/master/ZZLib/UIView%2BZZFontSetter.h
https://github.com/iutinvg/ZZLib/blob/master/ZZLib/UIView%2BZZFontSetter.m

If UITableview is empty show image

I am looking for 3 hours now on Google how to remove the tableview and show an image when the tableview is empty (have no more rows). Does someone know this? I know it's possible, because I saw it on many apps.
What I could find was:
// Check if table view has any cells
int sections = [self.tableView numberOfSections];
BOOL hasRows = NO;
for (int i = 0; i < sections; i++)
hasRows = ([self.tableView numberOfRowsInSection:i] > 0) ? YES : NO;
if (sections == 0 || hasRows == NO)
{
UIImage *image = [UIImage imageNamed:#"test.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
// Add image view on top of table view
[self.tableView addSubview:imageView];
// Set the background view of the table view
self.tableView.backgroundView = imageView;
}
where to put this?
Thanks!
If your using Storyboard just put your view behind your UITableView
If your array of data is empty when creating it, simply hide your UITableView to show the "empty table" view behind it.
[tableView setHidden:YES];
Please refer to:
http://www.unknownerror.org/Problem/index/905493327/how-do-i-display-a-placeholder-image-when-my-uitableview-has-no-data-yet/
Thanks to Cocoanut and Thomas:
#interface MyTableViewController : UITableViewController
{
BOOL hasAppeared;
BOOL scrollWasEnabled;
UIView *emptyOverlay;
}
- (void) reloadData;
- (void) checkEmpty;
#end
#implementation MyTableViewController
- (void)viewWillAppear:(BOOL)animated
{
[self reloadData];
[super viewWillAppear: animated];
}
- (void)viewDidAppear:(BOOL)animated
{
hasAppeared = YES;
[super viewDidAppear: animated];
[self checkEmpty];
}
- (void)viewDidUnload
{
if (emptyOverlay)
{
self.tableView.scrollEnabled = scrollWasEnabled;
[emptyOverlay removeFromSuperview];
emptyOverlay = nil;
}
}
- (UIView *)makeEmptyOverlayView
{
UIView *emptyView = [[[NSBundle mainBundle] loadNibNamed:#"myEmptyView" owner:self options:nil] objectAtIndex:0];
return emptyView;
}
- (void) reloadData
{
[self.tableView reloadData];
if (hasAppeared &&
[self respondsToSelector: #selector(makeEmptyOverlayView)])
[self checkEmpty];
}
- (void) checkEmpty
{
BOOL isEmpty = YES;
id<UITableViewDataSource> src = self.tableView.dataSource;
NSInteger sections(1);
if ([src respondsToSelector: #selector(numberOfSectionsInTableView:)])
sections = [src numberOfSectionsInTableView: self.tableView];
for (int i = 0; i < sections; ++i)
{
NSInteger rows = [src tableView: self.tableView numberOfRowsInSection: i];
if (rows)
isEmpty = NO;
}
if (!isEmpty != !emptyOverlay)
{
if (isEmpty)
{
scrollWasEnabled = self.tableView.scrollEnabled;
self.tableView.scrollEnabled = NO;
emptyOverlay = [self makeEmptyOverlayView];
[self.tableView addSubview: emptyOverlay];
}
else
{
self.tableView.scrollEnabled = scrollWasEnabled;
[emptyOverlay removeFromSuperview];
emptyOverlay = nil;
}
}
else if (isEmpty)
{
// Make sure it is still above all siblings.
[emptyOverlay removeFromSuperview];
[self.tableView addSubview: emptyOverlay];
}
}
#end
UITableView is a (non direct) subclass of UIView, so what you want to do is easy.
Say that you have this table view as subview of your view controller's view. This case you just create another view with the same frame as the table view, then you remove your table view from the superview, and add the newly created view as subview of your view controller's view. So simply:
UIImageView* view= [[UIImageView alloc] initWithImage: yourImage];
view.frame= tableView.frame;
[tableView removeFromSuperview];
[self.view addSubview: view];
Put it on - (void)viewDidAppear. Good luck ;)

Replace BookmarkButton with Activityindicator in UISearchBar

I use a UISearchBar for entering an address to establish a network connection. While the connection is made I want to show the activity indicator instead of the tiny BookmarkButton on the right side of the searchbar. As far as I can see there is no public declared property that would give me access to the correct subview of the searchbar. I have seen this been done, any thoughts?
How about replacing the search icon on the left side with an activity indicator while searches or connections are in progress?
SearchBarWithActivity.h:
#import <UIKit/UIKit.h>
#interface SearchBarWithActivity : UISearchBar
- (void)startActivity; // increments startCount and shows activity indicator
- (void)finishActivity; // decrements startCount and hides activity indicator if 0
#end
SearchBarWithActivity.m:
#import "SearchBarWithActivity.h"
#interface SearchBarWithActivity()
#property(nonatomic) UIActivityIndicatorView *activityIndicatorView;
#property(nonatomic) int startCount;
#end
#implementation SearchBarWithActivity
- (void)layoutSubviews {
UITextField *searchField = nil;
for(UIView* view in self.subviews){
if([view isKindOfClass:[UITextField class]]){
searchField= (UITextField *)view;
break;
}
}
if(searchField) {
if (!self.activityIndicatorView) {
UIActivityIndicatorView *taiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
taiv.center = CGPointMake(searchField.leftView.bounds.origin.x + searchField.leftView.bounds.size.width/2,
searchField.leftView.bounds.origin.y + searchField.leftView.bounds.size.height/2);
taiv.hidesWhenStopped = YES;
taiv.backgroundColor = [UIColor whiteColor];
self.activityIndicatorView = taiv;
[taiv release];
_startCount = 0;
[searchField.leftView addSubview:self.activityIndicatorView];
}
}
[super layoutSubviews];
}
- (void)startActivity {
self.startCount = self.startCount + 1;
}
- (void)finishActivity {
self.startCount = self.startCount - 1;
}
- (void)setStartCount:(int)startCount {
_startCount = startCount;
if (_startCount > 0)
[self.activityIndicatorView startAnimating];
else {
[self.activityIndicatorView stopAnimating];
}
}
#end
I updated the answer from #JohnLemberger to work with iOS 7 (note: I've only tested this on iOS 7), as well as a summary of my changes:
NOTE: this is not very robust code to begin with, since Apple can change the view hierarchy of UISearchBar in any release (as they did between iOS 6 and 7).
SearchBarWithActivity.h (nothing changed):
#interface SearchBarWithActivity : UISearchBar
- (void)startActivity; // increments startCount and shows activity indicator
- (void)finishActivity; // decrements startCount and hides activity indicator if 0
#end
#interface XXTreatmentHeaderViewController : XXViewController
#property (nonatomic, strong, readonly) SearchBarWithActivity *searchBar;
#end
SearchBarWithActivity.m:
1) Show/hide the "magnifying glass" icon when the activity indicator appears
2) Add depth in the view hierarchy search for the UITextField
#interface SearchBarWithActivity()
#property(nonatomic) UIActivityIndicatorView *activityIndicatorView;
#property(nonatomic) int startCount;
#end
#implementation SearchBarWithActivity
- (void)layoutSubviews {
UITextField *searchField = nil;
for(UIView* view in self.subviews){
// on iOS 6, the UITextField is one-level deep
if ([view isKindOfClass:[UITextField class]]){
searchField= (UITextField *)view;
break;
}
// on iOS 7, the UITextField is two-levels deep
for (UIView *secondLevelSubview in view.subviews) {
if([secondLevelSubview isKindOfClass:[UITextField class]]){
searchField= (UITextField *)secondLevelSubview;
break;
}
}
}
if(searchField) {
if (!self.activityIndicatorView) {
UIActivityIndicatorView *taiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
taiv.center = CGPointMake(searchField.leftView.bounds.origin.x + searchField.leftView.bounds.size.width/2,
searchField.leftView.bounds.origin.y + searchField.leftView.bounds.size.height/2);
taiv.hidesWhenStopped = YES;
self.activityIndicatorView = taiv;
_startCount = 0;
[searchField.leftView addSubview:self.activityIndicatorView];
}
}
[super layoutSubviews];
}
- (void)startActivity {
self.startCount = self.startCount + 1;
}
- (void)finishActivity {
self.startCount = self.startCount - 1;
}
- (void)setStartCount:(int)startCount {
_startCount = startCount;
if (_startCount > 0) {
[self.activityIndicatorView startAnimating];
// Remove the "magnifying glass icon"
[self setImage:[UIImage new] forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
} else {
[self.activityIndicatorView stopAnimating];
// Restore the "magnifying glass icon"
[self setImage:nil forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
}
}
#end
I have implemented a category for UISearchBar that shows a UIActivityIndicatorView, depending on state of a AFNetworking's request operation or session task https://gist.github.com/nguyenhuy/a11d15c11200477b05a6.
Just for the record:
for(UIView* view in self.subviews){
if([view isKindOfClass:[UITextField class]]){
searchField=view;
break;
}
}
if(searchField !=)) {
searchField.leftView = myCustomView;
}
You can subclass UISearchBar and call this code in the layoutSubview method. calling this code in layoutSubview makes sure that resize animations work properly.
I update jonsibley's answer by adding support for the cases where a UISearchBar is embedded in a UINavigationBar using the displaysSearchBarInNavigationBar flag.
SearchBarWithActivity.h (added a new property):
#interface SearchBarWithActivity : UISearchBar
- (void)startActivity; // increments startCount and shows activity indicator
- (void)finishActivity; // decrements startCount and hides activity indicator if 0
#property (nonatomic,assign) UINavigationItem *navigationItem;
#end
SearchBarWithActivity.m (get the searchField from the navigationItem if not nil):
#import "SearchBarWithActivity.h"
#interface SearchBarWithActivity()
#property(nonatomic) UIActivityIndicatorView *activityIndicatorView;
#property(nonatomic) int startCount;
#end
#implementation SearchBarWithActivity
#synthesize navigationItem;
- (void)layoutSubviews {
UITextField *searchField = nil;
if(self.navigationItem) {
searchField = (UITextField *)[self.navigationItem titleView];
} else {
for(UIView* view in self.subviews){
// on iOS 6, the UITextField is one-level deep
if ([view isKindOfClass:[UITextField class]]){
searchField= (UITextField *)view;
break;
}
// on iOS 7, the UITextField is two-levels deep
for (UIView *secondLevelSubview in view.subviews) {
if([secondLevelSubview isKindOfClass:[UITextField class]]){
searchField= (UITextField *)secondLevelSubview;
break;
}
}
}
}
if(searchField) {
if (!self.activityIndicatorView) {
UIActivityIndicatorView *taiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
taiv.center = CGPointMake(searchField.leftView.bounds.origin.x + searchField.leftView.bounds.size.width/2,
searchField.leftView.bounds.origin.y + searchField.leftView.bounds.size.height/2);
taiv.hidesWhenStopped = YES;
self.activityIndicatorView = taiv;
_startCount = 0;
[searchField.leftView addSubview:self.activityIndicatorView];
}
}
[super layoutSubviews];
}
- (void)startActivity {
self.startCount = self.startCount + 1;
}
- (void)finishActivity {
self.startCount = self.startCount - 1;
}
- (void)setStartCount:(int)startCount {
_startCount = startCount;
if (_startCount > 0) {
[self.activityIndicatorView startAnimating];
// Remove the "magnifying glass icon"
[self setImage:[UIImage new] forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
} else {
[self.activityIndicatorView stopAnimating];
// Restore the "magnifying glass icon"
[self setImage:nil forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
}
}
#end
In your ViewController:
#import "SearchBarWithActivity.h"
- (void)viewDidLoad
{
[super viewDidLoad];
// Embed the search bar into NavigationBar and setup the navigation item in order to show the spinner
[self.searchDisplayController setDisplaysSearchBarInNavigationBar:YES];
[(SearchBarWithActivity *)self.searchDisplayController.searchBar setNavigationItem:self.navigationItem];
}
I hope this saves somebody's time.
Since it seems like the depth of the UITextField keeps changing I figured I would add a recursive solution.
-(NSArray * ) findAllSubviewsForView:(UIView * ) view{
NSMutableArray * views = [[NSMutableArray alloc] init];
for(UIView * subview in view.subviews){
[views addObjectsFromArray:[self findAllSubviewsForView:subview]];
}
[views addObject:view];
return views;
}
You can use this array to find the UITextField,
UITextField * searchField = nil;
for(UIView * view in [self findAllSubviewsForView:self]){
if([view isKindOfClass:[UITextField class]]){
searchField = (UITextField *) view;
}
}