UISearchController的搜索栏在推出新视图控制器时不会消失

10

我在一个包含UITableViewUIViewcontroller中使用UISearchController,我在viewDidLoad中实现:

    self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
    self.searchController.delegate = self;
    self.searchController.searchResultsUpdater = self;
    self.searchController.searchBar.delegate = self;
    self.searchController.dimsBackgroundDuringPresentation = NO;
    self.searchController.hidesNavigationBarDuringPresentation = NO;
    self.definesPresentationContext = NO;

当我点击导航栏中的按钮时,我会执行以下操作:

    self.tableView.contentOffset = CGPointMake(0, 0 - self.tableView.contentInset.top);
    self.tableView.tableHeaderView = self.searchController.searchBar;
    [self.searchController.searchBar becomeFirstResponder];

一切工作正常,但当我从UITableView中的行推送一个UIViewController时,UISearchBar会停留在那里,并且还会显示在其他视图的内容上,当我推出一个视图时如何让它消失,并在返回查看UITableView结果时再次显示?

谢谢

编辑:

这是didSelectRowAtIndexPath方法的代码:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
    DetailListController *detailList = [[DetailListController alloc] init];
    [self.navigationController pushViewController:detailList animated:YES];
}

1
你能展示一下 didSelectRowAtIndexPath: 的代码吗?在那里你是如何推出新的视图控制器的。 - Abhinav
尝试文档中提供的代码。它可以正确处理所有的代码。 - Teja Nandamuri
你们是在使用iOS 8还是iOS 9? - Teja Nandamuri
@Mr.T 不,我没有使用任何UIStateRestoration。 - Piero
我已经发布了代码,你可以尝试一下。如果对你来说不起作用,你可以从https://developer.apple.com/library/prerelease/ios/samplecode/TableSearch_UISearchController/TableSearchwithUISearchController.zip下载模板。 - Teja Nandamuri
显示剩余4条评论
4个回答

11

把这段代码放在你的viewDidLoad方法中:

self.definesPresentationContext = YES;

这是正确的答案。如果您不这样做,搜索控制器将向上遍历层次结构以查找演示文稿上下文,如果您不这样做,它将从导航控制器而不是您的视图控制器中呈现。 - Ben Scheirman
这行神奇的代码也可以修复一些与UISearchController相关的其他错误。它还可以通过界面生成器中的视图控制器设置(“Defines Context”复选框)进行设置。在此答案中查看有关此微小设置的更多内容:https://dev59.com/QF8e5IYBdhLWcg3wucRT#34500398 - Vitalii
1
在我的特定设置中,我能够通过这两行神奇的代码解决所有搜索控制器的错误(iOS 10和11):self.definesPresentationContext = trueself.extendedLayoutIncludesOpaqueBars = true - Vitalii
在这种情况下,搜索栏根本没有展开,我可以输入搜索内容,但是我看不到任何输入文本字段。 - user924

6

当你从DetailListController返回到你的视图控制器时,需要调用此方法(为了安全起见,将其封装在主线程中):

dispatch_async(dispatch_get_main_queue(), ^{
    self.searchController.active = NO;
});

您还可以在当前视图控制器的 viewWillDisappear: 方法中调用此方法。

什么时候我回来?当我推视图时,我不需要做任何事情吗? - Piero
OP正在使用UISearchController而不是UISearchDisplayController。你可以编辑你的回答@Abhinav。 - Teja Nandamuri
searchdisplay在iOS 8中已被弃用。 - Teja Nandamuri
我认为这不是正确的解决方案,我想要搜索栏,并在我回来时呈现它,而且搜索仍然处于活动状态,如果我将活动属性设置为NO,当我回来时就会失去我的搜索,我认为问题在self.definesPresentationContext中,因为如果我将其设置为YES,搜索栏会出现和消失,但它是在uitableview的中间而不是在标题中呈现的。 - Piero
这个对我有用。 - devjme

2

请试用苹果建议的标准方式:

声明属性:

@interface APLMainTableViewController () <UISearchBarDelegate, UISearchControllerDelegate, UISearchResultsUpdating>

@property (nonatomic, strong) UISearchController *searchController;

// our secondary search results table view
@property (nonatomic, strong) APLResultsTableController *resultsTableController;

// for state restoration
@property BOOL searchControllerWasActive;
@property BOOL searchControllerSearchFieldWasFirstResponder;

@end



    - (void)viewDidLoad {
    [super viewDidLoad];

    _resultsTableController = [[APLResultsTableController alloc] init];
    _searchController = [[UISearchController alloc] initWithSearchResultsController:self.resultsTableController];
    self.searchController.searchResultsUpdater = self;
    [self.searchController.searchBar sizeToFit];
    self.tableView.tableHeaderView = self.searchController.searchBar;

    // we want to be the delegate for our filtered table so didSelectRowAtIndexPath is called for both tables
    self.resultsTableController.tableView.delegate = self;
    self.searchController.delegate = self;
    self.searchController.dimsBackgroundDuringPresentation = NO; // default is YES
    self.searchController.searchBar.delegate = self; // so we can monitor text changes + others

    // Search is now just presenting a view controller. As such, normal view controller
    // presentation semantics apply. Namely that presentation will walk up the view controller
    // hierarchy until it finds the root view controller or one that defines a presentation context.
    //
    self.definesPresentationContext = YES;  // know where you want UISearchController to be displayed
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // restore the searchController's active state
    if (self.searchControllerWasActive) {
        self.searchController.active = self.searchControllerWasActive;
        _searchControllerWasActive = NO;

        if (self.searchControllerSearchFieldWasFirstResponder) {
            [self.searchController.searchBar becomeFirstResponder];
            _searchControllerSearchFieldWasFirstResponder = NO;
        }
    }
}


#pragma mark - UISearchBarDelegate

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [searchBar resignFirstResponder];
}


#pragma mark - UISearchControllerDelegate

// Called after the search controller's search bar has agreed to begin editing or when
// 'active' is set to YES.
// If you choose not to present the controller yourself or do not implement this method,
// a default presentation is performed on your behalf.
//
// Implement this method if the default presentation is not adequate for your purposes.
//
- (void)presentSearchController:(UISearchController *)searchController {

}

- (void)willPresentSearchController:(UISearchController *)searchController {
    // do something before the search controller is presented
}

- (void)didPresentSearchController:(UISearchController *)searchController {
    // do something after the search controller is presented
}

- (void)willDismissSearchController:(UISearchController *)searchController {
    // do something before the search controller is dismissed
}

- (void)didDismissSearchController:(UISearchController *)searchController {
    // do something after the search controller is dismissed
}

接下来是有趣的部分:使用以下代码在从详细视图返回时恢复状态

#pragma mark - UIStateRestoration

// we restore several items for state restoration:
//  1) Search controller's active state,
//  2) search text,
//  3) first responder

NSString *const ViewControllerTitleKey = @"ViewControllerTitleKey";
NSString *const SearchControllerIsActiveKey = @"SearchControllerIsActiveKey";
NSString *const SearchBarTextKey = @"SearchBarTextKey";
NSString *const SearchBarIsFirstResponderKey = @"SearchBarIsFirstResponderKey";

- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
    [super encodeRestorableStateWithCoder:coder];

    // encode the view state so it can be restored later

    // encode the title
    [coder encodeObject:self.title forKey:ViewControllerTitleKey];

    UISearchController *searchController = self.searchController;

    // encode the search controller's active state
    BOOL searchDisplayControllerIsActive = searchController.isActive;
    [coder encodeBool:searchDisplayControllerIsActive forKey:SearchControllerIsActiveKey];

    // encode the first responser status
    if (searchDisplayControllerIsActive) {
        [coder encodeBool:[searchController.searchBar isFirstResponder] forKey:SearchBarIsFirstResponderKey];
    }

    // encode the search bar text
    [coder encodeObject:searchController.searchBar.text forKey:SearchBarTextKey];
}

- (void)decodeRestorableStateWithCoder:(NSCoder *)coder {
    [super decodeRestorableStateWithCoder:coder];

    // restore the title
    self.title = [coder decodeObjectForKey:ViewControllerTitleKey];

    // restore the active state:
    // we can't make the searchController active here since it's not part of the view
    // hierarchy yet, instead we do it in viewWillAppear
    //
    _searchControllerWasActive = [coder decodeBoolForKey:SearchControllerIsActiveKey];

    // restore the first responder status:
    // we can't make the searchController first responder here since it's not part of the view
    // hierarchy yet, instead we do it in viewWillAppear
    //
    _searchControllerSearchFieldWasFirstResponder = [coder decodeBoolForKey:SearchBarIsFirstResponderKey];

    // restore the text in the search field
    self.searchController.searchBar.text = [coder decodeObjectForKey:SearchBarTextKey];
}

@end

-1

在尝试解决同样的问题几天后,我意识到正确的做法是拥有一个包含搜索层级中所有内容的导航控制器

像这样:

  • 导航1(没有导航栏)
  • (....选项卡栏,其他任何东西...)
  • 导航2(有导航栏,此导航栏被搜索栏替换)
  • 表格控制器

当您推出详细信息控制器时,将其推入导航1中,同时显示其导航栏。

这样可以保持搜索堆栈不变,并在详细页面上单击“返回”时准备好工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接