iPhone:默认情况下隐藏UITableView搜索栏


85

我使用界面生成器创建了一个表视图,在该视图中添加了库的搜索栏和搜索显示控制器以添加搜索功能。但是,IB对其进行了设置,以便首次显示视图时,该条在屏幕顶部可见。

我想知道默认情况下如何隐藏搜索栏,但仍可以在表格视图中滚动(有关示例,请参阅Apple的Mail应用程序)。我已经打过电话scrollRectToVisible:animated:viewDidLoad滚动表视图下来,但无济于事。默认情况下,隐藏搜索栏的首选方法是什么?

Answers:


116

首先,确保将UISearchBar添加到UITableView的tableHeaderView中,以便它随表的内容滚动而不固定在视图的顶部。

搜索栏在表格视图中不算作一行,因此,如果将表格视图的顶部滚动到第一行,它将“隐藏”搜索栏:

[yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];

或在Swift中:

yourTableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)

确保在包含数据的表视图之前不要滚动它(scrollToRowAtIndexPath如果给定的indexPath没有指向有效的行(即,如果表视图为空)将引发异常)。


12
实际上,我遇到一个问题,即如果没有足够的行来填充屏幕,表视图将不会滚动-在具有一两行的表中,此代码不执行任何操作。那是预期的行为吗?我能以某种方式解决它吗?
蒂姆(Tim)

18
我找到了一种方法:将表的滚动视图的内容偏移量更改为CGRect,而不是滚动到索引路径,将其更改为0.0、44.0
Tim于2009年

107
蒂姆,那是要走的路!我使用了self.tableView.contentOffset = CGPointMake(0,self.searchDisplayController.searchBar.frame.size.height);
乔D'安德烈

1
顺便说一句-当我在调用[tableView reloadData]之后放入此代码后,导致我的表从2行增加到足够多的行以允许正常滚动时,我发现使用contentOffset方法会导致显示和隐藏搜索变得混乱使用scrollToRowAtIndexPath方法时的框没有相同的混用。
克里斯·R

1
这是在任何情况下都有效的唯一最终方法。contentOffset如果您使用复杂的视图控制器布局,则使用任何值进行设置都会中断。我不推荐。
2014年

45

不要将UISearchBar添加为UITableView的子视图,这不是必需的。

UITableView有一个tableHeaderView属性,非常适合此操作:

- (void) viewDidLoad {
    [super viewDidLoad]; 
    self.searchBar = [[[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)] autorelease];
    self.searchBar.showsCancelButton = YES;    
    self.searchBar.delegate = self;
    self.tableView.tableHeaderView = self.searchBar;     
}

如果您不想默认看到它:

- (void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.tableView setContentOffset:CGPointMake(0, 44)];
}

我还得到了取消按钮以再次隐藏它。...(并卸下键盘)

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
    [self.tableView setContentOffset:CGPointMake(0, 44) animated:YES];
    [self.searchBar resignFirstResponder];
}

对我来说,这是唯一产生隐藏的UISearchBar实现的解决方案,该实现似乎与HIG一致。其他“卡在”表视图顶部的解决方案感觉不自然。
kurtzmarc 2012年

1
此解决方案效果最佳。我认为这应该是答案。
darwindeeds 2013年

这个解决方案很好(+1)。但是,在某些情况下(在我的情况下:当tableview控制器从另一个视图控制器推回时),tableView不能正确隐藏搜索栏。关于这个问题有一个问题:stackoverflow.com/questions/15222186/…我认为所提供的这个问题的答案不是很好。@bandejapaisa,你有什么主意吗?
布赖恩

3
这对我来说效果很好,不过我建议不要在viewWillAppear中使用self.tableView.tableHeaderView.frame.size.height来对44进行硬编码,以允许Apple将来对搜索栏高度进行更改。
约翰·史蒂芬

放置它viewWillAppear不是一个好主意,因为它会在您每次导航回到视图时都运行。这意味着您的表视图将逐渐缩小。令人讨厌的错误!
Sirens

25

Tim和Joe D'Andrea在评论中指出了contentOffset,但是通过添加动画来隐藏搜索栏对此进行了扩展。我注意到搜索栏消失了有点意外。

对于想要定位iOS 4.0及更高版本的用户,请进行以下更新:

[UIView animateWithDuration:0.4 animations:^{
    self.tableView.contentOffset = CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height);
 } completion:nil];

先前的答案:

[UIView beginAnimations:@"hidesearchbar" context:nil];
[UIView setAnimationDuration:0.4];
[UIView setAnimationBeginsFromCurrentState:YES];

self.tableView.contentOffset = CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height);

[UIView commitAnimations];


9
我发现动画是此解决方案的关键。尝试在没有动画的情况下设置内容偏移将似乎无济于事。但是,我发现没有调用此示例中的动画方法(或块),而是简单地调用[self.tableView setContentOffset:CGPointMake(0,44)animation:TRUE]即可。
quickthyme 2011年

没有动画似乎根本无法工作。@quickthyme也做了同样的观察。
Thilo

5
如果您使用viewDidAppear:而不是实施动画,那么我已经成功完成了此过程而无需动画viewDidLoad
wbyoung

8

此解决方案有很多答案,但是对我来说,没有一个很好的解决方案。

这很完美。没有动画,并且完成-viewDidLoad

 - (void)viewDidLoad {
     [super viewDidLoad];
     self.tableView.contentOffset = CGPointMake(0,  self.searchBar.frame.size.height - self.tableView.contentOffset.y);
 }

注意:

代码假定您具有searchBar @property链接到搜索栏的。如果没有,您可以self.searchDisplayController.searchBar改用


这是唯一为我工作的人!,但它只会在未viewdidload时执行此操作...因此,第一次隐藏它,然后在de app运行时不再隐藏。我尝试将其放在viewwillappear和viewdidappear中,但是工作错误。谢谢!
fguespe 2014年

@fguespe这很奇怪。每次实例化视图时,都会调用viewDidLoad。从另一个视图回来后,UISearchBar一直可见,这对我来说从未发生过。
2014年

即时通讯使用选项卡式应用程序。也许吧?
fguespe 2014年

@fguespe我看到这可能是问题嗯。不幸的是林不知道你会如何去解决,如果-viewWillAppear不工作..是否得到当标签控制器切换到视图叫什么?
2014年

viewwillappear和viewdidappear被称为,但是我都尝试了两者,并且该视图就像错了一样。它不会返回预期的结果。我的意思是,有些事情可以做,但它却不好。
fguespe 2014年

7

对于Swift 3+

我这样做:

声明var

    var searchController = UISearchController()

而在viewDidLoad()方法

    searchController = UISearchController(searchResultsController: nil)
    searchController.searchResultsUpdater = self
    searchController.hidesNavigationBarDuringPresentation = false
    searchController.dimsBackgroundDuringPresentation = true
    searchController.searchBar.placeholder = NSLocalizedString("Search", comment: "")
    definesPresentationContext = true
    tableView.tableHeaderView = searchController.searchBar

    tableView.contentOffset = CGPoint(x: 0, y: searchController.searchBar.frame.size.height)

5

我的目标是在加载视图控制器时隐藏添加到情节提要中纯TableView样式的tableHeaderView的搜索栏,并在用户向下滚动UITableView顶部时显示搜索栏。因此,我正在使用Swift并添加了扩展名:

extension UITableView {
    func hideSearchBar() {
        if let bar = self.tableHeaderView as? UISearchBar {
            let height = CGRectGetHeight(bar.frame)
            let offset = self.contentOffset.y
            if offset < height {
                self.contentOffset = CGPointMake(0, height)
            }
        }
    }
}

viewDidLoad()中只需调用:

self.tableView.hideSearchBar()

5

当没有足够的行来填充tableView时,所有现有解决方案都无法在iOS 8上使用,因为iOS会在这种情况下自动调整插图。(但是,当有足够的行时,现有答案很好)

在这个问题上浪费了大约6个小时之后,我终于得到了这个解决方案。

简而言之,如果没有足够的单元格,则需要在tableView中插入空单元格,因此tableView的内容大小足够大,iOS无法为您调整插图。

这是我在Swift中的操作方式:

1.)将变量声明minimumCellNum为类属性

var minimumCellNum: Int?

2.)计算minimumCellNum并设置tableView.contentOffsetviewWillAppear

let screenHeight = Int(UIScreen.mainScreen().bounds.height)

// 101 = Height of Status Bar(20) + Height of Navigation Bar(44) + Height of Tab Bar(49)
// you may need to subtract the height of other custom views from the screenHeight. For example, the height of your section headers.
self.minimumCellNum = (screenHeight - 103 - heightOfOtherCustomView) / heightOfYourCell
self.tableView.contentOffset = CGPointMake(0, 44)

3.)在 tableView(tableView: UITableView, numberOfRowsInSection section: Int))

let numOfYourRows = YOUR LOGIC
if numOfYourRows > minimumCellNum {
    return numOfYourRows
} else {
    return minimumCellNum!
}

4.)在情节提要和故事板中注册一个selection属性为的空单元格NonetableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)

if indexPath.row < numOfYourRows {
    return YOUR CUSTOM CELL
} else {
    let cell = tableView.dequeueReusableCellWithIdentifier("EmptyCell", forIndexPath: indexPath) as! UITableViewCell
    return cell
}

5.)在 tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)

if tableView == self.tableView {
    if numOfYourRows < (indexPath.row + 1) {
        return
    }
    YOUR LOGIC OF SELECTING A CELL
}

这不是一个完美的解决方案,但这是对iOS 8真正有效的唯一解决方法。我想知道是否有更整洁的解决方案。


我必须在iOS 11上执行此操作。仅针对0个或更多单元格的iOS 9/10设置contentOffset即可,对于8个以上单元格的iOS 11也适用。几乎不需要此解决方案。有什么理由吗?
托尼

我认为这篇文章的第一段说明了原因。
布赖恩

我了解第一段,但对我来说,它只是在iOS 11上被“破坏”,这仍然很奇怪。当单元数相同(不足)时,我会期望iOS 9、10和11具有类似的行为。再次感谢,也很抱歉提出一个旧帖子!
托尼

4

以上都不对我有用,所以以防万一有人遇到同样的问题。

我已在iOS7的分组表上searchBar设置为tableHeaderView。在最初,viewDidLoad我必须tableView.contentOffset = CGPointMake(0, 44);保持searchBar“隐藏”状态。当用户向下滚动searchBar时dsiplayed

目标:在取消按钮点击时隐藏searchBar

searchBarCancelButtonClicked(searchBar: UISearchBar!)

这没有用: [yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; tableView向上滚动太多,导致第0行位于toolBar后面。

这不起作用: tableView.ContentOffset = CGPointMake(0, 44) -没有任何反应

这不起作用: tableView.ContentOffset = CGPointMake(0, searchBar.frame.size.height) -没有任何反应

这没有用: tableView.setContentOffset(CGPointMake(0, 44), animated: true); tableView再次向上滚动太多,导致第0行位于toolBar后面。

这部分起作用: tableView.setContentOffset(CGPointMake(0, 0)但是titleForHeaderInSection落后于toolBar

这工作: tableView.setContentOffset(CGPointMake(0, -20)

似乎不合逻辑,但只有-20向上移动到正确位置


您真的不应该对您的观点进行硬编码,为什么不使用CGRectGetHeight(searchBar.bounds)
佐拉尔2015年

我猜您必须在viewDidLoad()/ init()中执行此操作,其中未设置任何框架来获取搜索栏的高度。也许您应该尝试使用viewDidLayoutSubviews()
Kaushil Ruparelia,2016年

如果tableView尚未完成加载,则所有ContentOffset解决方案均不起作用
Maor

4

内容偏移量是解决之道,但并非100%的时间有效。

如果设置estimatedRowHeight为固定数字,则无法使用。我尚不知道要解决的问题。我创建了一个显示问题的项目,并且创建了一个与Apple雷达

如果您想快速获得一个示例,请查看该项目。


1
谢谢!这就是我想要的。你需要上去。
user2387149'7

3

请注意,如果表视图中没有数据,则接受的答案将崩溃。并将其添加到viewDidLoad在某些情况下可能不起作用。

[yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; // crashes if no rows/data

因此,改为添加以下代码行:

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

    [yourTableView setContentOffset:CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height)];
}

2

老实说,没有一个答案能真正解决这个问题(对我来说)。如果您的表格视图没有行,或者行高不同,那么这些答案就不够了。

唯一有效的方法:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.tableView.contentOffset = (CGPoint){.y =  self.tableView.contentOffset.y + self.searchController.searchBar.frame.size.height};
}

补偿问题的好解决方案。使用UINavigationController时对我有用。
Andreas Kraft 2015年

1
当行数不能覆盖整个屏幕时,该解决方案将不起作用-您知道解决方法吗?
Zorayr

不确定我是否完全理解。当我表中只有几行时,这对我有用。但是,我确实使用以下内容:self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
p0lAris

2

scrollToRowAtIndexPath如果表中的行为零,则该方法将导致崩溃。

UITableView只是一个滚动视图,因此您只需更改内容偏移即可。

这将检查您是否有您的tableHeaderView对象,并假设您进行滚动以隐藏它

    if let searchHeight = tableView.tableHeaderView?.frame.height {
        tableView.setContentOffset(CGPoint.init(x: 0, y:searchHeight ), animated: false)
    }

1

我发现tableView为空白时,“ Notes”应用程序无法搜索项目。所以我认为这只是在使用-(void)addSubview:(UIView *)view用来添加一个空白表。将项目添加到其数据源数组时,它将运行另一段代码来加载tableView。

所以我的解决方案是:

if([self.arrayList count] > 0)
{
     [self.tableView reloadData];     //use jdandrea's code to scroll view when cell==nil in cellForRowAtIndexPath
     self.tableView.scrollEnabled = YES;
}
else
{
     tableBlank = [[UITableView alloc]initWithFrame:CGRectMake(0,0,320,416) style:UITableViewStylePlain] autorelease];
     tableBlank.delegate = self;
     tableBlank.dataSource = self;
     [self.view addSubview:tableBlank];
}

希望这可以帮助 :-)


1

更改tableView的边界,可以在内部完成此操作,而且viewDidLoad您不必担心表是否为空(避免出现异常)。

CGRect newBounds = self.tableView.bounds;
newBounds.origin.y = newBounds.origin.y + self.searchBar.bounds.size.height;
self.tableView.bounds = newBounds;

用户向下滚动表格后,搜索栏将出现并正常运行


1

这个问题的其他答案对我来说根本不起作用,或者当tableView有0行时行为很奇怪,或者在父项和嵌套项之间来回移动时弄乱了滚动位置。这对我有用(目标是在加载时隐藏UISearchBar):

public override func viewWillAppear(animated: Bool) {        
    if self.tableView.contentOffset.y == 0 {
        self.tableView.contentOffset = CGPoint(x: 0.0, y: self.searchBar.frame.size.height) 
    }
}

说明
任何时候tableView都会出现,看到这样的代码检查,如果是的UISearchBar可见(指y的位置contentOffset,又名滚动位置,是0)。如果是,它只是向下滚动searchBar的高度。如果searchBar不可见(请在中查看较大的项列表tableView),则它将不会尝试滚动tableView,因为当您从嵌套订单项返回时,这会弄乱位置。

旁注
我建议将此代码放在单独的方法中,以确保单项职责,并使您可以在代码中随时调用它。


1

如果您的表格始终至少有一行,只需滚动到表格的第一行,搜索栏就会自动隐藏。

let firstIndexPath = NSIndexPath(forRow: 0, inSection: 0)

self.tableView.selectRowAtIndexPath(firstIndexPath,动画:false,scrollPosition:.Top)

如果将上面的代码放在viewDidLoad上,则将引发错误,因为tableView尚未加载,因此必须将其放在viewDidAppear中因为此时tableView已经加载。

如果放在viewDidAppear上,则每次打开tableView时,它将滚动到顶部。

如果tableView保持打开状态,例如当它是UITabBar View Controller或执行segue然后返回时,也许您不希望这种行为。如果只希望它在初始加载时滚动到顶部,则可以创建一个变量来检查它是否是初始加载,以便它只滚动一次到顶部。

首先在视图控制器类中定义一个名为isInitialLoad的变量,并将其设置为等于“ true”:

var isInitialLoad = true

然后在viewDidAppear上检查isInitialLoad是否为true ,如果为true,则滚动到顶部,并将isInitialLoad变量设置为false:

if isInitialLoad {
            let firstIndexPath = NSIndexPath(forRow: 0, inSection: 0)
            self.tableView.selectRowAtIndexPath(firstIndexPath, animated: false, scrollPosition: .Top)

            isInitialLoad = false
        }

1

下面的代码为我工作

self.tableView.contentOffset = CGPointMake(0.0, 44.0);

1

我尝试设置内容偏移量和scrollToIndexpath,但没有令人满意的工作。我从viewDidLoad中删除了tableHeaderView作为searchBar的设置,并在scrollview委托中进行了设置,如下所示

override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
    if scrollView.contentOffset.y < 0 && tableView.tableHeaderView == nil {
        tableView.tableHeaderView = searchController.searchBar
        tableView.setContentOffset(CGPointMake(0, scrollView.contentOffset.y + searchController.searchBar.frame.size.height), animated: false)
    }
}

这就像一个魅力一样.content offset设置用于平滑滚动目的


0

不要忘记考虑状态栏导航栏。我的表视图控制器嵌入在导航控制器中,位于viewDidAppear

tableView.contentOffset = CGPointMake(0, -20) // -20 = 44 (search bar height) - 20 (status bar height) - 44 (navigation bar height)

为我工作。


0

对于Swift:

只需使tableview内容y偏移,就应该像搜索栏的高度一样。

self.tableView.contentOffset = CGPointMake(0, self.searchController.searchBar.frame.size.height)

0

迅捷3

也适用于空表!

在我的环境中,我通过xib文件加载自定义单元格,并且rowHeight是自动设置的。

    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        self.tableView.contentOffset = CGPoint(x: 0, y: self.tableView.contentOffset.y + self.searchController.searchBar.bounds.height)
}

0

我有类似的问题。我发现解决此问题的唯一方法是在UITableView contentOffset属性上使用键值观察器。

经过一些调试后,我意识到即使表视图内容的大小小于表视图的大小,也会出现问题。我在viewWillAppear上启动KVO。并在视图出现0.3秒后发送停止KVO。每当contentOffset变为0.0时,KVO就会做出反应并在搜索栏高度上设置contentOffset。我在0.3秒后停止了KVO异步操作,因为即使视图出现后,视图布局也会重置contentOffset。

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}

-(void)viewDidAppear:(BOOL)animated{
   __weak typeof (self) weakSelf = self;
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [weakSelf.tableView removeObserver:self forKeyPath:@"contentOffset"];});
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if ([keyPath isEqualToString:@"contentOffset"] && self.tableView.contentOffset.y == 0.0) {
       self.tableView.contentOffset = CGPointMake(0, CGRectGetHeight(self.tableView.tableHeaderView.frame));
    }
}

-(void)dealloc {
    [self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.