如果推送控制器隐藏标签栏,UISearchBar会跳转

时间:2014-03-06 18:08:14

标签: ios iphone objective-c uitabbarcontroller uisearchbar

我的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,它与我在问题中描述的结构相同 为方便起见,我添加了两个小节按钮,“隐藏栏”按钮为您隐藏搜索栏,“切换计数”按钮切换表格视图行计数,以证明只有少数项目才会出现问题。

12 个答案:

答案 0 :(得分:7)

好的..在我看来,你已经偶然发现了一个错误。它应该通过apples bugreporter(here)报告。

我做了一个简单易懂的工作,但请记住, 是一种解决方法。这将有效,但如果您有/向tableView添加其他控件,则可能需要查看它。它应该是安全的(不是随意的),并且它不是最难处理的,所以我认为在一个版本中使用它是好的。我已经使用修复程序here 上传了相同的项目,您可以继续下载它,您可能已经了解我已完成的工作。我将解释(非常详细)我在这里实际思考和完成的内容,以防下载链接在将来死亡:

思路:

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

当我们从ViewController返回时,会在viewDidLayoutSubviews中对TableViewController进行三次调用。我猜第一个是tableView,而第二个电话似乎是'(或者来自)tabBar。第二个是移动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];
    }
}

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

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

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

这是为了跟踪以后对viewDidLayoutSubviews进行了多少次通话。我在count = 0;中设置viewDidLoad

现在到了魔法:

-(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,搜索栏可见。
  3. 如果1.为真,那么我们将count增加1。 如果2.为真,那么1.已经发生了,我们现在知道当我们从 VC 回来并且searchBar viewDidLayout..的第二轮> WAS 隐藏(因为1.发生了)但现在并未隐藏。它可能发生在超级方法或其他东西。 现在我们终于可以再次推出searchBar了。我还重置了count并将backPush设置回NO

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

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

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

答案 1 :(得分: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)
    }
}

答案 2 :(得分:1)

尝试设置TVC

 self.automaticallyAdjustsScrollViewInsets = NO

答案 3 :(得分:0)

这是由hidesBottomBarWhenPushed = YES引起的问题,如果您取消选中Hide Bottom Bar On Push,则当VC返回TVC时,不会显示searchBar。

在TableViewController.m中尝试:

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

我无法解释原因,但我知道如果hidesBottomBarWhenPushed = YES用于UITabBarController推送vc,viewDidLayoutSubviews将在视图再次出现时被多次调用。第一次子视图保持相同的位置,而第二次被调用,子视图将由于某种原因进行调整,以最原始的位置重新布局,这是非常奇怪的。在viewDidLayoutSubviews中进行自定义布局是否会阻止viewDidAppear发生这种情况。

答案 4 :(得分:0)

UITableViewController始终在其viewDidAppear中修改其UITableviews内容偏移量,以确保其所有行都可见。所以你的hacky方法在这里不起作用。

这个问题有几个解决方案。我选择的那个如下所示

首先从故事板中删除该搜索栏。

@interface TableViewController () {
    NSInteger _rows;
}

@end

@implementation TableViewController

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    _rows = 4; // +1 for searchBar

}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
}

- (void)hideSearchBar {
    // hide search bar
    [[self tableView] scrollToRowAtIndexPath:[NSIndexPath indexPathWithIndex:1] atScrollPosition:UITableViewScrollPositionTop animated:NO];
}

- (IBAction)toggleCount:(UIBarButtonItem *)sender {
    if (_rows == 20) {
        _rows = 4;
    } else {
        _rows = 20;
    }
    [self.tableView 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
{

    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)];
        [cell addSubview:searchBar];
        return cell;
    }

    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...
    cell.textLabel.text = @"cell";

    return cell;
}

@end

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

如果您希望隐藏默认的searchBar,请覆盖UITableView,并在首次加载tableview时调用hideSearchBar。

答案 5 :(得分:0)

我的解决方案有点愚蠢。

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

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

似乎tableView会重绘其中的scrollView。

答案 6 :(得分:0)

由于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 :(得分:0)

我修复了这个错误:

@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

答案 8 :(得分:0)

修复了与UISearchBar tableHeaderView - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; } 类似的情况。不确定这是否属于同一场景,但在视图出现时会隐藏搜索栏。 (不关心表格视图中的行数)

bookings:size

答案 9 :(得分:0)

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

当然,automaticallyAdjustsScrollViewInsets设置为false
编辑:似乎只有在tvc.tabBar为translucent

的情况下才有效

答案 10 :(得分:-1)

作为一个奇怪的黑客,我只能建议在高度约为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;
}
}

答案 11 :(得分:-1)

您的输出文件

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),则SearchBar在原始模式下跳转,这是一个问题