iPhone: скрыть панель поиска UITableView по умолчанию


Я использовал построитель интерфейса для создания табличного представления, к которому я добавил панель поиска библиотеки и контроллер отображения поиска для добавления функций поиска. Однако IB настроил его так, чтобы панель была видна в верхней части экрана при первом отображении представления.

Я хотел бы знать, как скрыть строку поиска по умолчанию, но все еще прокручивать с табличным представлением (см. Приложение Apple Mail для примера). Я пробовал звонить scrollRectToVisible:animated: на viewDidLoad для прокрутки вид таблицы вниз, но безрезультатно. Каков предпочтительный способ скрытия строки поиска по умолчанию?

22 84

22 ответа:

сначала убедитесь, что вы добавили UISearchBar в tableHeaderView UITableView, чтобы он прокручивался с содержимым таблицы и не фиксировался в верхней части представления.

панель поиска не считается строкой в tableview, поэтому, если вы прокручиваете верхнюю часть tableview до первой строки, она "скрывает" панель поиска:

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

или в Swift:

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

убедитесь, что не прокручивать tableview, прежде чем он содержит данные (scrollToRowAtIndexPath будет поднять исключение, если данный indexPath не указывает на допустимую строку (т. е. если tableview пуст)).

не ' добавить 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];
}

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

есть много ответов на это решение, однако никто не работал очень хорошо для меня.

это отлично работает. Нет анимации, и делается на -viewDidLoad

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

Примечание:

код предполагает, что у вас есть searchBar@property который связан с панелью поиска. Если нет, то вы можете использовать self.searchDisplayController.searchBar вместо

моя цель состояла в том, чтобы скрыть панель поиска, добавленную в tableHeaderView стиля plain TableView в раскадровке при загрузке контроллера вида и показать панель, когда пользователь прокрутит вниз в верхней части UITableView. Итак, я использую Свифт и добавил расширение:

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()

Для Swift 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)

ни один из вышеперечисленных не работал для меня, так что на всякий случай, если у кого-то будет такая же проблема.

у меня есть searchBar Как tableHeaderView на сгруппированной таблице на iOS7. В viewDidLoad у меня есть tableView.contentOffset = CGPointMake(0, 44); сохранить searchBar изначально "скрытый". Когда пользователь прокручивает вниз searchBar Это dsiplayed

цель: скрыть панель поиска на кнопке Отмена нажмите

на searchBarCancelButtonClicked(searchBar: UISearchBar!)

это не работает:[yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; tableView прокручивал слишком много вверх, в результате строка 0 идет за панель инструментов.

это не работает:tableView.ContentOffset = CGPointMake(0, 44) - ничего не происходит

это не работает:tableView.ContentOffset = CGPointMake(0, searchBar.frame.size.height) - ничего не происходит

это не работает:tableView.setContentOffset(CGPointMake(0, 44), animated: true); Снова tableView прокручивал слишком много вверх, в результате чего строка 0 шла за панелью инструментов.

это частично сработало:tableView.setContentOffset(CGPointMake(0, 0) но titleForHeaderInSection шел за панелью инструментов

ЭТО СРАБОТАЛО:tableView.setContentOffset(CGPointMake(0, -20)

кажется не логичным, но только -20 переместил его в правильное положение

все существующие решения не работают для меня на iOS 8, когда не хватает строк для заполнения tableView, так как iOS автоматически настроит вставку в этой ситуации. (Существующие ответы хороши, когда есть достаточно строк, хотя)

потратив около 6 часов на эту проблему, я, наконец, получил это решение.

короче говоря, вам нужно вставить пустые ячейки в tableView, если их недостаточно, поэтому размер содержимого tableView достаточно велик, чтобы iOS не будет настраивать вставку для вас.

вот как я сделал это в Swift:

1.) объявить переменную minimumCellNum в категории

var minimumCellNum: Int?

2.) вычислить minimumCellNum и set tableView.contentOffset на viewWillAppear

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.) Регистрируем пустую ячейку, у которой и None, на раскадровке и в tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)

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. Я хотел бы знать, есть ли более аккуратное решение.

смещение содержимого-это путь, но он не работает в 100% случаев.

Это не работает, если вы устанавливаете estimatedRowHeight на фиксированное число. Я еще не знаю, как это сделать. Я создал проект показывает проблему(Ы) и я создал радар С Apple.

Проверьте проект, если вы хотите пример в swift, как настроить все это.

обратите внимание, что принятый ответ рухнет, если нет данных в 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)];
}

честно говоря, ни один из ответов действительно решить проблему (для меня). Если в представлении таблицы нет строк или высота строк отличается, этих ответов недостаточно.

единственное, что работает:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.tableView.contentOffset = (CGPoint){.y =  self.tableView.contentOffset.y + self.searchController.searchBar.frame.size.height};
}

Я считаю, что приложение "Заметки"не может искать элементы, когда tableView пуст. Поэтому я думаю, что это просто использование -(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];
}

надеюсь, что это помогает :-)

измените границы tableView, это можно сделать внутри viewDidLoad плюс вам не нужно беспокоиться о том, если таблица пуста или нет (чтобы избежать исключения).

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

Как только пользователь прокрутил таблицу, появится строка поиска и будет вести себя нормально

другие ответы на этот вопрос либо вообще не работали для меня, имели странное поведение, когда 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 (что означает y положение contentOffset, ака положение прокрутки, это 0). Если это так, он просто прокручивает вниз по высоте панели поиска. Если панель поиска не видна (подумайте о большем списке элементов в tableView), то он не будет пытаться прокрутить tableView, так как это испортит позиционирование при возврате из вложенного элемента строки.

Примечание
я бы рекомендовал поместить этот код в отдельный метод, чтобы обеспечить единую ответственность и дать вам возможность повторно использовать его в любое время ваш код.

Если ваша таблица всегда будет иметь хотя бы одну строку, просто прокрутите до первой строки таблицы, и строка поиска будет автоматически скрыта.

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

self.tableView.selectRowAtIndexPath(firstIndexPath, animated: false, scrollPosition: .Сверху)

Если вы ставите выше код на viewDidLoad, он выдаст ошибку, потому что tableView еще не загружен, поэтому вы должны поместить его в viewDidAppear потому что к этому моменту tableView уже загрузился.

Если вы наденете его viewDidAppear, каждый раз, когда вы открываете tableView он будет прокручиваться вверх.

возможно, вы не хотите такого поведения, если tableView остается открытым, например, когда это контроллер вида UITabBar или когда вы делаете сегмент, а затем возвращаетесь. Если вы просто хотите, чтобы он прокручивался вверх при начальной загрузке, вы можете создать переменную, чтобы проверить, является ли она начальной загрузкой, чтобы она прокручивалась вверх только один раз.

сначала определите переменную с именем isInitialLoad в классе контроллера вида и установите ее равной "true":

var isInitialLoad = true

затем проверьте, является ли isInitialLoad true на viewDidAppear и если это правда, прокрутите вверх и установите переменную isInitialLoad в false:

if isInitialLoad {
            let firstIndexPath = NSIndexPath(forRow: 0, inSection: 0)
            self.tableView.selectRowAtIndexPath(firstIndexPath, animated: false, scrollPosition: .Top)

            isInitialLoad = false
        }

ниже код работает для меня

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

The scrollToRowAtIndexPath метод вызывает сбой, если в таблице есть нулевые строки.

UITableView-это просто вид прокрутки, поэтому вы можете просто изменить смещение содержимого.

это проверяет, что у вас есть панель поиска в качестве tableHeaderView, и предполагая, что вы делаете прокрутки, чтобы скрыть его

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

Не забудьте взять строка состояния и панель навигации на рассмотрение. Мой контроллер табличного представления встроен в навигационный контроллер, в viewDidAppear:

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

работал для меня.

Для Swift:

просто сделайте TableView content y offset, который должен быть похож на высоту панели поиска.

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

Swift 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)
}

Я попытался установить смещение содержимого и scrollToIndexpath, но ничего не сработало удовлетворительно. Я удалил параметр tableHeaderView as searchBar из viewDidLoad и установил его в делегате scrollview, как показано ниже

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)
    }
}

это работает как шарм.Настройка смещения содержимого предназначена для плавной прокрутки

У меня была аналогичная проблема. И единственный способ, который я нашел, чтобы решить эту проблему, состоял в том, чтобы использовать key value observer на свойстве UITableView contentOffset.

после некоторой отладки я понял, что проблема появляется только в том случае, если размер содержимого представления таблицы меньше, чем tableview. Я начинаю кво, когда на viewWillAppear. И отправка остановить кво 0,3 секунды после просмотра появляются. Каждый раз, когда contentOffset стал 0.0, KVO реагирует и устанавливает contentOffset на высоту строки поиска. Я останавливаю кво асинхронный через 0,3 секунды потому что макет представления сбросит 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"];
}