ive created coverflow effect using icarousel,but i've added two buttons not by programmatically in xib and i defined outlet and action and connected for that buttons..but those buttons are displayed as image and not working...Here is my code in .m file
#interface GoldViewController ()<UIActionSheetDelegate>
#property (nonatomic, assign) BOOL useButtons;
#end
#implementation GoldViewController
#synthesize carousel1;
-(IBAction)showhome:(id)sender{
ViewController *sec=[[ViewController alloc]initWithNibName:Nil bundle:Nil];
sec.modalTransitionStyle=UIModalTransitionStyleCoverVertical;
[self presentModalViewController:sec animated:YES];
}
-(IBAction)showback:(id)sender{
CollectionsViewController *sec=[[CollectionsViewController alloc]initWithNibName:Nil bundle:Nil];
sec.modalTransitionStyle=UIModalTransitionStyleCoverVertical;
[self presentModalViewController:sec animated:YES];
}
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
carousel1.type = iCarouselTypeCylinder;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
return NUMBER_OF_ITEMS;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
UIImage *buttonImage=[NSArray arrayWithObjects:[UIImage imageNamed:#"ban.jpg"],
[UIImage imageNamed:#"pen.jpg"],
[UIImage imageNamed:#"ear1.jpg"],
[UIImage imageNamed:#"neck.jpg"],
[UIImage imageNamed:#"brac.jpg"],
[UIImage imageNamed:#"rings.jpg"],nil];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, 250.0f, 320.0f);
[button setImage:(UIImage*)[buttonImage objectAtIndex:index] forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
return button;
}
- (CGFloat)carouselItemWidth:(iCarousel *)carousel
{
return ITEM_SPACING;
}
- (BOOL)carousel:(iCarousel *)_carousel shouldSelectItemAtIndex:(NSInteger)index
{
if (index == carousel1.currentItemIndex)
{
NSLog(#"Should select current item");
}
else
{
NSLog(#"Should select item number %i", index);
}
return YES;
}
- (void)carousel:(iCarousel *)_carousel didSelectItemAtIndex:(NSInteger)index
{
if (index == carousel1.currentItemIndex)
{
//note, this will only ever happen if useButtons == NO
//otherwise the button intercepts the tap event
NSLog(#"Did select current item");
}
else
{
NSLog(#"Did select item number %i", index);
}
}
#end
Make sure that the nib files owner is definitely GoldViewController. Check that you've definitely added the buttons to the nib as buttons and not images. Make sure touchupinside action on each relevant button is connected to the showhome and showback actions in the nib files owner.
If all of this is ok, its possible that when the view controllers view is being displayed, the iCarousel's view is overlaying your button views. (I seem to recall some issues with iCarousel not laying out the view at runtime to be in the same place as defined in the nib but don't remember details). To find out - log the frame of the buttons and of the iCarousel view in your viewWillAppear method.
Related
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;
}
How to integrate two images where one act as default and another as animating image when app launches..The animating image should not come again even that view loads again..it should come only when app launches like default.png image..The idea is to get two default images..How can i do that?..
Here is my code...
#interface ViewController ()<UIActionSheetDelegate>
#property (nonatomic, assign) BOOL useButtons;
#end
#implementation ViewController
#synthesize carousel,Chaintop;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
carousel.type = iCarouselTypeInvertedWheel ;
CGRect ChaintopFrame = Chaintop.frame;
ChaintopFrame.origin.y = self.view.bounds.size.height;
[UIView animateWithDuration:3
delay:1.0
options: UIViewAnimationCurveEaseOut
animations:^{
Chaintop.frame = ChaintopFrame;
//Chainbottom.frame = ChainbottomFrame;
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
[self.view addSubview: Chaintop];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
return NUMBER_OF_ITEMS;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
UIImage *buttonImage=[NSArray arrayWithObjects:[UIImage imageNamed:#"Cover_0.png"],
[UIImage imageNamed:#"Cover_1.png"],
[UIImage imageNamed:#"Cover_2.png"],
[UIImage imageNamed:#"Cover_3.png"],
[UIImage imageNamed:#"Cover_4.png"],
[UIImage imageNamed:#"Cover_5.png"],
[UIImage imageNamed:#"Cover_6.png"],
[UIImage imageNamed:#"Cover_7.png"],nil];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, 200.0f, 200.0f);
[button setImage:(UIImage*)[buttonImage objectAtIndex:index] forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
return button;
}
-(void)dealloc{
[Chaintop release];
}
- (BOOL)carousel:(iCarousel *)_carousel shouldSelectItemAtIndex:(NSInteger)index
{
if (index == carousel.currentItemIndex)
{
NSLog(#"Should select current item");
}
else
{
NSLog(#"Should select item number %i", index);
}
return YES;
}
- (void)carousel:(iCarousel *)_carousel didSelectItemAtIndex:(NSInteger)index
{
if (index == carousel.currentItemIndex)
{
//note, this will only ever happen if useButtons == NO
//otherwise the button intercepts the tap event
NSLog(#"Did select current item");
}
else
{
NSLog(#"Did select item number %i", index);
}
}
#end
In your APP DELEGATE.
Something like this should do the job:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIImageView *imageView = [[[UIImageView alloc] initWithFrame:self.window.bounds] autorelease];
UIImage *image = [UIImage imageNamed:#"NAMEOFYOURSPLASHSCREEN.png"];
imageView.image = image;
[self.window addSubview:imageView];
[self.window makeKeyAndVisible];
[self performSelector:#selector(remove1stSplash:) withObject:imageView afterDelay:5];
return YES;
}
- (void)remove1stSplash:(UIImageView *)1stView {
UIImageView *imageView = ...
[self.window addSubview:imageView];
[self performSelector:#selector(remove2ndSplash:) withObject:imageView afterDelay:5];
[1stView removeFromSuperView];
}
- (void)remove2ndSplash:(UIImageView *)2ndView {
[self.window addSubview:.....
[2ndView removeFromSuperView];
}
EDIT:
Link for a sample project:
Two Splash Screen display in iphone
My iPhone app is quite simple, however it errors out. My app delegate opens up this view controller:
#import "Launch.h"
#implementation Launch
- (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.
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(100, 100, 100, 100);
[button setTitle:#"Hello" forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
-(void)buttonPressed {
NSLog(#"Button Pressed!");
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
//image.image = img;
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
which is just a red screen with a button. When I click the button, the app errors out with no error message. Why?
here is my main.m method, and when the program errors out, it points to the "return" line and indicates an EXEC_BAD_ACCESS in a thread.
#import <UIKit/UIKit.h>
#import "testAppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([testAppDelegate class]));
}
}
The target class has been deallocated, it needs to be retained.
From the Apple docs:
addTarget:action:forControlEvents:
When you call this method, target is not retained.
You need to insure the class instance is retained.
Change it to:
#selector(buttonPressed:) //add the :
and use this:
-(IBAction)buttonPressed:(id)sender;
The button is asking for an action.
The iOS app I'm currently working on is tabbar-based, and one of the tab is a UITableViewController.
The thing is, when I open this tab with an empty datasource (for whatever reason), I'd like to bring another view, with some kind of message/image, instead of the blank view I get with the tableviewcontroller.
I tried something like that :
- (void)viewWillAppear:(BOOL)animated {
if ([myData count] == 0) {
if (!emptyView) {
emptyView = [[UIView alloc] initWithFrame:self.view.frame];
UILabel *emptyMsg = [[UILabel alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height / 2, self.view.frame.size.width, 20)];
emptyMsg.text = #"This is Empty !";
emptyMsg.textAlignment = UITextAlignmentCenter;
[emptyView addSubview:emptyMsg];
}
[self.view insertSubview:emptyView atIndex:0];
}
else {
if (emptyView != nil) { [emptyView removeFromSuperview]; emptyView = nil; }
[self.tableView reloadData];
[super viewWillAppear:animated];
}
}
With emptyView defined as an iVar in the view controller.
But It doesn't work as expected, and I can't find the reason :/
Could any of you give it a look and give me the proper way to do this kind of behavior ?
Thanks,
I solved this problem by adding a UIView in the tableview header with a frame size equal to tableview size and disallowing user interaction on the tableview:
UIView *emptyView = [[UIView alloc] initWithFrame:self.tableView.frame];
/* Customize your view here or load it from a NIB */
self.tableView.tableHeaderView = emptyView;
self.tableView.userInteractionEnabled = NO;
When you have data you just remove the header and reenable user interaction:
self.tableView.tableHeaderView = nil;
self.tableView.userInteractionEnabled = YES;
UITableViewController doesn't allow you to add subviews to it's view (the tableView).
You should make a UIViewController and add the UITableView yourself with your optional emptyView.
Don't forget to set the dataSource and the delegate!
Update : I've made a subclass of UIViewController to avoid mimics UITableViewController every time.
.h
//
// GCTableViewController.h
// GCLibrary
//
// Created by Guillaume Campagna on 10-06-17.
// Copyright 2010 LittleKiwi. All rights reserved.
//
#import <UIKit/UIKit.h>
//Subclass of UIViewController that mimicks the UITableViewController except that the tableView is a subview of self.view and allow change of the frame of the tableView
#interface GCTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, readonly) UITableView *tableView;
- (id) initWithStyle:(UITableViewStyle)style;
//Subclass if you want to change the type of tableView. The tableView will be automatically placed later
- (UITableView*) tableViewWithStyle:(UITableViewStyle) style;
#end
.m
//
// GCTableViewController.m
// GCLibrary
//
// Created by Guillaume Campagna on 10-06-17.
// Copyright 2010 LittleKiwi. All rights reserved.
//
#import "GCTableViewController.h"
#implementation GCTableViewController
#synthesize tableView;
- (id) initWithStyle:(UITableViewStyle) style {
if (self = [super initWithNibName:nil bundle:nil]) {
tableView = [[self tableViewWithStyle:style] retain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.tableView];
self.tableView.frame = self.view.bounds;
self.tableView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
}
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
#pragma mark TableView methods
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger) tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
return 0;
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return nil;
}
#pragma mark Getter
- (UITableView *) tableViewWithStyle:(UITableViewStyle)style {
return [[[UITableView alloc] initWithFrame:CGRectZero style:style] autorelease];
}
- (void)dealloc {
[tableView release];
tableView = nil;
[super dealloc];
}
#end
You could perhaps try setting the number of rows to zero. Then inserting the no results view as the header view.
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;
}
}