ChatGPT解决这个技术问题 Extra ChatGPT

iPhone:默认隐藏 UITableView 搜索栏

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

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


e
eonil

首先确保将 UISearchBar 添加到 UITableView 的 tableHeaderView 中,以便它与表格的内容一起滚动并且不固定在视图的顶部。

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

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

或在斯威夫特:

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

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


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

不要将 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 将慢慢缩小。恼人的错误!
S
Sam Spencer

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];


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

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

这完美地工作。没有动画,在 -viewDidLoad 完成

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

笔记:

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


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

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

k
kbpontius

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

B
Brian

当没有足够的行来填充 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 会有类似的行为。再次感谢并抱歉提出旧帖子!
g
grep

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

我在 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 行落后于 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)
我猜你必须在 viewDidLoad() / init() 中执行此操作,其中没有为你设置任何框架来获取搜索栏的高度。也许您应该尝试使用 viewDidLayoutSubviews()
如果 tableView 尚未完成加载,则 ContentOffset 解决方案均无效
R
RyanJM

内容偏移是要走的路,但它不是 100% 的时间。

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

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


谢谢!这就是我要找的。你需要往上走。
d
dibi

请注意,如果 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)];
}

p
p0lAris

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

唯一有效的:

- (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];
C
Confused Vorlon

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

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

这会检查您是否有一些东西作为您的 tableHeaderView,并假设您滚动隐藏它

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

C
Chilly Zhong

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

希望这可以帮助 :-)


L
LightMan

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

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

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


k
kbpontius

这个问题的其他答案要么对我根本不起作用,要么在 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,因为这会在您从嵌套订单项返回时弄乱定位。

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


d
drv

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

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

self.tableView.selectRowAtIndexPath(firstIndexPath, 动画: 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
        }

A
Ashu

下面的代码对我有用

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

A
Abdur Rahman

我尝试设置内容偏移量和 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)
    }
}

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


f
fujianjin6471

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

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

为我工作。


s
stakahop

对于斯威夫特:

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

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

J
Johannes Knust

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

s
slobodans

我有类似的问题。我发现解决此问题的唯一方法是在 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"];
}