28

Game Center 的 UITableViews 和顶部的搜索栏有这个很酷的功能。与将搜索栏放置在表格标题视图中的应用程序不同(因此它被视为标准表格单元格),相反,它似乎被固定在其上方的父导航栏上。因此,当滚动表格时,搜索栏确实会移动,但如果您在表格边界上方滚动,搜索栏将永远不会停止接触导航栏。

有谁知道这可能是怎么做到的?我想知道Apple是否可能将搜索栏和表格都放在父滚动视图中,但我想知道它是否比这更简单。

4

7 回答 7

28

Bob 的答案是相反的:它应该是 MIN(0, scrollView.contentOffset.y)。

此外,为了正确支持调整大小(旋转时会发生),应该重用其他帧值。

-(void)scrollViewDidScroll:(UIScrollView *)scrollView 
{
    UISearchBar *searchBar = searchDisplayController.searchBar;
    CGRect rect = searchBar.frame;
    rect.origin.y = MIN(0, scrollView.contentOffset.y);
    searchBar.frame = rect;
}
于 2011-03-12T21:00:48.833 回答
7

您可以将 searchBar 放在表头中并为 tableView 实现 - (void)scrollViewDidScroll:(UIScrollView *)scrollView 委托方法。做这样的事情应该有效:

-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
    searchBar.frame = CGRectMake(0,MAX(0,scrollView.contentOffset.y),320,44);
} 

如果您使用了 searchDisplayController,您将使用 self.searchDisplayController.searchbar 访问搜索栏。

于 2011-02-03T16:08:39.207 回答
5

在 Swift 2.1 和 iOS 9.2.1 中

    let searchController = UISearchController(searchResultsController: nil)

        override func viewDidLoad() {
        /* Search controller parameters */
            searchController.searchResultsUpdater = self  // This protocol allows your class to be informed as text changes within the UISearchBar.
            searchController.dimsBackgroundDuringPresentation = false  // In this instance,using current view to show the results, so do not want to dim current view.
            definesPresentationContext = true   // ensure that the search bar does not remain on the screen if the user navigates to another view controller while the UISearchController is active.

            let tableHeaderView: UIView = UIView.init(frame: searchController.searchBar.frame)
            tableHeaderView.addSubview(searchController.searchBar)
            self.tableView.tableHeaderView = tableHeaderView

        }
        override func scrollViewDidScroll(scrollView: UIScrollView) {
           let searchBar:UISearchBar = searchController.searchBar
           var searchBarFrame:CGRect = searchBar.frame
           if searchController.active {
              searchBarFrame.origin.y = 10
           }
           else {
             searchBarFrame.origin.y = max(0, scrollView.contentOffset.y + scrollView.contentInset.top)

           }
           searchController.searchBar.frame = searchBarFrame
       }
于 2016-01-22T14:49:30.220 回答
4

虽然其他答案似乎有帮助并且部分完成了这项工作,但它并不能解决搜索栏没有接收到用户触摸的问题,因为它会在您更改其框架时移动到其父视图的范围之外。

更糟糕的是,当您单击搜索栏使其成为第一响应者时,tableView 委托很可能会调用tableView:didSelectRowAtIndexPath:位于搜索栏下方的单元格。

为了解决上述这些问题,您需要将搜索栏包装在一个普通的 UIView 中,一个能够处理发生在其边界之外的触摸的视图。通过这种方式,您可以将这些触摸传递到搜索栏。

所以让我们先这样做:

class SearchBarView: UIView {
  override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    for subview in subviews {
      if !subview.userInteractionEnabled { continue }

      let newPoint = subview.convertPoint(point, fromView: self)
      if CGRectContainsPoint(subview.bounds, newPoint) {
        return subview.hitTest(newPoint, withEvent: event)
      }
    }
    return super.hitTest(point, withEvent: event)
  }
}

现在,我们有一个名为 UIView 的子类SearchBarView,它能够接收发生在其边界之外的触摸。

其次,我们应该在视图控制器加载其视图时将搜索栏放入新视图中:

class TableViewController: UITableViewController {
  private let searchBar = UISearchBar(frame: CGRectZero)
  ...

  override func viewDidLoad() {
    super.viewDidLoad()
    ...
    searchBar.sizeToFit()
    let searchBarView = SearchBarView(frame: searchBar.bounds)
    searchBarView.addSubview(searchBar)

    tableView.tableHeaderView = searchBarView
  }
}

最后,我们应该在用户向下滚动表格视图时更新搜索栏的框架,使其保持固定在顶部:

override func scrollViewDidScroll(scrollView: UIScrollView) {
  searchBar.frame.origin.y = max(0, scrollView.contentOffset.y)
}

结果如下:

在此处输入图像描述

--

重要提示:如果您的表格视图有部分,它们可能会遮住您的搜索栏,因此您需要在每次bounds更新表格视图时将搜索栏放在它们的顶部。

在此处输入图像描述

viewDidLayoutSubviews是这样做的好地方:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  ...
  if let tableHeaderView = tableView.tableHeaderView {
    tableView.bringSubviewToFront(tableHeaderView)
  }
}

--

希望这可以帮助。您可以从这里下载示例项目。

于 2016-09-28T03:10:02.420 回答
3

如果您想完全模拟 Game Center 中的搜索栏,还有一步。

如果您从friedenberg的出色回答开始,以及评论中提到的followben对iOS 6+的修改,您仍然需要在搜索栏本身处于活动状态时调整功能。

在 Game Center 中,当您向下滚动时,搜索栏将随表格一起滚动,但当您尝试向上滚动超出表格边界时,搜索栏将保持固定在导航栏下方。但是,当搜索栏处于活动状态并且正在显示搜索结果时,搜索栏不再随表格滚动;它仍然固定在导航栏下方

这是实现此功能的完整代码(适用于 iOS 6+)。(如果您的目标是 iOS 5 或更低版本,则无需将 UISearchBar 包装在 UIView 中)

自定义表视图控制器.m

- (void)viewDidLoad
{
    UISearchBar *searchBar = [[UISearchBar alloc] init];
    ...
    UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchBar.frame];
    [tableHeaderView addSubview:searchBar];

    [self.tableView setTableHeaderView:tableHeaderView];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    UISearchBar *searchBar = self.tableView.tableHeaderView.subviews.lastObject;
    CGRect searchBarFrame = searchBar.frame;

    /*
     * In your UISearchBarDelegate implementation, set a boolean flag when
     * searchBarTextDidBeginEditing (true) and searchBarTextDidEndEditing (false)
     * are called.
     */

    if (self.inSearchMode)
    {
        searchBarFrame.origin.y = scrollView.contentOffset.y;
    }
    else
    {
        searchBarFrame.origin.y = MIN(0, scrollView.contentOffset.y);
    }

    searchBar.frame = searchBarFrame;
}

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    self.inSearchMode = YES;
}

- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
    self.inSearchMode = NO;
}

瞧!现在,当搜索栏处于非活动状态时,它将与表格一起移动,并在尝试超出表格边界时保持固定。激活时,它将保持固定不变,就像在 Game Center 中一样。

于 2013-06-29T12:51:58.397 回答
0

这里的所有其他答案都为我提供了有用的信息,但没有一个可以使用 iOS 7.1。这是对我有用的简化版本:

MyViewController.h:

@interface MyViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate> {
}
@end

MyViewController.m:

@implementation MyViewController {

    UITableView *tableView;
    UISearchDisplayController *searchDisplayController;
    BOOL isSearching;
}

-(void)viewDidLoad {
    [super viewDidLoad];

    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    searchBar.delegate = self;

    searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
    searchDisplayController.delegate = self;
    searchDisplayController.searchResultsDataSource = self;
    searchDisplayController.searchResultsDelegate = self;

    UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchDisplayController.searchBar.frame];
    [tableHeaderView addSubview:searchDisplayController.searchBar];
    [tableView setTableHeaderView:tableHeaderView];

    isSearching = NO;
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {

    UISearchBar *searchBar = searchDisplayController.searchBar;
    CGRect searchBarFrame = searchBar.frame;

    if (isSearching) {
        searchBarFrame.origin.y = 0;
    } else {
        searchBarFrame.origin.y = MAX(0, scrollView.contentOffset.y + scrollView.contentInset.top);
    }

    searchDisplayController.searchBar.frame = searchBarFrame;
}

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    isSearching = YES;
}

-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
    isSearching = NO;
}

@end

注意:如果您在列表中使用“下拉刷新”,则需要用常量替换scrollView.contentInset.topinscrollViewDidScroll:以允许搜索栏滚动刷新动画。

于 2014-06-10T23:05:29.450 回答
0

如果您的部署目标是 iOS 9 及更高版本,那么您可以使用锚点并以编程方式设置 UISearchBar 和 UITableView:

    private let tableView = UITableView(frame: .zero, style: .plain)
    private let searchBar = UISearchBar(frame: CGRect .zero)

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self
        tableView.contentInset = UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0)

        searchBar.delegate = self
        view.addSubview(tableView)
        view.addSubview(searchBar)
        NSLayoutConstraint.activate([
            searchBar.heightAnchor.constraint(equalToConstant: 44.0),
            searchBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            searchBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            searchBar.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
            ])
    }

我假设您从代码中创建 UISearchBar 和 UITableView,而不是在情节提要中。

于 2017-02-17T10:55:38.417 回答