ios 将 UISearchBar 锁定到 UITableView 的顶部,例如 Game Center
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4870085/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Locking a UISearchBar to the top of a UITableView like Game Center
提问by TiM
There's this cool feature in the UITableViews in Game Center and the search bars they have at their tops. Unlike apps where the search bar is placed in the table header view (so it counts as a standard table cell), instead, it seems to be bolted to the parent navigation bar above it. So when scrolling the table, the search bar does indeed move, but if you scroll above the boundaries of the table, the search bar never stops touching the navigation bar.
Game Center 的 UITableViews 和它们顶部的搜索栏有这个很酷的功能。与将搜索栏放置在表格标题视图中的应用程序不同(因此它被视为标准表格单元格),相反,它似乎被固定在其上方的父导航栏中。因此,在滚动表格时,搜索栏确实会移动,但如果滚动到表格边界上方,搜索栏将永远不会停止接触导航栏。
Does anyone know how this might have been done? I was wondering if Apple maybe placed both the search bar and the table in a parent scroll view, but I'm wondering if it may be simpler than that.
有谁知道这可能是如何完成的?我想知道 Apple 是否可能将搜索栏和表格都放在父滚动视图中,但我想知道它是否比这更简单。
采纳答案by friedenberg
Bob's answer is reversed: it ought to be MIN(0, scrollView.contentOffset.y).
Bob 的答案是相反的:它应该是 MIN(0, scrollView.contentOffset.y)。
Also, in order to properly support resizing (which would occur when rotated), the other frame values should be reused.
此外,为了正确支持调整大小(旋转时会发生),应重用其他帧值。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UISearchBar *searchBar = searchDisplayController.searchBar;
CGRect rect = searchBar.frame;
rect.origin.y = MIN(0, scrollView.contentOffset.y);
searchBar.frame = rect;
}
回答by Bob Vork
You could put the searchBar in the table header and implement the - (void)scrollViewDidScroll:(UIScrollView *)scrollView delegate method for the tableView. Doing something like this should work:
您可以将 searchBar 放在表头中并为 tableView 实现 - (void)scrollViewDidScroll:(UIScrollView *)scrollView 委托方法。做这样的事情应该有效:
-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
searchBar.frame = CGRectMake(0,MAX(0,scrollView.contentOffset.y),320,44);
}
If you used the searchDisplayController, you would access the searchbar using self.searchDisplayController.searchbar.
如果您使用 searchDisplayController,您将使用 self.searchDisplayController.searchbar 访问搜索栏。
回答by Muhammad Qasim
In Swift 2.1 and iOS 9.2.1
在 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
}
回答by ozgur
While other answers seem helpful and partially do the job, it doesn't solve the issue of search bar not receiving the user's touches because it moves outside the bounds of its parent view as you change its frame.
虽然其他答案似乎有帮助并且部分完成了工作,但它并没有解决搜索栏没有收到用户触摸的问题,因为它在您更改框架时移动到其父视图的边界之外。
What's worse is that, when you click on the search bar to make it the first responder, it is very likely that the tableView delegate will call tableView:didSelectRowAtIndexPath:
on cell that is laid out under the search bar.
更糟糕的是,当您单击搜索栏使其成为第一响应者时,tableView 委托很可能会调用tableView:didSelectRowAtIndexPath:
位于搜索栏下方的单元格。
In order to address those issues described above, you need to wrap the search bar in a plain UIView, a view which is capable of processing touches occurred outside of its boundaries. By this way, you can relay those touches to the search bar.
为了解决上述这些问题,您需要将搜索栏包装在一个普通的 UIView 中,该视图能够处理发生在其边界之外的触摸。通过这种方式,您可以将这些触摸转发到搜索栏。
So let's do that first:
所以让我们先这样做:
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)
}
}
Right now, we have a UIView subclass named SearchBarView
which is capable of receiving touches occurred outside of its boundaries.
现在,我们有一个名为 UIView 的子类SearchBarView
,它能够接收发生在其边界之外的触摸。
Secondly, we should put the search bar into that new view while the view controller is loading its view:
其次,我们应该在视图控制器加载其视图时将搜索栏放入该新视图中:
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
}
}
At last, we should update the frame of the search bar as user scrolls down the table view so that it will stay fixed at the top:
最后,当用户向下滚动表格视图时,我们应该更新搜索栏的框架,使其固定在顶部:
override func scrollViewDidScroll(scrollView: UIScrollView) {
searchBar.frame.origin.y = max(0, scrollView.contentOffset.y)
}
Here is the result:
结果如下:
--
——
Important note: If your table view has sections, they will probably shadow your search bar so you need to bring the search bar on top of them every time the table view's bounds
gets updated.
重要提示:如果您的表格视图有部分,它们可能会遮住您的搜索栏,因此每次表格视图bounds
更新时您都需要将搜索栏放在它们的顶部。
viewDidLayoutSubviews
is a good place to do that:
viewDidLayoutSubviews
是这样做的好地方:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
...
if let tableHeaderView = tableView.tableHeaderView {
tableView.bringSubviewToFront(tableHeaderView)
}
}
--
——
Hope this helps. You can download the example project from here.
希望这可以帮助。您可以从这里下载示例项目。
回答by Nick Schneble
There's one more step if you want to fully emulate the search bars in Game Center.
如果您想完全模拟 Game Center 中的搜索栏,还有一个步骤。
If you start with friedenberg'sexcellent answer, as well as followben'smodification for iOS 6+ mentioned in the comments, you still need to adjust the functionality when the search bar itself is active.
如果你从Friedenberg 的优秀答案开始,以及评论中提到的followben对 iOS 6+的修改,你仍然需要在搜索栏本身处于活动状态时调整功能。
In Game Center, the search bars will scroll with the table as you scroll down, but will remain fixed below the navigation bar when you attempt to scroll up past the boundaries of the table. However, when the search bar is active and search results are being displayed, the search bar no longer scrolls with the table; it remains fixed in place below the navigation bar.
在 Game Center 中,当您向下滚动时,搜索栏将随表格一起滚动,但当您尝试向上滚动超过表格边界时,搜索栏将保持固定在导航栏下方。但是,当搜索栏处于活动状态并且正在显示搜索结果时,搜索栏不再随表格滚动;它仍然固定在导航栏下方。
Here's the complete code (for iOS 6+) for implementing this. (If you're targeting iOS 5 or below, you don't need to wrap the UISearchBar in a UIView)
这是用于实现此功能的完整代码(适用于 iOS 6+)。(如果您的目标是 iOS 5 或更低版本,则不需要将 UISearchBar 包装在 UIView 中)
CustomTableViewController.m
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;
}
Voilà! Now, when the search bar is inactive it will move with the table, and remain fixed when attempting to move beyond the table boundaries. When active, it will remain fixed in place, just like in Game Center.
瞧!现在,当搜索栏是无效的,将与移动表,并试图超越表边界时保持固定。激活时,它将保持固定不变,就像在 Game Center 中一样。
回答by Brian
All of the other answers here provided me with helpful information, but none of them worked using iOS 7.1. Here's a simplified version of what worked for me:
此处的所有其他答案都为我提供了有用的信息,但没有一个在使用 iOS 7.1 时有效。这是对我有用的简化版本:
MyViewController.h:
MyViewController.h:
@interface MyViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate> {
}
@end
MyViewController.m:
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
Note: If you're using "pull down to refresh" on your list, you'll need to replace scrollView.contentInset.top
in scrollViewDidScroll:
with a constant to allow the search bar to scroll over the refresh animation.
注意:如果您在列表中使用“下拉刷新”,则需要将scrollView.contentInset.top
in替换scrollViewDidScroll:
为常量以允许搜索栏在刷新动画上滚动。
回答by Filipp Ignatov
If your deployment target is iOS 9 and higher then you can use anchors and set UISearchBar and UITableView programmatically:
如果您的部署目标是 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)
])
}
I assume that you create UISearchBar and UITableView from code, not in storyboard.
我假设您从代码创建 UISearchBar 和 UITableView,而不是在故事板中。