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


85

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

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

Ответы:


116

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

Панель поиска не считается строкой в ​​табличном представлении, поэтому, если вы прокручиваете верхнюю часть табличного представления до первой строки, она «скрывает» панель поиска:

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

или в Swift:

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

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


12
На самом деле у меня возникает проблема, при которой представление таблицы не прокручивается, если не хватает строк для заполнения экрана - в таблице с одной или двумя строками этот код ничего не делает. Это ожидаемое поведение? Могу я как-нибудь обойти это?
Тим

18
Я нашел способ: вместо прокрутки до пути к индексу измените смещение содержимого в представлении прокрутки таблицы на CGRect на 0,0, 44,0
Тим

107
Тим, так и надо! Я использовал self.tableView.contentOffset = CGPointMake (0, self.searchDisplayController.searchBar.frame.size.height);
Джо Д'Андреа,

1
Кстати, когда я вставлял этот код после вызова [tableView reloadData], который заставлял мою таблицу перейти с 2 строк на достаточное количество строк, чтобы обеспечить нормальную прокрутку, я обнаружил, что использование метода contentOffset вызывает прерывистое отображение и скрытие поиска box при использовании метода scrollToRowAtIndexPath не было такого же рывка.
Chris R

1
Это единственный окончательный способ, который работает при ЛЮБЫХ обстоятельствах. Установка contentOffsetс любым значением будет нарушена, если вы используете сложную компоновку контроллера представления. Не рекомендую.
eonil

45

Не добавляйте 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];
}

Для меня это было единственное решение, в котором была создана скрытая реализация UISearchBar, которая, как казалось, соответствовала HIG. Другие решения, в которых он «застревает» в верхней части таблицы, кажутся неестественными.
kurtzmarc

1
Это решение сработало лучше всего. На мой взгляд, это должен быть ответ.
darwindeeds

Хорошее решение (+1). Однако в некоторых случаях tableView не скрывает панель поиска должным образом (в моем случае: когда контроллер tableview отодвигается от другого контроллера представления). По этой проблеме есть вопрос: stackoverflow.com/questions/15222186/… Я думаю, что предоставленный ответ на этот вопрос не совсем хорош. @bandejapaisa, у тебя есть идеи?
Брайан

3
Это сработало для меня, хотя вместо жесткого кодирования 44 я предлагаю использовать self.tableView.tableHeaderView.frame.size.height в viewWillAppear, чтобы учесть изменения, которые Apple может внести в будущем в высоту панели поиска.
Джон Стивен

вставлять его viewWillAppear- плохая идея, потому что он будет запускаться каждый раз, когда вы вернетесь к своему представлению. Это означает, что ваш tableview будет медленно уменьшаться. Раздражающий баг!
Sirens

25

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


9
Я обнаружил, что анимация - ключ к этому решению. Попытка установить смещение содержимого без анимации ничего не даст. Однако, вместо того, чтобы использовать методы (или блоки) анимации в этом примере, я считаю, что достаточно просто вызвать [self.tableView setContentOffset: CGPointMake (0,44) animated: TRUE].
quickthyme 05

Вроде без анимации вообще не работает. @quickthyme сделал то же наблюдение.
Тило

5
Я добился успеха без анимации, если вы реализуете ее viewDidAppear:вместо viewDidLoad.
wbyoung 08

8

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

Это прекрасно работает. Без анимации и делается на-viewDidLoad

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

Заметка:

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


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

@fguespe, это странно ... viewDidLoad должен вызываться каждый раз при создании экземпляра представления. Со мной никогда не случалось, чтобы UISearchBar был виден после возвращения из другого представления.
Matej

Я использую приложение с вкладками. Может что?
fguespe

@fguespe Я вижу, что проблема в этом. К сожалению, я не уверен, как бы вы решили эту проблему, если -viewWillAppearне работает ... Вызывается ли он, когда контроллер вкладки переключается на представление?
Matej

Вызываются viewwillappear и viewdidappear, но я пробовал с обоими, и представление похоже на неправильное смещение. Он не возвращает ожидаемого результата. Я имею в виду, что что-то делает, но плохо.
fguespe

7

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

5

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

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

5

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

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

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

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

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

var minimumCellNum: Int?

2.) рассчитать minimumCellNumи установить 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.) Зарегистрируйте пустую ячейку с selectionатрибутом 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. Я хотел бы знать, есть ли более изящное решение.


Мне пришлось сделать это для iOS 11. Только установка contentOffset работала для iOS 9/10 для 0 или более ячеек, а также для iOS 11 для 8+ ячеек. Все, что меньше, требовало этого решения. Есть причина почему?
Тони

Я думаю, что первый абзац этого поста объясняет, почему.
Брайан

Я понял первый абзац, но все равно было странно, что он у меня «сломался» только на iOS 11. Я ожидал аналогичного поведения для iOS 9, 10 и 11, когда количество ячеек одинаковое (менее чем достаточно). Еще раз спасибо и извините за старый пост!
Тони

4

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

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

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

В searchBarCancelButtonClicked(searchBar: UISearchBar!)

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

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

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

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

Это сработало частично: tableView.setContentOffset(CGPointMake(0, 0) но titleForHeaderInSectionбыло за toolBar

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

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


Вам действительно не следует жестко кодировать свои баллы, почему бы вам не использовать CGRectGetHeight(searchBar.bounds)?
Зорайр 09

Я предполагаю, что вы, должно быть, делаете это в viewDidLoad () / init (), где для вас не был установлен фрейм, чтобы вы могли получить высоту панели поиска. Может быть вам стоит попробовать viewDidLayoutSubviews ()
Kaushil Ruparelia

Если Tableview не закончил загрузку, ни одно из ContentOffsetрешений не будет работать
Maor

4

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

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

Ознакомьтесь с проектом, если вам нужен быстрый пример того, как все это настроить.


1
Спасибо! Это было то, что я искал. Вам нужно подняться наверх.
user2387149 06

3

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

2

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

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

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

Хорошее решение проблемы смещения. У меня работает при использовании UINavigationController.
Андреас Крафт

1
Решение не работает, когда количество строк не покрывает весь экран - знаете ли вы обходной путь для этого?
Зорайр 09

Не уверен, что я полностью это понимаю. У меня это работает, когда в таблице всего несколько строк. Однако я использую следующее: self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];.
p0lAris

2

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

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

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

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

1

Я обнаружил, что приложение «Заметки» не может искать элементы, когда 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];
}

Надеюсь это поможет :-)


1

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

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

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


1

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

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


1

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

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

self.tableView.selectRowAtIndexPath (firstIndexPath, анимированный: false, scrollPosition: .Top)

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

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

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

Сначала определите переменную с именем isInitialLoad в классе контроллера представления и установите для нее значение «true»:

var isInitialLoad = true

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

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

            isInitialLoad = false
        }

1

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

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

1

Я попытался установить смещение содержимого и scrollToIndexpath, но ничего не получилось. Я удалил настройку tableHeaderView как 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)
    }
}

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


0

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

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

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


0

Для Swift:

Просто сделайте смещение содержимого таблицы по оси y, которое должно быть похоже на высоту панели поиска.

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

0

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

0

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

После некоторой отладки я понял, что проблема возникает, только если размер содержимого табличного представления меньше, чем табличное представление. Я запускаю КВО, когда на viewWillAppear. И отправка остановится KVO через 0,3 секунды после появления просмотра. Каждый раз, когда contentOffset становится равным 0,0, KVO реагирует и устанавливает contentOffset на высоту панели поиска. Я останавливаю асинхронный KVO через 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"];
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.