【问题标题】:iPhone: Hide UITableView search bar by defaultiPhone:默认隐藏 UITableView 搜索栏
【发布时间】:2010-11-08 01:24:11
【问题描述】:

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

我想知道如何在默认情况下隐藏搜索栏,但仍可使用表格视图滚动(请参阅 Apple 的邮件应用程序以获取示例)。我尝试在viewDidLoad 中调用scrollRectToVisible:animated: 来向下滚动表格视图,但无济于事。默认隐藏搜索栏的首选方式是什么?

【问题讨论】:

    标签: ios iphone uitableview user-interface


    【解决方案1】:

    如果表中有零行,scrollToRowAtIndexPath 方法会导致崩溃。

    UITableView 只是一个滚动视图,因此您可以简单地更改内容偏移量。

    这会检查您的 tableHeaderView 是否有某些内容,并假设您通过滚动来隐藏它

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

    【讨论】:

      【解决方案2】:

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

      经过一些调试,我意识到只有当 table view 内容大小小于 tableview 时才会出现问题。我在 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"];
      }
      

      【讨论】:

        【解决方案3】:

        我尝试设置内容偏移量和 scrollToIndexpath,但没有任何效果令人满意。我从 viewDidLoad 中删除了 tableHeaderView 作为 searchBar 的设置,并将其设置在滚动视图委托中,如下所示

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

        这就像一个魅力。内容偏移设置是为了平滑滚动

        【讨论】:

          【解决方案4】:

          下面的代码对我有用

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

          【讨论】:

            【解决方案5】:

            斯威夫特 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)
            }
            

            【讨论】:

              【解决方案6】:

              对于 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)
              

              【讨论】:

                【解决方案7】:

                对于斯威夫特:

                只需让tableview内容y偏移,应该是搜索栏的高度。

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

                【讨论】:

                  【解决方案8】:

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

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

                  为我工作。

                  【讨论】:

                    【解决方案9】:

                    内容偏移是可行的方法,但它并非 100% 有效。

                    如果将estimatedRowHeight 设置为固定数字,则不起作用。我还不知道周围的工作。我已经创建了一个 project 来显示问题,并且我已经创建了一个与 Apple 合作的 radar

                    如果您想快速了解如何设置它的示例,请查看该项目。

                    【讨论】:

                    • 谢谢!这就是我要找的。你需要往上走。
                    【解决方案10】:

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

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

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

                    如果你把上面的代码放在viewDidLoad上,会抛出一个错误,因为tableView还没有加载,所以你必须把它放在viewDidAppear,因为通过此时 tableView 已经加载。

                    如果你把它放在viewDidAppear上,每次打开tableView它都会滚动到顶部。

                    如果 tableView 保持打开状态,例如当它是 UITabBar 视图控制器或执行 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
                            }
                    

                    【讨论】:

                      【解决方案11】:

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

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

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

                      这是我在 Swift 中的做法:

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

                      var minimumCellNum: Int?
                      

                      2.) 计算minimumCellNum 并在viewWillAppear 中设置tableView.contentOffset

                      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.) 在情节提要和tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) 中注册一个selection 属性为None 的空单元格

                      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 执行此操作。单独设置 contentOffset 对 iOS 9/10 的 0 个或更多单元格有效,它也适用于 iOS 11 的 8+ 个单元格。任何更少的东西都需要这个解决方案。有什么原因吗?
                      • 我认为这篇文章的第一段解释了原因。
                      • 我理解了第一段,但对我来说它只是在 iOS 11 上被“破坏”仍然很奇怪。当单元格的数量相同(不足)时,我会期待 iOS 9、10 和 11 的类似行为。再次感谢并抱歉提出旧帖子!
                      【解决方案12】:

                      我的目标是在加载视图控制器时隐藏添加到情节提要中普通 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()
                      

                      【讨论】:

                        【解决方案13】:

                        这个问题的其他答案要么对我根本不起作用,要么在 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 是否可见(这意味着contentOffsety 位置,也就是滚动位置,是0)。如果是,它只是向下滚动 searchBar 的高度。如果 searchBar 不可见(想想tableView 中更大的项目列表),那么它不会尝试滚动 tableView,因为当您从嵌套的订单项返回时,这会打乱定位。

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

                        【讨论】:

                          【解决方案14】:

                          不要将 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 一致。其他“卡”在表格视图顶部的解决方案感觉不自然。
                          • 这个解决方案效果最好。这应该是我认为的答案。
                          • 这个解决方案很好(+1)。但是,tableView 在某些情况下不能正确隐藏搜索栏(在我的情况下:当 tableview 控制器从另一个视图控制器推回时)。关于这个问题有一个问题:stackoverflow.com/questions/15222186/… 我认为提供的这个问题的答案不是很好。 @bandejapaisa,你有什么想法吗?
                          • 这对我来说效果很好,虽然我建议在 viewWillAppear 中使用 self.tableView.tableHeaderView.frame.size.height 而不是硬编码 44 以允许 Apple 将来可能对搜索栏高度。
                          • 把它放在viewWillAppear 是个坏主意,因为它会在您每次导航回视图时运行。这意味着您的 tableview 将慢慢缩小。恼人的错误!
                          【解决方案15】:

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

                          唯一有效的:

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

                          【讨论】:

                          • 很好的解决偏移问题。使用 UINavigationController 时对我有用。
                          • 当行数没有覆盖整个屏幕时,该解决方案不起作用 - 您知道解决方法吗?
                          • 不确定我是否完全理解这一点。当表中只有几行时,这对我有用。但是我确实使用以下内容:self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];.
                          【解决方案16】:

                          首先确保将 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)
                          

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

                          【讨论】:

                          • 实际上,我遇到了一个问题,如果没有足够的行来填充屏幕,表格视图将不会滚动 - 在只有一两行的表格中,这段代码什么也不做。这是预期的行为吗?我能以某种方式绕过它吗?
                          • 我找到了一个方法:不滚动到索引路径,而是将表格的滚动视图的内容偏移更改为 CGRect 在 0.0, 44.0
                          • 蒂姆,这就是要走的路!我用 self.tableView.contentOffset = CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height);
                          • 顺便说一句 - 当我在调用 [tableView reloadData] 后输入此代码时,导致我的表格从 2 行变为足够的行以允许正常滚动,我发现使用 contentOffset 方法导致使用 scrollToRowAtIndexPath 方法时搜索框的显示和隐藏不平稳没有相同的不平稳。
                          • 这是在任何情况下都有效的唯一终极方式。如果您使用复杂的视图控制器布局,将contentOffset 设置为任何值都会中断。我不推荐它。
                          【解决方案17】:

                          以上方法都不适合我,所以以防万一有人遇到同样的问题。

                          我在 iOS7 的分组表上将 searchBar 设置为 tableHeaderView。在viewDidLoad 我有tableView.contentOffset = CGPointMake(0, 44); 保持searchBar 最初“隐藏”。当用户向下滚动时searchBar 被显示

                          目标:在点击取消按钮时隐藏搜索栏

                          searchBarCancelButtonClicked(searchBar: UISearchBar!)

                          这不起作用:[yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; tableView 向上滚动太多,导致第 0 行落后于工具栏。

                          这不起作用: 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 落后于工具栏

                          成功了: tableView.setContentOffset(CGPointMake(0, -20)

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

                          【讨论】:

                          • 你真的不应该硬编码你的观点,为什么不使用CGRectGetHeight(searchBar.bounds)
                          • 我猜你必须在 viewDidLoad() / init() 中进行,其中没有为你设置框架来获取搜索栏的高度。也许您应该尝试使用 viewDidLayoutSubviews()
                          • 如果tableView还没有完成加载,ContentOffset解决方案都不起作用
                          【解决方案18】:

                          请注意,如果 tableview 中没有数据,则接受的答案将崩溃。在某些情况下,将其添加到 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)];
                          }
                          

                          【讨论】:

                            【解决方案19】:

                            这个解决方案有很多答案,但没有一个对我很有效。

                            这非常有效。没有动画,在-viewDidLoad上完成

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

                            注意:

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

                            【讨论】:

                            • 这是唯一对我有用的! ,但它只会取消查看didload ...所以第一次隐藏它,但在de app运行时不再隐藏。我尝试将它放在 viewwillappear 和 viewdidappear 中,但工作错误。谢谢!
                            • @fguespe 这很奇怪。每次实例化视图时都应该调用 viewDidLoad。我从来没有遇到过 UISearchBar 从另一个视图返回后可见的情况。
                            • 我正在使用选项卡式应用程序。也许是这样?
                            • @fguespe 我知道这可能是问题嗯。不幸的是,如果-viewWillAppear 不起作用,我不确定你将如何解决这个问题。当标签控制器切换到视图时它会被调用吗?
                            • viewwillappear 和 viewdidappear 被调用,但我都试过了,视图就像错误的偏移。它没有返回预期的结果。我的意思是,有些事情确实如此,但它做得很糟糕。
                            【解决方案20】:

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

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

                            一旦用户向下滚动表格,搜索栏就会出现并正常运行

                            【讨论】:

                              【解决方案21】:

                              Tim 和 Joe D'Andrea 在 cmets 中记录了 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];
                              

                              【讨论】:

                              • 我发现动画是这个解决方案的关键。尝试在没有动画的情况下设置内容偏移量似乎无济于事。然而,我发现在这个示例中没有使用动画方法(或块),而是简单地调用 [self.tableView setContentOffset:CGPointMake(0,44) animated:TRUE] 就足够了。
                              • 没有动画似乎根本无法工作。 @quickthyme 也做了同样的观察。
                              • 如果你在viewDidAppear:而不是viewDidLoad中实现它,我已经成功了,不需要动画。
                              【解决方案22】:

                              我发现当 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];
                              }
                              

                              希望这会有所帮助:-)

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2011-03-08
                                • 1970-01-01
                                • 2019-05-21
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多