I am making something like facebook app's left panel search feature. I have two UITableViewControllers. I take a tableView from one of them and put it above another one, when user taps on UISearchBar to show search results. When I create this second UITableViewController, I tell, that it's separator style will be none in ViewDidLoad method:
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
And then, after I put it's tableView above the first UITableViewController's view, it is empty at first, since no data is yet entered to the UISearchBar. And there are no any separators. But then, when user enters any letter and table data has some records, the separator style returns to the default one (white lines).
So, for some reason, my second controller forgets, what should be the separator style and displays after the reload of table view separators again. What can be the reason?
Edit:
This is how I create my second UITableViewController and take it's tableView to put on the first controller's view:
- (UIView *)searchResultsView
{
if (! _searchResultsView) {
_searchResultsView = self.searchResultsViewController.tableView;
_searchResultsView.frame = CGRectMake(0, 44, 320, self.view.frame.size.height-44);
}
return _searchResultsView;
}
- (SearchResultsViewController *)searchResultsViewController
{
if (! _searchResultsViewController) {
_searchResultsViewController = [[SearchResultsViewController alloc] init];
_searchResultsViewController.searchBar = _searchBar;
}
return _searchResultsViewController;
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
[self.view addSubview:self.searchResultsView];
}
Screenshot:
This is how it looks, when there is no data for table view:
As you see, there are no separators.
This is how it looks, when there is data. I have deleted cell contents. As you see, there are white separators.
The problem was that I used UISearchController and it creates it's own tableView. So I had even 3 tableViews there. After I removed UISearchController and it's delegate methods, everything started working. Now I take a string from UISearchBar and transfer it to my second UITableViewController. UISearchController is not needed there.
- (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
NSString *searchString = [_searchBar.text stringByReplacingCharactersInRange:range withString:text];
self.searchResultsViewController.searchString = searchString;
return YES;
}
And I use custom setter for searchString in second UITableViewController:
- (void)setSearchString:(NSString *)searchString
{
_searchString = searchString;
[self filterContentForSearchText:searchString];
[self.tableView reloadData];
}
Try with self.tableView.separatorColor = [UIColor clearColor];
Related
If I have multiple UITableViews, and a UISearchBar with UISearchDisplayController, in the UITableView delegate and datasource methods, how do I get a reference to the UITableView for the Search? Thanks.
get a reference to your UISearchDisplayController either via an outlet connected to your NIB file or when you create the searchBar programmatically. Once you have the reference, in all of your datasource and delegate methods you can just check to see which tableView is being passed and act appropriately. For example:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (aTableView==self.searchDisplayController.searchResultsTableView) {
// do something with the search data source
self.searchDisplayController.active=NO;
}else{
// do something with the regular data Source
}
// present a view or other response to the selected row
}
here is how you might create the search bar and get the reference to the UISearchDisplayController
-(UISearchBar *)searchBar{
// Create a search bar
if (searchBar==nil){
UISearchBar *aSearchBar = [[[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)] autorelease];
aSearchBar.autocorrectionType = UITextAutocorrectionTypeNo;
aSearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
aSearchBar.keyboardType = UIKeyboardTypeAlphabet;
aSearchBar.barStyle=UIBarStyleBlack;
self.searchBar=aSearchBar;
// Create the search display controller
UISearchDisplayController *aSearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self] ;
aSearchDisplayController.searchResultsDataSource = self;
aSearchDisplayController.searchResultsDelegate = self;
aSearchDisplayController.delegate=self;
self.searchDisplayController=aSearchDisplayController;
[aSearchDisplayController release];
}
return searchBar;
}
then add it to your tableView like this:
self.tableView.tableHeaderView = self.searchBar;
Good luck
I have a UITableView in a UINavigationController that contains a rightBarButtonItem which should hide the UITableView and show an MKMapView instead. The button seems to work great: it hides the UITableView, and shows the MKMapView. However, this MKMapView is empty. As in, completely white. I've tried to use a UILabel (just for testing purposes), and that doesn't appear either, so the problem must occur when I add the MKMapView (and UILabel) to the view hierarchy. Some relevant code:
- (void)viewDidLoad {
[super viewDidLoad];
self.mapView = [[MKMapView alloc] init];
mapView.hidden = YES;
[self.view addSubview:self.mapView];
// Some other stuff, table set up, etc.
}
That is the viewDidLoad of a class that inherits from UITableViewController. Now, I use the following method that gets called when tapped on the rightBarButtonItem of the UINavigationController:
- (void) toggleView {
if (self.mapView.isHidden) {
self.mapView.hidden = NO;
self.tableView.hidden = YES;
self.viewButton.title = #"List";
}
else {
self.mapView.hidden = YES;
self.tableView.hidden = NO;
self.viewButton.title = #"Map";
}
}
I am certain that function gets called, I have checked using NSLog. Also, the UITableView correctly disappears, and, I assume, the MKMapView (or whatever other UIView object for that matter) appears, but is empty/completely white. Does anybody see why I'm not seeing maps when trying to switch to Map View?
You should give it a size and position.
CGSize size = self.view.frame.size;
self.mapView =[[MKMapView alloc] initWithFrame:CGRectMake(0,0, size.width, size.height)];
To check the size in the console, add the following line in your toogleview method:
NSLog(#"%#", self.mapView);
I have this in my table view controller, and i want to pass the cell's text value to my UITextField on the modal view.
- (void)buttonTapped:(id)sender {
AddName *addName = [[AddName alloc] init];
UITableViewCell *cell = (UITableViewCell*)[sender superview];
NSLog(#"text in buttontapped: %#", cell.textLabel.text);
addName.nameField.text = cell.textLabel.text;
addName.delegate = self;
[self presentModalViewController:addName animated:YES];
}
The NSLog shows the correct cell text value.
On my modal view's viewDidLoad method i have this... and the text value never gets set...
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"text: %#",nameField.text);
nameField.autocapitalizationType = UITextAutocapitalizationTypeWords;
nameField.autocorrectionType = UITextAutocorrectionTypeDefault;
[nameField becomeFirstResponder];
}
What's going on?
Thanks.
It looks as though you're assuming that the modal view controller has already loaded its view, since I'm assuming nameField is a subview. But clearly at the point that your code is trying to set its text property, the text field and its parent view haven't been loaded yet, so you're sending a -setText: message to nil.
Instead, add a property to your modal view controller of type NSString *, and set that in your buttonTapped implementation. Then, in the modal view controller's -viewWillAppear method, you can take the value from the property and put it into the text field.
I have a UITableViewController that I have specified as a UISearchBarDelegate. Up until now, I had programmatically added the UISearchBar to the headerView of the table, and there were no problems.
I began to run out of screen real estate, so I decided to kill my normal UINavigationController title (which was text), and added the following code, moving my SearchBar from the table to the UINavigationBar:
// (Called in viewDidLoad)
// Programmatically make UISearchBar
UISearchBar *tmpSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,320,45)];
tmpSearchBar.delegate = self;
tmpSearchBar.showsCancelButton = YES;
tmpSearchBar.autocorrectionType = UITextAutocorrectionTypeNo;
tmpSearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
[self set_searchBar:tmpSearchBar];
[tmpSearchBar release];
self.navigationItem.titleView = [self _searchBar];
This code works as expected - my UINavigationBar is now a UISearchBar.
However, my delegate method:
/** Only show the cancel button when the keyboard is displayed */
- (void) searchBarDidBeginEditing:(UISearchBar*) lclSearchBar
{
lclSearchBar.showsCancelButton = YES;
}
...is no longer being called. I've breakpointed, and I've confirmed that the UISearchBar's delegate is indeed self, the view controller. Oddly, this delegate method is still called just fine:
/** Run the search and resign the keyboard */
- (void) searchBarSearchButtonClicked:(UISearchBar *)lclSearchBar
{
_deepSearchRan = NO;
[self runSearchForString:[[self _searchBar] text] isSlowSearch:NO];
[lclSearchBar resignFirstResponder];
}
Any ideas why UINavigationBar is swallowing my delegate calls?? What am I missing?
I think you write the wrong method signature. It should be : – searchBarTextDidBeginEditing:
Here is all the UISearchBarDelegate methods for text editing.
– searchBar:textDidChange:
– searchBar:shouldChangeTextInRange:replacementText:
– searchBarShouldBeginEditing:
– searchBarTextDidBeginEditing:
– searchBarShouldEndEditing:
– searchBarTextDidEndEditing:
UISearchBarDelegate
I have an application that, on load, displays a UITableView with the user's data in it.
However, when the user first loads the application (before they've created any data), I'd like to display, instead of an empty table, a background image (with an arrow pointing to the 'add a record' navbar button). Once the user has added their first record, the tableview is displayed instead. I've seen numerous apps do this - the only example I can think of at present is Cha-Ching, before you have any budgets/etc set up. I can't for the life of me work out how to do this, though.
I initially added a navigationcontroller to the main window's xib, the rootViewController of which was a custom viewcontroller/xib. This rootViewController contained the background image with a hidden tableview above it, and a custom tableviewcontroller that managed the tableview. This seemed to work just fine, and if there was data it would load and display in the table. However, if I was to scroll the data offscreen, the app would crash, with this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'*** -[UITextEffectsWindow tableView:cellForRowAtIndexPath:]: unrecognized selector sent to instance 0xd2d130'
I have no clue what a UITextEffectsWindow is, or why it was trying to manage my tableview. I presume something may be hooked up incorrectly in my view hierarchy...
If there's a much simpler/more straightforward way of doing this, I'd be very grateful if someone could explain it. How would you do this?
Thanks in advance.
Here's one solution that I've been satisfied with so far.
First, I created a transparent view that was the same size as my TableView. I add this view as a sibling of the TableView whenever the TableView's data source has no data in it. I completely remove the view when data is available because transparency can affect the smoothness of the scrolling animation with TableViews.
I simply added a transparent label to this view that says something to the effect of "No Data Available". Adding a little shadowing to this label really helped to reinforce the concept that the label was 'floating' over top of the empty TableView.
I like your method of using an image though. Done properly, it might save you some work if you don't have to localize a string like I currently do.
To achieve this using a UITableViewController subclass as my only view (within a UINavigationController as per the Apple template) I used the following approach:
Create a UIView of the size of my tableView in the XIB that contains your UITableViewController and tableView.
Add an ImageView set with my placeholder image to the UIView.
Wire up the UIView as an IBOutlet (in the example code below, I called it emptyTableView)
When it is time to show the placeholder from within the UITableViewController subclass :
[self.tableView addSubView:emptyTableView];
[self.tableView setScrollEnabled:FALSE];
Disabling the scroll is necessary otherwise the user will be able to move the placeholder image up and down. Just remember to enable it once the user adds an item.
To remove the image view
[emptyTableView removeFromSuperview];
To do this, I use the following controller instead of UITableViewController. It will automatically place a view over the table when it is empty, and remove it when it is filled.
Just call [self reloadData] instead of [self.tableView reloadData] so that it can check if the table became empty.
In your subclass, implement a makeEmptyOverlayView function that will create the view to show over an empty table.
#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;
}
}
- (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];
[emptyOverlay release];
}
else
{
self.tableView.scrollEnabled = scrollWasEnabled;
[emptyOverlay removeFromSuperview];
emptyOverlay = nil;
}
}
else if (isEmpty)
{
// Make sure it is still above all siblings.
[emptyOverlay retain];
[emptyOverlay removeFromSuperview];
[self.tableView addSubview: emptyOverlay];
[emptyOverlay release];
}
}
#end
If you use Three20, you can easily set any image you want as a place holder prior to your table being populated.
So, to solve this I did as discussed in the comments above:
I created a normal UIViewController subclass, which contained a UIImageView and a UITableView. The viewController conforms to the UITableViewDelegate and UITableViewDatasource protocols, and looks after the tableView. The viewController class simply shows or hides the imageView depending on whether data is available.
I was going wrong before by trying to manage both these views with a UITableViewController. A UITableViewController has to have a tableView as its view, whereas, with this solution, a viewController can contain both the image and the tableView, and implement the necessary protocols to manage the tableView.
Thanks for all the help!