I'm trying to create a tableview with a searchbar inside the header view of the table. I'd like to use a searchDisplayController to manage everything.
I've created everything programmatically (I'm not feeling comfortable with IB) trying to set all the correct properties, but it seems that I'm missing something, because when the table shows up I'm not able to edit the text in the searchbar and see any animation.
Here is a part of the code:
- (void)viewDidLoad {
[super viewDidLoad];
UISearchBar *searchBarTMP=[[UISearchBar alloc]init];
self.searchBar=searchBarTMP;
[searchBarTMP release];
self.searchBar.autocapitalizationType=UITextAutocapitalizationTypeNone;
self.searchBar.delegate=self;
self.searchBar.showsScopeBar=YES;
self.searchBar.keyboardType=UIKeyboardTypeDefault;
self.searchBar.userInteractionEnabled=YES;
self.searchBar.multipleTouchEnabled=YES;
self.searchBar.scopeButtonTitles=[NSArray arrayWithObjects:NSLocalizedString(#"City",#"Scope City"),NSLocalizedString(#"Postal Code",#"Scope PostalCode"),nil];
self.tableView.tableHeaderView=searchBar;
self.searchBar.selectedScopeButtonIndex=0;
self.navigationItem.title=NSLocalizedString(#"Store",#"Table title");
//SearchDisplayController creation
UISearchDisplayController *searchDisplayControllerTMP = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchDisplayController=searchDisplayControllerTMP;
[searchDisplayControllerTMP release];
self.searchDisplayController.delegate=self;
self.searchDisplayController.searchResultsDelegate=self;
self.searchDisplayController.searchResultsDataSource=self;
//....continue
}
I know that when you use a searchbar alone you must deal with its delegate protocol, but I'm guessing that the searchDisplayController manage for you as seen in the Apple sample code. (build up with IB).
Any suggestion?
Thank you,
Andrea
Found it...
After putting in the header of the table view must write
[self.searchBar sizeToFit];
If you are using ARC, make sure you create an iVar for the UISearchDisplayController in your header file.
If you create an UISearchDisplayController using:
UISearchDisplayController* searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchField contentsController:self];
it will get released by ARC, it will not call any delegate methods and when you'll call self.searchDisplayController (the UIViewController's property) it will be nil.
So, the fix is:
In your header (.h) file:
#interface MenuViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {
UISearchDisplayController* searchDisplayController;
UISearchBar *searchField;
UITableView* tableView;
NSArray* searchResults;
}
and in the implementation (.m) file:
searchField = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 49)];
searchField.delegate = self;
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchField contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
searchDisplayController.searchResultsDelegate = self;
tableView.tableHeaderView = searchField;
tableView.contentOffset = CGPointMake(0, searchField.frame.size.height);
When implemented like that, you can call both self.searchDisplayController and searchDisplayController in the rest of your code.
Related
I have 2 UIViewControllers and I can pass values between them with no problem. The problem comes when I try to pass a value to a third view (a UITableViewController).
Here is my code
All the connections are set correctly, the tableview gets called here:
- (IBAction)goToTableView:(id)sender {
TableMovieViewController *vc=[self.storyboard instantiateViewControllerWithIdentifier:#"TableMovieViewController"];
vc.finalResult.text=stringFromFirstView;
[self presentViewController:vc animated:YES completion:nil];
}
The vc.finalResult.text has by example "Test Call", I verified and the value is there.
Now on the table view file:
.h file:
#property(strong, nonatomic)UITextField *finalResult;
.m file:
#synthesize finalResult;
- (void)viewDidLoad
{
[super viewDidLoad];
UINavigationBar *nav = [[UINavigationBar alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
UINavigationItem *navItem = [[UINavigationItem alloc] initWithTitle:finalResult];
[nav pushNavigationItem:navItem animated:FALSE];
[self.view addSubview:nav];
}
The finalResult never gets set with the value passed from the previous view.
Any ideas
Thanks
Rodrigo
Change:
UINavigationItem *navItem = [[UINavigationItem alloc] initWithTitle:finalResult];
With:
UINavigationItem *navItem = [[UINavigationItem alloc] initWithTitle:finalResult.text];
finalResult is of type UITextField and not NSString.
initWithTitle: asks for a NSString and not a UITextField as you are setting now.
EDIT:
As I read the edit that was meant as a comment on my answer you say that the UITextField *finalResult is a nil-value. This is probably because you did not allocate and ininitialise the UITextField as you should.
You need to do the finalResult = [[UITextField alloc] init]; at least BEFORE the place you set the text of it. I suggest you do it in the init function of the UITableViewController.
I am trying to implement the UIRefreshControl in my application. I have an xib file and I added a UITableViewController to the empty nib file and I set the refresh property to "enabled". Also I have added code to the viewDidLoad and a custom refresh method. The problem is I have an error I can't find any information on....in my viewDidLoad I get "Property 'refreshControl' not found on object of type ViewController"
- (void)viewDidLoad{
[super viewDidLoad];
self.myTableView =
[[UITableView alloc] initWithFrame:self.view.bounds
style:UITableViewStyleGrouped];
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
[self.view addSubview:self.myTableView];
UIRefreshControl *refresh = [[UIRefreshControl alloc] init];
refresh.attributedTitle = [[NSAttributedString alloc] initWithString:#"Pull to Refresh"];
[refresh addTarget:self action:#selector(refreshView:) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refresh;
}
-(void)refreshView:(UIRefreshControl *)refresh {
refresh.attributedTitle = [[NSAttributedString alloc] initWithString:#"Refreshing data..."];
// custom refresh logic would be placed here...
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMM d, h:mm a"];
NSString *lastUpdated = [NSString stringWithFormat:#"Last updated on %#",
[formatter stringFromDate:[NSDate date]]];
refresh.attributedTitle = [[NSAttributedString alloc] initWithString:lastUpdated];
[refresh endRefreshing];
}
I have no idea why that property isn't available....what am I missing?
Looks like I need to inherit from UITableViewController in my ViewController.h file. If I already have UITableView there how do I inherit from both? If I change my code from ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> to ViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> then I get an error:
error: NSInternalInconsistencyException',
reason: '-[UITableViewController loadView] loaded the "ViewController_iPhone" nib but didn't get a UITableView.'
You can add UIRefreshControl as a subview to your UITableView.
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.myTableView addSubview:refreshControl];
As per Dave's comment, this may break in future version of iOS. So please be careful while using this and try to raise a bug report to apple regarding this.
Update:
A better approach is by adding UITableViewController as a ChildViewController of self and then adding tableViewController.tableView as the subview of self.view. You dont have to do any hack to make it work in this way.
[self addChildViewController:tableViewController];
[self.view addSubview:tableViewController.tableView];
You can define the frame for tableView accordingly. Using this approach, UIRefreshControl should work in the same way as it works for UITableViewController.
`
Things to Remember:
UIRefreshControl only for UITableViewController, so your class should be the subclass of UITableViewController.
UITableViewController has a property refreshControl, you should allocate a UIRefreshControl
and set it to that property.
Ex:
UITableViewController *tableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refreshControlAction:) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = refreshControl;
All of these are complex ways of doing something simple.
You don't need to add a refresh control, or declare one in your viewController. Adding pull-to-refresh is a two-step process.
Step 1: In your storyboard, go to your tableViewController and, where it says "Refreshing", select "Enabled".
Step 2: Add the following code to your tableViewController.m file, in viewDidLoad:
[self.refreshControl addTarget:self
action:#selector(refresh)
forControlEvents:UIControlEventValueChanged];
That's the entire process, other than doing stuff in your -refresh method. When you want it to stop refreshing, call [self.refreshControl endRefreshing];.
Your ViewController class must be a subclass of UITableViewController in order to have access to the refreshControl property.
I would recommend you to make separate UITableViewController Subclass for myTableView.
And then by using addChildviewController or iOS6 ContainerView to add that class within original ViewController. That way even in the part of View, you can use UIRefreshControl.
Accepted answer is not official way, so it could break in future release as comment said...
I have a xib with a subview that I set to Class: S7GraphView (if I select it and click on the identity inspector)
When all I have in the (void) didViewLoad { ...
s7graphView = [[S7GraphView alloc] initWithFrame:[self.view bounds]];
//[self.view addSubview:s7graphView];
s7graphView.dataSource = self;
...
}
In the nib, I control-hold the file's owner to the subview and select - S7GraphView and it still shows up empty in the simulator.
If I comment all this out, I get a black subview (I do set the background to black) but not graph:
// s7graphView = [[S7GraphView alloc] initWithFrame:[self.view bounds]];
// //[self.view addSubview:s7graphView];
// s7graphView.dataSource = self;
// [s7graphView release];
If I remove the subview from my view and instead add it programmatically, it works when I add it like this:
s7graphView = [[S7GraphView alloc] initWithFrame:CGRectMake(20., 70., 280.0, 240.0 )];
[self.view addSubview:s7graphView];
s7graphView.dataSource = self; [s7graphView release];
I should be able to add it via the xib too, right? What do youz think I'm doing wrong?
Thanks!!
Have you implemented the delegate and the datasource?
I have a UIViewController which has a grouped UITableView as a property. I instantiate the UITableView in code and don't use IB. I would like to hook up a UISearchDisplayController to it but can't find any example how this could be done.
This what I have.
//Have implemented the UISearchDisplayDelegate in the header file
//SearchBar
UISearchBar *searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, 320, 45)];
searchBar.barStyle=UIBarStyleBlackTranslucent;
searchBar.showsCancelButton=NO;
searchBar.autocorrectionType=UITextAutocorrectionTypeNo;
searchBar.autocapitalizationType=UITextAutocapitalizationTypeNone;
searchBar.delegate=self;
UISearchDisplayController *mySearchDisplayController = [[UISearchDisplayController alloc ]initWithSearchBar:searchBar contentsController:self];
self.searchDisplayController = mySearchDisplayController; //Error here ?? self.searchDisplayController is ReadOnly and can't assign
[self.searchDisplayController setDelegate:self];
[self.searchDisplayController setSearchResultsDataSource:self];
[mySearchDisplayController release];
[myDisplayController release];
This doesn't seem to work, the searchDisplayController propery of the UIViewController seems to be readonly and I can't hook myDisplayController onto it. I'm really not sure if this the right way to do it.
I've been looking all around google to find some tip or tutorial on how to use a UISearchDisplayController in UIViewController. All the examples I could find was how to implement it into UITableViewController using IB, which is not the way I want to use it.
Can anyone explain how I could get this working ?
Here's the code that I use. Put this in viewDidLoad of a UIViewController that instantiates it's own UITableView. I think the part you're looking for is to add the search bar as the header view of the table view.
UISearchBar * theSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,320,40)]; // frame has no effect.
theSearchBar.delegate = self;
if ( !searchBarPlaceHolder ) {
searchBarPlaceHolder = #"What are you looking for?";
}
theSearchBar.placeholder = searchBarPlaceHolder;
theSearchBar.showsCancelButton = YES;
self.theTableView.tableHeaderView = theSearchBar;
UISearchDisplayController *searchCon = [[UISearchDisplayController alloc]
initWithSearchBar:theSearchBar
contentsController:self ];
self.searchController = searchCon;
[searchCon release];
searchController.delegate = self;
searchController.searchResultsDataSource = self;
searchController.searchResultsDelegate = self;
[searchController setActive:YES animated:YES];
[theSearchBar becomeFirstResponder];
See the Apple Docs:
#property(nonatomic, readonly, retain) UISearchDisplayController *searchDisplayController
Discussion: This property reflects the value of the
searchDisplayController outlet that you set in Interface Builder. If
you create your search display controller programmatically, this
property is set automatically by the search display controller when
it is initialized.
I have a viewcontroller which contains an instance variable containing a dictionary object with a bunch of data. The view is fairly complex and contains several subviews that i instantiate and embed from seperate view files(To avoid having a thousand lines of UI code in the actual viewcontroller) - But how do these subviews, which exists in their own files, get access to my dictionary object from the viewcontroller?
So when im editing the DescriptionView.m file - How do i get access to the contents of the locationData dictionary object from the ViewController?
Hope you understand what i mean.
Here's a snippet from the ViewController:
CaseViewController.h
#import "DescriptionView.h"
#interface CaseViewController : UIViewController {
NSDictionary *locationData;
DescriptionView *descriptionView;
}
#property (nonatomic, retain) NSDictionary *locationData;
#property (nonatomic, retain) DescriptionView *descriptionView;
#end
CaseViewController.m
- (void)loadView {
UIView *view = [[UIView alloc] init];
descriptionView = [[DescriptionView alloc] initWithFrame:CGRectMake(0, 130, 320, 237)];
descriptionView.hidden = NO;
[view addSubview:descriptionView];
self.view = view;
[view release];
}
Ideally you should never access any properties of viewcontroller from the view.
The main idea of MVC architecture is that viewcontroller tells it's views what to render and not vise versa.
So you just have to provide all the data that your view needs for rendering during it's initialization:
- (void)loadView {
UIView *view = [[UIView alloc] init];
descriptionView = [[DescriptionView alloc] initWithFrame:CGRectMake(0, 130, 320, 237) paramDict: self.locationData]; descriptionView.hidden = NO;
[view addSubview:descriptionView];
[descriptionView release]; // BTW add this line here (or in dealloc) or you'll have a leak
self.view = view; [view release];
}
If you need to update your view dynamically, then you should add some methods to your view and call them from viewcolnroller.
E.g.:
DescriptionView.m:
-(void) updateWithDict:(NSDictionary*) udict;
If you need to perform some actions when some button in DescriptionView is pressed (or any other user interaction) a good idea would be declaring a protocol like DescriptionViewDelegate (or smth like that):
-(void) descriptionViewButton1Pressed:(DescriptionView*) dview;
-(void) descriptionViewButton2Pressed:(DescriptionView*) dview;
then make your CaseViewController a delegate and implement that methods there.
The simpliest way to have a reference to its viewcontroller from a view is to extend UIView:
#interface MyView: UIView {
UIViewController *mViewController;
}
Then in loadView
MyView *view = [[MyView alloc] init];
view.mViewController = self;