Couple of months ago I asked a question to show the UISearchDisplayController and it worked like a charm but
Now I am presenting another controller after user taps on the searchResultsController's tableview cell and it selects a cell and hides the searchResultsController to show MapView but once you tap the search bar searchResultsController doesn't shows up but once you press cancel button and tap search bar again it starts working again.
Scenario is exactly the Apple's Maps app opening up favourite view along with recent view controller with segments.
Here's my code for UISearchControllerDelegate:
- (void)willPresentSearchController:(UISearchController *)searchController
{
mainViewController *__weak weakSelf=self;
dispatch_async(dispatch_get_main_queue(), ^{
mainViewController *__strong strongSelf = weakSelf;
strongSelf.searchController.searchResultsController.view.hidden = NO;
});
}
- (void)didPresentSearchController:(UISearchController *)searchController
{
self.searchController.searchResultsController.view.hidden = NO;
}
And here is my code for UISearchBarDelegate.
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
mainViewController *__weak weakSelf=self;
dispatch_async(dispatch_get_main_queue(), ^{
mainViewController *__strong strongSelf = weakSelf;
strongSelf.searchController.searchBar.showsCancelButton = YES;
});
self.searchController.searchResultsController.view.hidden = NO;
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar{
mainViewController *__weak weakSelf=self;
dispatch_async(dispatch_get_main_queue(), ^{
mainViewController *__strong strongSelf = weakSelf;
strongSelf.searchController.searchBar.showsCancelButton = NO;
});
return YES;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
mainViewController *__weak weakSelf=self;
dispatch_async(dispatch_get_main_queue(), ^{
mainViewController *__strong strongSelf = weakSelf;
strongSelf.searchController.searchResultsController.view.hidden = YES;
});
[searchBar resignFirstResponder];
}];
}
Related
I am trying to do some experiment.
- (IBAction)btn1Action:(id)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"popvc2id" sender:self];
});
NSLog(#"TAP");
}
When button will tap it will take 1 second to perform segue and when this button tapped again it will trigger segue twice, so two instance of ViewController will be created.
In instruments I can see two instances but one of them is leaked VC object.
Now what I am trying to do is
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
VC2 *vc2 = [segue destinationViewController];
[ary addObject:vc2];
if(ary.count > 1) {
VC2 *vc = (VC2*)ary[1];
vc = nil;
[ary removeObjectAtIndex:1];
}
[ary removeAllObjects];
NSLog(#"-> %#", vc2);
}
to keep a record of VC objects and try to destroy the second obj, so I can prevent memory leak.
But its not working, how to I can fix it?
If you want to cancel your previous request. My suggestion is using NSObject CancelPreviousRequest method
How to implement:
- (IBAction)btn1Action:(id)sender {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(delayedAction) object:nil];
[self performSelector:#selector(delayedAction) withObject:nil afterDelay:1];
}
-(void)delayedAction{
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"popvc2id" sender:self];
});
}
- (IBAction)btn1Action:(id)sender {
__block UIButton * btn = (UIButton*) sender;
btn.enabled = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"popvc2id" sender:self];
btn.enabled = YES;
});
NSLog(#"TAP");
}
Wrote by memory, may be compile errors here
Creating a search view, this is the code that i did for the main search view.
What happens is if i dont search/filter, the uisearchbar gets dismissed when segueing. but if i search/filter then the uisearchbar stays on the nav bar when seguing.
- (void)viewDidLoad {
[super viewDidLoad];
// There's no transition in our storyboard to our search results tableview or navigation controller
// so we'll have to grab it using the instantiateViewControllerWithIdentifier: method
UINavigationController *searchResultsController = [[self storyboard] instantiateViewControllerWithIdentifier:#"CompanySearchResultsNavigationController"];
// Our instance of UISearchController will use searchResults
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
// The searchcontroller's searchResultsUpdater property will contain our tableView.
self.searchController.searchResultsUpdater = self;
self.searchController.hidesNavigationBarDuringPresentation = NO;
// The searchBar contained in XCode's storyboard is a leftover from UISearchDisplayController.
// Don't use this. Instead, we'll create the searchBar programatically.
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.navigationItem.titleView = self.searchController.searchBar;
self.definesPresentationContext = YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.objects count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
CompanySearchTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"searchCell" forIndexPath:indexPath];
cell.productImageView.file = (PFFile *)object[#"profileImage"];
cell.productImageView.layer.cornerRadius = cell.productImageView.frame.size.width / 2;
cell.productImageView.clipsToBounds = YES;
[cell.productImageView loadInBackground];
cell.companyNameLabel.text = object[#"username"];
return cell;
}
#pragma mark - UISearchControllerDelegate & UISearchResultsDelegate
// Called when the search bar becomes first responder
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
// Set searchString equal to what's typed into the searchbar
NSString *searchString = self.searchController.searchBar.text;
[self updateFilteredContentForAirlineName:searchString];
// If searchResultsController
if (self.searchController.searchResultsController) {
UINavigationController *navController = (UINavigationController *)self.searchController.searchResultsController;
// Present SearchResultsTableViewController as the topViewController
CompanySearchResultsTableViewController *vc = (CompanySearchResultsTableViewController *)navController.topViewController;
// Update searchResults
vc.searchResults = self.searchResults;
// And reload the tableView with the new data
[vc.tableView reloadData];
}
}
// Update self.searchResults based on searchString, which is the argument in passed to this method
- (void)updateFilteredContentForAirlineName:(NSString *)companyName
{
if (companyName == nil) {
// If empty the search results are the same as the original data
self.searchResults = [self.objects mutableCopy];
} else {
NSMutableArray *searchResults = [[NSMutableArray alloc] init];
// Else if the airline's name is
for (PFObject *company in self.objects) {
if ([company[#"username"] containsString:companyName]) {
// NSString *str = [NSString stringWithFormat:#"%#", company[#"username"]];
// [searchResults addObject:str];
PFObject *searchedObject = company;
[searchResults addObject:searchedObject];
NSLog(#"Searched: %#",searchedObject[#"username"]);
}
self.searchResults = searchResults;
}
}
}
In your AirlineTableViewController, override [UIViewController prepareForSegue]:
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender {
[self.searchController setActive:NO];
}
I am using UISearchbar in tableview controller in storyboard.
And searchbar returnKeyType is UIReturnKeySearch.
Its working fine with iOS7 but returnKeyType is not working with iOS8.
in iOS8, return key appears every time in keyboard.
I tried to set returnkeytype in viewDidLoad method of controller too.
What I need to do to set returnKeyType = UIReturnKeySearch in iOS8?
I think you can go with your hard codded logic for right now.
I will update if I will get better solution for your problem.
-(void)viewDidLoad {
[self setReturnKeyTypeSearchForView:searchBar];
}
-(void)setReturnKeyTypeSearchForView:(UIView *)view
{
for (id subView in view.subviews) {
if ([subView isKindOfClass:[UITextField class]]) {
[subView setReturnKeyType:UIReturnKeySearch];
}
else {
[self setReturnKeyTypeSearchForView:subView];
}
}
if ([view isKindOfClass:[UITextField class]]) {
[(UITextField *)view setReturnKeyType:UIReturnKeySearch];
}
}
Try making IBOutlet of your SearchBar
#property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
and add the below line code to your viewDidLoad Method
// if u want Done return key and change accordingly.
_searchBar.returnKeyType = UIReturnKeyDone;
SearchViewController.h
//
#import <UIKit/UIKit.h>
#interface SearchViewController : UIViewController
<UISearchBarDelegate, UITableViewDataSource> {
NSMutableArray *tableData;
UIView *disableViewOverlay;
UITableView *theTableView;
UISearchBar *theSearchBar;
}
#property(retain) NSMutableArray *tableData;
#property(retain) UIView *disableViewOverlay;
#property (nonatomic, retain) IBOutlet UITableView *theTableView;
#property (nonatomic, retain) IBOutlet UISearchBar *theSearchBar;
- (void)searchBar:(UISearchBar *)searchBar activate:(BOOL) active;
#end
SearchViewController.m
//
#import "SearchViewController.h"
#implementation SearchViewController
#synthesize tableData;
#synthesize disableViewOverlay;
#synthesize theSearchBar;
#synthesize theTableView;
// Initialize tableData and disabledViewOverlay
- (void)viewDidLoad {
[super viewDidLoad];
self.tableData =[[NSMutableArray alloc]init];
self.disableViewOverlay = [[UIView alloc]
initWithFrame:CGRectMake(0.0f,44.0f,320.0f,416.0f)];
self.disableViewOverlay.backgroundColor=[UIColor blackColor];
self.disableViewOverlay.alpha = 0;
}
// Since this view is only for searching give the UISearchBar
// focus right away
- (void)viewDidAppear:(BOOL)animated {
[self.theSearchBar becomeFirstResponder];
[super viewDidAppear:animated];
}
#pragma mark -
#pragma mark UISearchBarDelegate Methods
- (void)searchBar:(UISearchBar *)searchBar
textDidChange:(NSString *)searchText {
// We don't want to do anything until the user clicks
// the 'Search' button.
// If you wanted to display results as the user types
// you would do that here.
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
// searchBarTextDidBeginEditing is called whenever
// focus is given to the UISearchBar
// call our activate method so that we can do some
// additional things when the UISearchBar shows.
[self searchBar:searchBar activate:YES];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
// searchBarTextDidEndEditing is fired whenever the
// UISearchBar loses focus
// We don't need to do anything here.
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
// Clear the search text
// Deactivate the UISearchBar
searchBar.text=#"";
[self searchBar:searchBar activate:NO];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// Do the search and show the results in tableview
// Deactivate the UISearchBar
// You'll probably want to do this on another thread
// SomeService is just a dummy class representing some
// api that you are using to do the search
NSArray *results = [SomeService doSearch:searchBar.text];
[self searchBar:searchBar activate:NO];
[self.tableData removeAllObjects];
[self.tableData addObjectsFromArray:results];
[self.theTableView reloadData];
}
// We call this when we want to activate/deactivate the UISearchBar
// Depending on active (YES/NO) we disable/enable selection and
// scrolling on the UITableView
// Show/Hide the UISearchBar Cancel button
// Fade the screen In/Out with the disableViewOverlay and
// simple Animations
- (void)searchBar:(UISearchBar *)searchBar activate:(BOOL) active{
self.theTableView.allowsSelection = !active;
self.theTableView.scrollEnabled = !active;
if (!active) {
[disableViewOverlay removeFromSuperview];
[searchBar resignFirstResponder];
} else {
self.disableViewOverlay.alpha = 0;
[self.view addSubview:self.disableViewOverlay];
[UIView beginAnimations:#"FadeIn" context:nil];
[UIView setAnimationDuration:0.5];
self.disableViewOverlay.alpha = 0.6;
[UIView commitAnimations];
// probably not needed if you have a details view since you
// will go there on selection
NSIndexPath *selected = [self.theTableView
indexPathForSelectedRow];
if (selected) {
[self.theTableView deselectRowAtIndexPath:selected
animated:NO];
}
}
[searchBar setShowsCancelButton:active animated:YES];
}
#pragma mark -
#pragma mark UITableViewDataSource Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"SearchResult";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
id *data = [self.tableData objectAtIndex:indexPath.row];
cell.textLabel.text = data.name;
return cell;
}
#pragma mark -
#pragma mark Memory Management Methods
- (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.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[theTableView release], theTableView = nil;
[theSearchBar release], theSearchBar = nil;
[tableData dealloc];
[disableViewOverlay dealloc];
[super dealloc];
}
#end
Building a SearchView with UISearchBar and UITableView
this might helps you :)
I'm not sure if I understood your question correctly. You want to have "search" button instead of "return" button, right? There is a new SearchController in ios 8, give it a try:
YourTableViewController.h
#interface YourTableViewController : UITableViewController<UISearchResultsUpdating>
#end
And now the implementation:
YourTableViewController.m
- (void)viewDidLoad {
// initializing with the same controller as presenting
UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
searchController.searchResultsUpdater = self;
searchController.searchBar.frame = CGRectMake(searchController.searchBar.frame.origin.x, searchController.searchBar.frame.origin.y, searchController.searchBar.frame.size.width, 44.0f);
searchController.dimsBackgroundDuringPresentation = NO;
searchController.searchBar.delegate = self;
searchController.searchBar.returnKeyType = UIReturnKeySearch; //should be search by default.. you can change to whatever you want.
// adding searchBar into HeaderView
self.tableView.tableHeaderView = searchController.searchBar;
// just to be able to present results on the same controller
self.definesPresentationContext = YES;
}
You also have to implement method from UISearchResultsUpdating protocol:
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
// you can leave it blank
}
EDIT: If it is not what you were looking for please comment, so I can update my answer accordingly
try this in viewDidLoad:
UITextField *txfSearchField = [yourSearchbar valueForKey:#"_searchField"];
if([txfSearchField conformsToProtocol:#protocol(UITextInputTraits)]) {
[txfSearchField setReturnKeyType:UIReturnKeyDefault];
}
I am seeing a bug where my app crashes if I click the back button in a navigation controller while editing a UISearchBar embedded as the titleView of the UINavigationBar. The main VC is a UITableViewController that is pushed onto the view stack using [parentView.navigationController pushViewController:myTableView animated:YES];
Here is the code I use to create the UISearchBar in my viewDidLoad:
UISearchBar *customSearch = [[UISearchBar alloc] initWithFrame:
CGRectMake(0,0, 320, 44)];
customSearch.delegate = self;
customSearch.placeholder = #"Some placeholder text";
self.navigationItem.titleView = customSearch;
These are my delegate implementations for the UISearchBar delegate - handle search just updates the array backing the tableView and calls [self.tableview reloadData]:
- (void) searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
searchBar.showsCancelButton = YES;
}
- (void) searchBarTextDidEndEditing:(UISearchBar *)searchBar {
searchBar.showsCancelButton = NO;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self handleSearch:searchBar];
[searchBar resignFirstResponder];
}
- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self handleSearch:searchBar];
}
- (void)handleSearch:(UISearchBar *)searchBar {
[self updateFilteredData:searchBar.text];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar {
searchBar.text = #"";
[self handleSearch:searchBar];
[searchBar resignFirstResponder];
}
I don't get any information from the crash - just a sigkill. If I'm not editing the UISearchBar it works fine. I've tried resigning the first responder and it still crashes.
Update - adding filtered data
- (void) updateFilteredData: (NSString *) nameFilter {
if (nameFilter.length) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(first_name CONTAINS[cd] %#) OR (last_name CONTAINS[cd] %#)", nameFilter, nameFilter];
self.filteredData = [self.data filteredArrayUsingPredicate:predicate];
} else {
self.filteredData = self.data;
}
[self.tableView reloadData];
}
I've tried all of the following + all of them together in viewWillDisappear. They all run successfully and the searchBar reference is to a valid UISearchBar.
-(void)viewWillDisappear:(BOOL)animated {
UISearchBar *mySearchBar = (UISearchBar *)self.navigationItem.titleView;
[mySearchBar resignFirstResponder];
mySearchBar.delegate = nil;
self.navigationItem.titleView = nil;
for (UIView *view in [mySearchBar subviews] ) {
[view removeFromSuperview];
}
[mySearchBar removeFromSuperview];
[super viewWillDisappear:animated];
}
It may not be a search bar thing, I just see the crash consistently when I'm editing the search bar - It could be something with the view hiding the keyboard and trying to redraw the cells below at the same time the TableView is being deconstructed.
I have 2 views
1) A
2) B
When I segue from view A to view B, it takes a long while to load, so I added an activity indicator in the segue.
My problem is, when I segue over to view B, my screen freezes(loads) in view A and only for a split second, it shows the activity indicator before going onto view B.
How do I make sure the activity indicator appears before it starts to load.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[self useActivityIndicator];
if ([[segue identifier] isEqualToString:#"ShowAdsDetail"])
{
//do anything that needs to be done
}
}
-(void)useActivityIndicator{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[activityView startAnimating];
subView.hidden = NO;
}
I had a similar issue with a UIActivityIndicator that was animating too fast. The segue was pushing the next view controller right away and barely showing the activity indicator.
In case this can help others - I was able to fix it by implementing performSelector:withObject:afterDelay and then, in the removeSpinner method, calling my performSegueWithIdentifier method.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES];
[self.spinner startAnimating];
[self performSelector:#selector(removeSpinner:) withObject:self.spinner afterDelay:2.0];
}
- (void)removeSpinner: (UIActivityIndicatorView *)spinner {
[self.spinner stopAnimating];
[self.spinner removeFromSuperview];
[self performSegueWithIdentifier:#"showMyPlans" sender:nil];
}
Just change your code like:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self useActivityIndicator];
});
if ([[segue identifier] isEqualToString:#"ShowAdsDetail"])
{
//do anything that needs to be done
}
}
-(void)useActivityIndicator
{
dispatch_async(dispatch_get_main_queue(),^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[activityView startAnimating];
subView.hidden = NO;
});
}