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?
-
where you have seen this been done? – Rahul Vyas Sep 26 '09 at 12:36
-
The app "Wikipanion"(free) does it. – Monobono Oct 02 '09 at 18:30
-
is this app native iphone app or a web app? – Rahul Vyas Oct 03 '09 at 09:14
-
see my answer here http://stackoverflow.com/questions/5109272/change-search-magnifying-glass-to-uiactivityindicatorview – abuharsky Jul 31 '12 at 10:40
6 Answers
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

- 2,689
- 26
- 25
-
1This didn't quite work for me - my UISearchBar only had one subview (another UIView), so I had to use a recursive (breadth-first) search to find the UITextField. Not sure if this is because the component has changed, or whether I'm just doing something wrong. But thanks for pointing me in the right direction. – Joe Freeman Dec 22 '13 at 09:30
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

- 1
- 1

- 8,050
- 5
- 32
- 51
-
Unfortunately this doesn't work on iOS 7 when setDisplaysSearchBarInNavigationBar is true. I am not sure the searchField is accessible in this case. – Gerhat Feb 16 '14 at 00:17
-
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.

- 454
- 2
- 9
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.

- 780
- 10
- 24
-
Oh please use for-in loop `for(UIView* view in self.subviews){if([view isKindOfClass:[UITextField class]]){searchField=view;break;}}`. – kennytm Apr 26 '10 at 19:27
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;
}
}

- 5,889
- 3
- 27
- 51