我的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 语句需要以下任一情况:
backPush
YES
,count
0
,搜索栏已经隐藏。backPush
YES
,count
1
,搜索栏可见。
如果 1. 为真,则我们将count
递增 1。 如果 2.为真,则为 1。已经发生了,我们现在知道,当我们从VC回来时,我们正处于第二轮viewDidLayout..
,并且搜索栏被隐藏了(因为发生了1.),但现在没有隐藏。它可能发生在超级方法或其他东西中。 现在我们终于可以再次推出搜索栏了。我还重置计数并设置回推回NO
。
else if
也非常重要。它检查count
是0
还是1
,或者搜索栏是否显示键盘。如果 count 到达此处时0
或1
,则表示第一个 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]
,而不仅仅是.top
TVC为我解决了问题。
当然,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)搜索栏跳跃像原始模式一样