【问题标题】:UISearchDisplayController - how to preload searchResultTableViewUISearchDisplayController - 如何预加载 searchResultTableView
【发布时间】:2011-06-03 09:14:41
【问题描述】:

我想在用户点击搜索栏时显示一些默认内容,但在输入任何文本之前。

我有一个使用 settext 的解决方案:

- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {

   [searchDisplayController.searchBar setText:@" "];

}

这可行,但它并不优雅,并且搜索栏中的提示文本消失了。

有没有更好的方法将数据预加载到 UISearchDisplayController 中的 SearchResultTableView 中,而不必自己在自定义控制器中实现整个 UISearch 功能?

要获得所需效果的演示,请查看 Safari 的搜索框,如果您点击它,搜索界面将打开并显示以前的搜索。

【问题讨论】:

  • 3 班轮!当然,在我弄清楚那个 UISearchDisplayController 发生了什么之前,它大约有 40 行和大量阅读。好问题,我喜欢这样的小挑战来学习 iOS。
  • 看这个链接stackoverflow.com/a/21344947/747719这是一个带回车的解决方案。

标签: ios cocoa-touch uisearchbar uisearchdisplaycontroller


【解决方案1】:

好的,我知道了。用一些数据预加载你的 dataSource 并执行以下 hack(没有违法),你会得到 tableView 显示。请注意,可能需要进行一些清理工作,但这会显示您的默认数据。

在视图控制器的 viewDidLoad 中,拥有 UISearchBar:

[super viewDidLoad];
[self.searchDisplayController.searchResultsTableView reloadData];
// whatever you named your search bar - mine is property named searchBar
CGRect testFrame = CGRectMake(0, 20, self.searchBar.frame.size.width, 100);
self.searchDisplayController.searchResultsTableView.frame = testFrame;
[self.searchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];

这是发生了什么:
在您开始编辑之前,UISearchBar 不想显示 searchResultsTableView。如果您触摸 tableView(例如 reloadData),它将加载并在那里,以 frame = CGRectZero 坐在内存中。你给它一个适当的框架,然后将它添加到 searchBar 的超级视图中,等等。

我通过显示 searchBar 的超级视图和 tableView 的超级视图来验证这是正确的, 适当加载 tableView - 它们具有相同的超级视图。于是,我早早的回去把tableView加到superview中,果然出现了。对我来说,它显示为暗淡(可能上面的另一个视图?alpha 设置较低?),但仍然可以点击。我没有进一步说明,但这绝对可以让您在没有任何用户交互的情况下显示您的 tableView。

享受,

达米安

【讨论】:

  • 这对我不起作用。我已经通过故事板很好地放置了我的搜索栏。我不想再次定位它。当我点击搜索栏时,没有加载数据。
  • 这不是一个简单的问题 - 如果您通读“这是发生了什么”,您就会明白为什么添加子视图对于它的工作是必要的。我也没有验证 ios6 或 7 中的行为 - 现在可能有更好的方法来做到这一点。祝你好运!
  • 在 iOS 8 和之后你需要做 self.view.superview addSubview ... 而不是使用搜索栏。然后你需要用状态栏调整tabview的大小,如下所示:CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height+ [UIApplication sharedApplication].statusBarFrame.size.height, self.notesSearchBar.frame。 size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
【解决方案2】:

我为这个问题找到了一个好多更好的解决方案,而且它似乎在 iOS 6 和 7 上完美运行。虽然它仍然是一个 hack,但它比上面的更干净和面向未来的 hack .其他解决方案无法始终如一地工作,并阻止某些 UISearchDisplayDelegate 方法触发!此外,我遇到了复杂的嵌入问题,我无法用上述方法解决。其他解决方案的主要问题是它们严重混淆了 UISearchDisplayController 的内部结构。我的解决方案是基于观察 UISearchDisplayContoller 是 UISearchbarDelegate 并且可以通过模拟搜索字段中的按键来触发结果表的自动取消调暗和显示!所以:

- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller 
{
    if ([controller respondsToSelector: @selector(searchBar:textDidChange:)])
        [(id<UISearchBarDelegate>)controller searchBar: controller.searchBar textDidChange: @" "];
}

此代码通过检查它是否响应 UISearchbarDelegate 方法来防止崩溃,并发送空格 @" " 以欺骗 UISearchDisplayController 认为用户输入了一个字母。

现在,如果用户键入内容然后将其删除,表格将再次变暗。其他解决方案尝试通过在 searchDisplayController:didHideSearchResultsTableView: 方法中执行某些操作来解决此问题。但这对我来说没有意义,因为当您取消搜索时,它肯定需要真正隐藏您的结果表,并且在这种情况下您可能需要运行代码。我对此部分的解决方案是子类化(请注意,如果您的项目需要,您可能会使用 Method Swizzled Category 使其在任何地方都能工作):

// privately declare protocol to suppress compiler warning
@interface UISearchDisplayController (Super) <UISearchBarDelegate>
@end

// subclass to change behavior
@interface GMSearchDisplayController : UISearchDisplayController
@end

@implementation GMSearchDisplayController

- (void) searchBar: (UISearchBar *) searchBar textDidChange: (NSString *) searchString
{
    if (searchString.length == 0)
        searchString = @" ";
    if ([super respondsToSelector: @selector(searchBar:textDidChange:)])
        [super searchBar: searchBar textDidChange: searchString];
}

@end

此代码通过拦截 textDidChange 委托方法并将 nil 或空字符串更改为空格字符串 @" " 来防止在空搜索栏上发生的正常隐藏/变暗。如果您使用的是第二位代码,那么您可以修改第一位以传递 nil 而不是 @" ",因为第二位将为您完成所需的到 @" " 的转换。

在我自己的项目中,我需要处理用户输入空格的情况,所以我使用了一个定义的标记来代替上面的@" ":

// arbitrary token used internally
#define SEARCH_PRELOAD_CONDITIONAL @"_#preresults#_"

然后通过将其转换回 nil 字符串在内部进行处理:

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    if ([searchString isEqualToString: SEARCH_PRELOAD_CONDITIONAL])
        searchString = nil;
}

享受吧! :)

【讨论】:

    【解决方案3】:

    适用于 iOS 7 的解决方案:

    // When the tableView is hidden, put it back
    - (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
        [self.searchDisplayController.searchResultsTableView reloadData];
        controller.searchResultsTableView.hidden = NO;
    
        // Also, remove the dark overlay
        for (UIView *v in [[controller.searchResultsTableView superview] subviews]) {
            // This is somewhat hacky..
            if (v.alpha < 1) {
                [v setHidden:YES];
            }
        }
    }
    
    -(void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {
        [self.searchDisplayController.searchResultsTableView reloadData];
        if (self.searchDisplayController.active == YES) {
            tableView.hidden = NO;
        }
    }
    

    【讨论】:

    • 这个显示了第二个延迟,我可以看到覆盖视图的出现和消失。有什么可以避免的吗??
    【解决方案4】:

    我有办法。插入这三种方法。您必须在表格视图中预加载数据才能使其正常工作。我的代码中有这些代码,因此只要您已经预加载了数据并且您正在使用搜索显示控制器,它就必须为您工作。

    - (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
    {
        CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
        self.searchDisplayController.searchResultsTableView.frame = testFrame;
        [self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
    
    //    [self.view addSubview:self.searchDisplayController.searchResultsTableView];
        controller.searchResultsTableView.hidden = NO;
    }
    
    -(void) searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
    {
        CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
        self.searchDisplayController.searchResultsTableView.frame = testFrame;
        [self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
    
        //    [self.view addSubview:self.searchDisplayController.searchResultsTableView];
        controller.searchResultsTableView.hidden = NO;
    }
    
    
    -(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
    {
        controller.searchResultsTableView.hidden = YES;
    }
    

    【讨论】:

    • 这是一个很好的解决方案。也可能还应该有一个 [self.searchDisplayController.searchResultsTableView removeFromSuperview];在搜索DisplayControllerWillEndSearch
    【解决方案5】:

    这适用于 iOS 8:

    - (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
    {
      self.searchDisplayController.searchResultsTableView.hidden = NO;
    }
    
    - (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
    {
        self.searchDisplayController.searchResultsTableView.hidden = NO;
        [self.searchDisplayController.searchResultsTableView.superview.superview bringSubviewToFront:self.searchDisplayController.searchResultsTableView.superview];
    
        CGRect frame = self.searchDisplayController.searchResultsTableView.frame;
        self.searchDisplayController.searchResultsTableView.frame = CGRectMake(frame.origin.x, 64, frame.size.width, frame.size.height);
    }
    

    但是,我不喜欢这种 hack,并用我自己的实现(UISearchBar 和 UITableView)替换了 searchDisplayController。

    【讨论】:

      【解决方案6】:

      我在 iOS 9 中使用 UISearchController 测试了以下内容。

      默认情况下,当 UISearchBar 中没有文本时,显示的表格将是您的原始表格视图(以下称为 originalTableView)。根据dimsBackgroundDuringPresentation 的值,这可能有也可能没有黑色透明层。

      一旦用户在 UISearchBar 中输入 any 文本,就会显示新表格视图的内容(我将其称为 searchTableView)。 p>

      请注意,在这两种解决方案中,我实现 UISearchController 的方式类似于 Apple 在 this example 中的做法;即有两个独立的 UITableViewController 负责显示数据。

      解决方案一:实现复杂,动画更流畅

      为了更流畅的动画:

      1. 实现 UISearchControllerDelegate 的 willPresentSearchController 和 willDismissSearchController 方法。
      2. 在 willPresent 中,更新您 originalTableView 的数据以准备显示您想要的任何零文本数据,然后调用 reloadData()
      3. 在 willDismiss 中,还原流程以显示原始内容并调用reloadData()

      例子:

      // here tableView refers to originalTableView
      
      extension ViewController: UISearchControllerDelegate {
          func willPresentSearchController(searchController: UISearchController) {
              data = ["A", "B", "C"]
              tableView.reloadData()
          }
      
          func willDismissSearchController(searchController: UISearchController) {
              data = ["a", "b", "c"]
              tableView.reloadData()
          }
      }
      

      在这个虚构的例子中,当用户没有关注 UISearchBar 时,数据显示为a,b,c,但当用户点击 UISearchBar 时,数据显示为A,B,C

      解决方案 2:快速实施,不连贯的动画

      不用在 originalTableView 和 searchTableView 中实现搜索控制器的逻辑,您可以使用此解决方案来简单地显示 searchTableView,即使它默认隐藏。

      您很可能已经在实现 UISearchResultsUpdating 协议。通过在该方法中简单地调用tableView.hidden = false,你应该得到你所追求的行为;尽管动画不那么出色。

      // here tableView refers to searchTableView
      
      extension SearchViewController: UISearchResultsUpdating {
          func updateSearchResultsForSearchController(searchController: UISearchController) {
              tableView.hidden = false
              filterData(text: searchController.searchBar.text!)
              tableView.reloadData()
          }
      }
      

      【讨论】:

        【解决方案7】:

        为什么不看看 Xcode 中的 TableSearch 项目?它或多或少地解决了您的需求。它在开头显示一个表格,并在点击搜索字段时将其隐藏。在帮助下的 Xcode 4 中的文档和 API 参考中查看 TableSearch。

        还可以看看this 它几乎可以满足您对 UISearchBar 和 UITableView 的需求。我更喜欢这种方法。调整它。

        为了进一步满足您的需求,我建议使用两个数据源。一个包含所有内容(这是您在开始时要显示的内容),一个用于保存过滤后的搜索结果。现在继续清空第一个数据源并用第二个数据源的内容填充它以存储和暗淡显示先前搜索的结果。以这种方式进行。在 TableSearch 代码中为您的任务使用这种方法。

        【讨论】:

          【解决方案8】:

          这是一种预填充 searchController 的方法。

          // 1. 用户输入文本后立即保存之前的搜索结果

          - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
          {
              // save searchString
          
              return YES;
          }
          

          // 2. 启动时加载上一个搜索字符串

          - (void)viewDidLoad 
          {
              // create data source for table view
              // searchData may be a NSMutableArray property defined in your interface
          
              self.searchData = // search your master list for searchString
          
              // or create your own list using any search criteria you want
          
              // or you may act depending on user preference
              if(!showPreviousSearch)
              {
                  // [searchData removeAllObjects];
              }
          }
          

          // 3. 询问时提供正确的数据源

          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
          {
              static NSString *CellIdentifier = @"Cell";
              UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
              if (cell == nil) 
              {
                  ...
              }
          
              NSMutableArray *dataSource = nil;
              if (tableView == [[self searchDisplayController] searchResultsTableView])
              {
                  dataSource = self.searchData;
              }
              else
              {
                  dataSource = self.masterData;
              }
          
              ...
          
              return cell;
          }
          

          // 希望这会有所帮助。

          谢谢

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-04-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多