如果推送控制器隐藏标签栏,UISearchBar 会跳转
Posted
技术标签:
【中文标题】如果推送控制器隐藏标签栏,UISearchBar 会跳转【英文标题】:UISearchBar jumps if pushed controller hides the tab bar 【发布时间】:2014-03-06 18:08:14 【问题描述】:我的 UI 结构如下:
UITabBarController
(TBC) -> UINavigationController
(NC) -> UITableViewController
(TVC)
(为简单起见,假设 TBC 在其viewControllers
阵列上只有一个控制器 - NC)
我的 TVC 将 UISearchBar
作为其表头,当 TVC 出现时,我将搜索栏隐藏在 NC 导航栏下方通过设置表视图内容偏移量。
当用户点击 TVC 中的一个单元格时,会推送另一个视图控制器 (VC) 并使用 VC.hidesBottomBarWhenPushed = YES;
隐藏标签栏
现在有一个很烦人的行为我不知道如何解决: 当用户从 VC 点按返回按钮回到 TVC 时,搜索栏会跳转到可见,即使它在 之前隐藏(在导航栏下方) VC 被推送。
这种效果只有在 TVC 没有足够的行来填满屏幕时才会发生,就像如果屏幕上有一个位置,搜索栏会强制自己可见。但它真的看起来很糟糕而且有问题。
我上传了一个 simple project 来演示该问题,它与我在问题中描述的结构相同。 为方便起见,我添加了两个栏按钮,“隐藏栏”按钮为您隐藏搜索栏,“切换计数”按钮切换表格视图的行数以证明问题仅在项目很少时才会发生。
【问题讨论】:
视频/示例项目会有所帮助(我知道重新创建很简单,但可以帮助缩小可能的解决方案) @staticVoidMan 我用一个示例项目更新了我的问题。 hm... 确实很奇怪,当您选择 searchBar 并且当键盘在屏幕上并且您按下并弹回时,搜索栏会变得更低。 ...让c @Eyal 你的问题解决了,检查我的解决方案 简单的逻辑为什么你会打破你的脑袋 【参考方案1】:好的.. 在我看来你偶然发现了一个错误。应该通过 apples bugreporter (here) 报告。
我做了一个简单的变通办法,但请记住,它是变通办法。这将起作用,但如果您有/向 tableView 添加其他控件,您可能需要查看它。它应该是安全的(不是随机行为),而且它不是最丑陋的变通方法,所以我认为在发布中使用它是可以的。我已经上传了相同的项目,修复了here,你可以继续下载它,你可能会明白我做了什么。我将(非常详细地)解释我在这里的实际想法和做法,以防下载链接将来失效:
思路:
正如 simalone 所说,问题是当 hidesBottomBarWhenPushed
设置为 YES
时,它会调用一个额外的 viewDidLayoutSubviews
以某种方式重置您当前的状态。我们需要覆盖viewDidLayoutSubviews
,并检查我们是否正在布置子视图因为我们来自ViewController
,或者这只是一个常规调用。当我们确定调用确实是因为我们从ViewController
返回时,我们需要隐藏搜索栏(仅当它之前隐藏时)。
当我们从ViewController
返回时,在TableViewController
中对viewDidLayoutSubviews
进行了三个调用。我猜第一个是为tableView
,似乎第二个调用是'for'(或者更确切地说是来自)tabBar。这第二个是将searchBar
向下移动的那个。我不知道第三个电话是什么,但我们可以忽略它。
所以现在我们需要在viewDidLayoutSubviews
中检查三件事:我们需要检查我们是否从ViewController
返回,我们需要检查 searchBar 在我们推送之前是否隐藏(如果应该现在隐藏),我们需要检查它是否是对这个方法的第二次调用。
首先要做的事情。
在TableViewController
中,我在标题(.h
) 文件中添加了一个属性@property BOOL backPush;
。现在我需要从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];
在上面的代码中,当视图消失(即向前、向后、关闭等等)时,我正在检查我们是否因为它已从父视图中删除而消失。如果是(调用后退按钮时),我检查 now-current 顶视图控制器是否属于 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 语句需要以下任何一种情况:
backPush
是 YES
,count
是 0
,并且 searchBar 已经隐藏。
backPush
是 YES
,count
是 1
,并且 searchBar 是可见的。
如果 1. 为真,那么我们将 count
加 1。
如果 2. 是真的,那么 1. 已经发生了,我们现在知道当我们从 VC 回来时我们正处于第二轮 viewDidLayout..
并且 searchBar WAS 隐藏(因为 1. 发生了)但现在没有隐藏。它可能发生在超级方法或其他东西中。
现在我们终于可以再次推出 searchBar 了。我还重置了计数并将 backPush 设置回NO
。
else if
也很重要。它检查count
是0
还是1
,或者搜索栏是否显示键盘。如果 count 到达这里时是 0
或 1
,则意味着第一个 if 语句失败,例如 searchBar 没有隐藏,或者它被向上滚动得很远。
(当我想到它时,else-if 应该检查backPush
是否也是YES
。现在它重复设置这些变量)
如果你找到更好的方法,请告诉我!
【讨论】:
感谢您的详细回答,但这看起来是一个非常大的解决方法,所以我现在更愿意避免它,为您的努力 +1 @Eyal 我同意,但问题似乎是一个错误,所以这是我能想到的临时修复的唯一方法。您应该将其报告给 Apple 的 bugreporter。还是您找到了其他解决方案?【参考方案2】:我认为这是一个简单的解决方案。谢谢 Sti
提供一些解决此错误的想法。
初始化变量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)
【讨论】:
【参考方案3】:尝试设置为 TVC
self.automaticallyAdjustsScrollViewInsets = NO
【讨论】:
【参考方案4】:这是hidesBottomBarWhenPushed = YES
引起的问题,如果不勾选Hide Bottom Bar On Push
,VC回TVC时不会出现searchBar。
在 TableViewController.m 中试试这个:
- (void)viewDidLayoutSubviews
[super viewDidLayoutSubviews];
[self hideSearchBar];
我无法解释为什么,但我知道如果 hidesBottomBarWhenPushed = YES
让 UITabBarController 推送 vc,当视图再次出现时,viewDidLayoutSubviews
将被多次调用。第一次子视图保持相同的位置,而第二次被调用时,子视图会由于某种原因被调整到最原始的位置,这很奇怪。即使在viewDidAppear
之后,在viewDidLayoutSubviews
中进行自定义布局也可以防止这种情况发生。
【讨论】:
这将阻止用户完全显示搜索栏 @Eyal 你应该做什么viewDidLayoutSubviews
取决于你自己。这里只是一个如何控制搜索栏外观的例子,您可以将外观保存在viewDidDisappear
,然后在viewDidAppear
之后恢复到viewDidLayoutSubviews
。【参考方案5】:
我的解决方案有点愚蠢。
将此方法添加到示例代码中。
- (void)viewWillLayoutSubviews
[self hideSearchBar];
好像tableView会重绘里面的scrollView。
【讨论】:
【参考方案6】:由于tableView重置了contentOffset,所以我做了一个自定义tableView有属性来保存搜索栏的隐藏状态。下面是代码。希望对你有帮助。
//
// 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
【讨论】:
【参考方案7】:UITableViewController 总是在其 viewDidAppear 中修改其 UITableviews 内容偏移量,以确保其所有行都是可见的。所以你的hacky方法在这里不起作用。
这个问题有几种解决方案。我选择的如下图所示
首先从情节提要中删除该搜索栏。
@interface TableViewController () NSInteger _rows; @结尾 @implementation 表视图控制器 - (id)initWithStyle:(UITableViewStyle)style self = [超级 initWithStyle:style]; 如果(自我) // 自定义初始化 回归自我; - (void)viewDidLoad [超级视图DidLoad]; _rows = 4; // +1 搜索栏 -(void)viewDidAppear:(BOOL)动画 [超级 viewDidAppear:动画]; - (void)hideSearchBar //隐藏搜索栏 [[self tableView] scrollToRowAtIndexPath:[NSIndexPath indexPathWithIndex:1] atScrollPosition:UITableViewScrollPositionTop 动画:NO]; - (IBAction)toggleCount:(UIBarButtonItem *)sender 如果(_rows == 20) _rows = 4; 别的 _rows = 20; [self.tableView reloadData]; - (IBAction)hideBar:(UIBarButtonItem *)sender [自我隐藏搜索栏]; #pragma mark - 表格视图数据源 - (NSInteger)numberOfSectionsInTableView:(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]; // 配置单元格... 单元格.textLabel.text = @"单元格"; 返回单元格; @结尾上述解决方案只是确保禁用自动滚动魔法。
如果您希望隐藏默认搜索栏,请覆盖 UITableView 并在第一次初始加载 tableview 时调用 hideSearchBar。
【讨论】:
sorry你错了,搜索栏是table view header view,你可以直接打印self.tableView.tableHeaderView看看。 tableHeadrView 也是 tableView 的一个子视图,所以当然你会在打印所有子视图时看到它......无论如何我不喜欢使用你的方法,因为在这里将搜索栏添加到单元格不是一个理想的解决方案。跨度> @Eyal 你是对的。但是,我的解决方案很简单,理想与否是你的选择。【参考方案8】:我就是这样修复错误的:
@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
【讨论】:
【参考方案9】:用UISearchBar
和tableHeaderView
修复我有点类似的情况。不确定这是否属于相同的确切场景,但是当视图出现时它会隐藏搜索栏。 (不关心表格视图中的行数)
- (void)viewWillAppear:(BOOL)animated
[super viewWillAppear:animated];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
【讨论】:
【参考方案10】:在 TVC 上将 edgesForExtendedLayout
设置为 [.top, .bottom]
而不仅仅是 .top
为我解决了问题。
当然,automaticallyAdjustsScrollViewInsets
设置为 false
编辑: 似乎只有 tvc.tabBar 为 translucent
时才有效
【讨论】:
【参考方案11】:作为一个奇怪的 hack,我只能建议在高度约为 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;
【讨论】:
【参考方案12】:你的输出文件
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) 会出现一个问题,搜索栏会像原始模式一样跳跃
【讨论】:
以上是关于如果推送控制器隐藏标签栏,UISearchBar 会跳转的主要内容,如果未能解决你的问题,请参考以下文章