如果按下的控制器隐藏了标签栏,UISearchBar 会跳跃



我的UI结构如下:

UITabBarController(待定) ->UINavigationController(NC)->UITableViewController(TVC)

(为了示例的简单性,假设TBC在其viewControllers阵列上只有一个控制器 -NC)

我的TVCUISearchBar作为其表标题,当TVC出现时,我通过设置表视图内容偏移量将搜索栏隐藏在NC导航栏下方。

当用户点击TVC中的单元格时,将推送另一个视图控制器 (VC)并隐藏选项卡栏并显示VC.hidesBottomBarWhenPushed = YES;

现在有一个非常烦人的行为我不知道如何解决:
当用户点击VC的后退按钮返回TVC时,搜索栏会跳跃可见,即使它在按下VC之前被隐藏(在导航栏下方)。

仅当TVC没有足够的行来填充屏幕时,才会发生这种效果,就像如果屏幕上有一个位置,搜索栏会强制自己可见。 但它看起来真的很糟糕而且有问题。

我上传了一个简单的项目来演示这个问题,它的结构与我在问题中描述的结构相同。
为了方便起见,我添加了两个栏按钮,"隐藏栏"按钮为您隐藏搜索栏,"切换计数"按钮切换表视图行计数,以证明问题仅在项目很少时才发生。

好的。在我看来,你好像偶然发现了一个错误。它应该通过苹果错误报告器(这里)报告。

我已经做了一个仙女般的简单工作解决方法,但请记住,这是一个解决方法。这将起作用,但是如果您具有/向 tableView 添加其他控件,则可能需要查看它。它应该可以安全使用(而不是随机操作),而且它不是最丑陋的解决方法,所以我认为在发行版中使用是可以的。我已经在这里上传了带有修复程序的相同项目,您可以继续下载它,您可能会了解我所做的工作。我将(非常详细地)解释我在这里的实际想法和所做的事情,以防下载链接在未来死亡:

思路:

正如 simalone 所说,问题是当hidesBottomBarWhenPushed设置为YES,然后它会调用一个额外的viewDidLayoutSubviews,以某种方式重置您的当前状态。我们需要覆盖viewDidLayoutSubviews,并检查我们是否因为来自ViewController而布置子视图,或者它是否只是一个常规调用。当我们确定呼叫确实是因为 我们从ViewController返回 ,我们需要隐藏搜索栏(仅当它之前被隐藏时)。

当我们从ViewController回来时,在TableViewController中拨打了三个电话给viewDidLayoutSubviews。我猜第一个是针对tableView的,似乎第二个调用是"for"(或者更确切地说是来自)选项卡栏。第二个是向下移动searchBar的那个。我不知道第三个电话是什么,但我们可以忽略它。

所以现在我们需要在viewDidLayoutSubviews内部检查三件事:我们需要检查我们是否从ViewController返回,我们需要检查在我们推送之前是否隐藏了 searchBar(如果它现在应该隐藏),我们需要检查它是对此方法的第二次调用。

首先要做的事。

TableViewController,我在头文件中添加了一个属性@property BOOL backPush;(.h)文件。现在我需要将此变量从ViewController.

ViewController,我把这个:

#import "TableViewController"
...
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if(self.isMovingFromParentViewController)
{
if([self.navigationController.topViewController isKindOfClass:[TableViewController class]])
[((TableViewController*)self.navigationController.topViewController) setBackPush:YES];
}
}

在上面的代码中,当视图消失(即向前、向后、关闭等)时,我正在检查我们是否消失了,因为它已从父级中删除。如果是(调用后退按钮时),我会检查当前当前的顶视图控制器是否属于TableViewController类,如果我们返回,也是如此。然后我将属性backPush设置为YES。这是我们在ViewController中唯一需要的东西.

现在,到TableViewController.我在您的行数旁边添加了一个计数器

@interface TableViewController () {
NSInteger _rows;
int count;
}

这是为了跟踪以后向viewDidLayoutSubviews进行了多少次调用。我在viewDidLoad中设置了count = 0;.

现在来看魔法:

-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if((self.backPush && count == 0 && self.tableView.contentOffset.y == 
self.tableView.tableHeaderView.frame.size.height) || 
(self.backPush && count == 1 && 
self.tableView.contentOffset.y == 0))
{
if(count == 0)
count++;
else
{
count = 0;
self.backPush = NO;
[self hideSearchBar];
}
}
else if((count == 0 || count == 1) || self.tableView.tableHeaderView.isFirstResponder)
{
count = 0;
self.backPush = NO;
}
}

第一个 if 语句需要以下任一情况:

  1. backPushYEScount0,搜索栏已经隐藏。
  2. backPushYEScount1,搜索栏可见。

如果 1. 为真,则我们将count递增 1。 如果 2.为真,则为 1。已经发生了,我们现在知道,当我们从VC回来时,我们正处于第二轮viewDidLayout..,并且搜索栏隐藏了(因为发生了1.),但现在没有隐藏。它可能发生在超级方法或其他东西中。 现在我们终于可以再次推出搜索栏了。我还重置计数并设置回推回NO

else if也非常重要。它检查count0还是1,或者搜索栏是否显示键盘。如果 count 到达此处时01,则表示第一个 if 语句失败,例如搜索栏没有隐藏,或者它向上滚动了很远。

(当我想到它时,其他 if 也应该检查backPush是否YES。现在它重复设置这些变量)

如果您找到更好的方法,请告诉我!

我认为这是简单的解决方案。由于 性病

提供一些想法来解决此错误。

初始化变量var hideSearchBar = false

并在 viewDidLayoutSubviews 中添加此代码以保持相同的内容偏移量。

if hideSearchBar == true {
self.tableView.contentOffset = CGPointMake(0,  self.tableView.tableHeaderView!.bounds.height - self.tableView.contentInset.top)
}

最后实现以下方法。

override func scrollViewDidScroll(scrollView: UIScrollView) {
if self.tableView.tableHeaderView!.bounds.height - self.tableView.contentInset.top == self.tableView.contentOffset.y && self.tableView.dragging == false {
hideSearchBar = true
}
else if self.tableView.dragging == true {//Reset hiding process after user dragging
hideSearchBar = false
}
}
func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if self.tableView.contentOffset.y + self.tableView.contentInset.top <= self.tableView.tableHeaderView!.bounds.height
{
self.tableView.contentOffset = CGPointMake(0, self.tableView.tableHeaderView!.bounds.height - self.tableView.contentInset.top)
}
}

尝试为 TVC 设置

self.automaticallyAdjustsScrollViewInsets = NO

这是hidesBottomBarWhenPushed = YES引起的问题,如果取消选中Hide Bottom Bar On Push,VC回到TVC时搜索栏不会出现。

在 TableViewController.m 中试试这个:

- (void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self hideSearchBar];
}

我无法解释为什么,但我知道如果 UITabBarControllerhidesBottomBarWhenPushed = YES推送 vc,当视图再次出现时,viewDidLayoutSubviews将被多次调用。第一次子视图保持相同的位置,而第二次调用子视图时,由于某种原因会调整子视图以使用最原始的位置重新布局,这很奇怪。在viewDidLayoutSubviews中做自定义布局即使在viewDidAppear之后也会防止这种情况发生。

我的解决方案有点愚蠢。

将此方法添加到示例代码中。

- (void)viewWillLayoutSubviews
{
[self hideSearchBar];
}

似乎表视图将在其中重绘滚动视图。

由于表视图重置了内容偏移量,因此我制作了一个自定义表视图具有保存搜索栏隐藏状态的属性。下面是代码。希望对您有所帮助。

//
//  TableViewController.m
//  SearchBarJump
//
//  Created by Eyal Cohen on 3/9/14.
//  Copyright (c) 2014 Eyal. All rights reserved.
//
#import "TableViewController.h"

@interface CustomTableView : UITableView
@property (nonatomic, assign, getter = isSearchBarHidden)BOOL searchBarHidden;
@end
@implementation CustomTableView
@synthesize searchBarHidden = _searchBarHidden;
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.isSearchBarHidden) {
[self hideSearchBar:NO];
}
}
- (void)setSearchBarHidden:(BOOL)searchBarHidden
{
_searchBarHidden = searchBarHidden;
if (_searchBarHidden && self.contentOffset.y != self.tableHeaderView.frame.size.height) {
[self hideSearchBar:YES];
}
}
- (void)hideSearchBar:(BOOL)animated {
// hide search bar
[self setContentOffset:CGPointMake(0.0, self.tableHeaderView.frame.size.height) animated:animated];
}
@end
@interface TableViewController () {
NSInteger _rows;
}
@property (nonatomic, weak) IBOutlet CustomTableView *mainTable;
@end
@implementation TableViewController
@synthesize mainTable = _mainTable;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view = _mainTable;
[_mainTable setDelegate:self];
[_mainTable setDataSource:self];
_rows = 3;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.mainTable setSearchBarHidden:YES];
}

- (void)hideSearchBar {
// hide search bar
[_mainTable setContentOffset:CGPointMake(0.0, self.tableView.tableHeaderView.frame.size.height) animated:NO];
}
- (IBAction)toggleCount:(UIBarButtonItem *)sender {
if (_rows == 20) {
_rows = 3;
} else {
_rows = 20;
}
[_mainTable reloadData];
}
- (IBAction)hideBar:(UIBarButtonItem *)sender {
[self hideSearchBar];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return _rows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = @"cell";
return cell;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[_mainTable setSearchBarHidden:NO];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
if (_mainTable.contentOffset.y == _mainTable.tableHeaderView.bounds.size.height) {
[_mainTable setSearchBarHidden:YES];
}
}
@end

UITableViewController 总是在其 viewDidSeem 中修改其 UITableviews 内容偏移量,以确保其所有行都可见。所以你的黑客方法在这里不起作用。

此问题有几种解决方案。我选择的如下所示

首先从情节提要中删除该搜索栏。

@interface 表视图控制器 () { NSInteger _rows; } @end @implementation 表视图控制器 - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; 如果(自我){ 自定义初始化 } 回报自我; } - (空)视图加载 { [超级视图确实加载]; _rows = 4;+1 用于搜索栏 } -(void)viewDidAppear:(BOOL)animated{ [超级视图确实出现:动画]; } - (无效)隐藏搜索栏 { 隐藏搜索栏 [[self tableView] scrollToRowAtIndexPath:[NSIndexPath indexPathWithIndex:1] atScrollPosition:UITableViewScrollPositionTop animated:NO]; } - (IBAction)toggleCount:(UIBarButtonItem *)sender { 如果 (_rows == 20) { _rows = 4; } else { _rows = 20; } [self.tableView reloadData]; } - (IBAction)hideBar:(UIBarButtonItem *)sender { [自我隐藏搜索栏]; } #pragma 标记 - 表视图数据源 - (NSInteger)numberOfSectionInTableView:(UITableView *)tableView { 返回节数。 返回 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 返回节中的行数。 返回_rows; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if(indexPath.row == 0){ UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width,44)]; [单元格添加子视图:搜索栏]; 返回细胞; } 静态 NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 配置单元... cell.textLabel.text = @"cell"; 返回细胞; } @end

上述解决方案仅确保禁用自动滚动魔法。

如果您希望隐藏默认搜索栏,请覆盖UITableView,并在首次加载表视图时调用hideSearchBar。

我像这样修复了这个错误:

@interface NTTableView : UITableView
@end
@implementation NTTableView
-(void)setContentOffset:(CGPoint)contentOffset{
if (self.contentOffset.y==-20&&
contentOffset.y==-64) {
NSLog(@"iOS7 bug here, FML");
}else{
[super setContentOffset:contentOffset];
}
}
@end

修复了我与UISearchBar有点相似的情况,作为tableHeaderView。不确定这是否属于完全相同的情况,但它会在视图出现时隐藏搜索栏。(不关心表视图中的行数)

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
}

edgesForExtendedLayout设置为[.top, .bottom],而不仅仅是.topTVC为我解决了问题。

当然,automaticallyAdjustsScrollViewInsets设置为false
编辑似乎只有在tvc.tabBartranslucent时才有效

作为一个奇怪的黑客,我只能建议在高度约为 400 的单元格末尾添加一个空单元格

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return _rows + 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(indexPath.row == _rows)
{
//cellEmpty - cell identifier in storyboard
cell = [tableView dequeueReusableCellWithIdentifier:@"cellEmpty" forIndexPath:indexPath];
}
else
{
cell.textLabel.text = @"cell";
}
// Configure the cell...
return cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == _rows)
{
return 400;
}
else
{
return 44;
}
}

输出文件

https://github.com/iDevAndroid/SearchBarJump

只需使用此代码,不要为此做复杂的事情

-(void)viewDidDisappear:(BOOL)animated{
[self.tableView setContentInset:UIEdgeInsetsMake(-0.3, 0, 0, 0)];
[super viewDidAppear:animated];
}

这是一个问题,如果你设置UIEdgeInsetsMake(0, 0, 0, 0)搜索栏跳跃像原始模式一样

最新更新