将UISearchBarlocking到像Game Center那样的UITableView的顶部

在Game Center中的UITableViews中有这个很酷的function,他们在顶部有search栏。 与search栏放置在表头视图中的应用程序不同(它被视为标准表格单元格),而不是像上面的父导航栏那样。 所以当滚动表格时,search栏确实会移动,但是如果您在表格的边界之上滚动,search栏永远不会停止触摸导航栏。

有谁知道这可能是怎么完成的? 我想知道,如果苹果可能在父滚动视图中同时放置search栏和表,但我想知道是否可能比这更简单。

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

此外,为了正确支持resize(旋转时会出现这种情况),应重新使用其他帧值。

-(void)scrollViewDidScroll:(UIScrollView *)scrollView { UISearchBar *searchBar = searchDisplayController.searchBar; CGRect rect = searchBar.frame; rect.origin.y = MIN(0, scrollView.contentOffset.y); searchBar.frame = rect; } 

您可以将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访问search栏。

在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 } 

如果您想在Game Center中完全模拟search栏,还有一步。

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

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

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

CustomTableViewController.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; } 

瞧! 现在,当search栏处于非活动状态时,它将随着表格一起移动,并在尝试移动到表格边界之后保持固定状态。 活动时,它将保持固定,就像在游戏中心一样。

所有其他的答案在这里为我提供了有用的信息,但他们都没有使用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.top中的scrollViewDidScroll:replace为常量,以允许search栏在刷新animation上滚动。

虽然其他答案似乎有帮助,并部分做这项工作,但它并没有解决search栏没有收到用户的触摸问题,因为当你改变其框架,它移动到其父视图的范围之外。

更糟糕的是,当你点击search栏使其成为第一个响应者时,tableView委托很可能会在search栏下面调用tableView:didSelectRowAtIndexPath:单元格。

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

所以我们先这样做:

 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) } } 

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

其次,当视图控制器加载视图时,我们应该把search栏放到新的视图中:

 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 } } 

最后,我们应该更新search栏的框架作为用户向下滚动表视图,以便它将保持固定在顶部:

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

结果如下:

在这里输入图像说明

重要提示:如果你的表格视图有部分,他们可能会隐藏你的search栏,所以你需要在每次更新表视图的bounds时将search栏放在它们之上。

在这里输入图像说明

viewDidLayoutSubviews是做这件事的好地方:

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

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

如果您的部署目标是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) ]) } 

我假设你从代码创build了UISearchBar和UITableView,而不是在storyboard中。