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

<p>я использовал interface builder для создания табличного представления, к которому я добавил панель поиска библиотеки и контроллер отображения поиска, чтобы добавить функцию поиска. однако ib настроил его так, чтобы полоса была видна в верхней части экрана при первом отображении представления.</p><p>я хотел бы знать, как по умолчанию скрыть панель поиска, но при этом ее можно прокручивать в табличном представлении (например, см. приложение apple mail). я попытался вызвать <code>scrollrecttovisible:animated:</code> в <code>viewdidload</code>, чтобы прокрутить представление таблицы вниз, но безрезультатно. какой предпочтительный способ скрыть панель поиска по умолчанию?</p>

<p>сначала убедитесь, что добавили uisearchbar в tableheaderview uitableview, чтобы он прокручивался с содержимым таблицы и не фиксировался в верхней части представления.</p><p>панель поиска не считается строкой в табличном представлении, поэтому, если вы прокрутите верхнюю часть табличного представления до первой строки, она «скрывает» панель поиска:</p><pre><code>[yourtableview scrolltorowatindexpath:[nsindexpath indexpathforrow:0 insection:0] atscrollposition:uitableviewscrollpositiontop animated:no]; </code></pre><p>или в swift:</p><pre><code>yourtableview.scrolltorowatindexpath(nsindexpath(forrow: 0, insection: 0), atscrollposition: uitableviewscrollposition.top, animated: false) </code></pre><p>убедитесь, что вы не прокручиваете табличное представление, пока оно не содержит данных (<code>scrolltorowatindexpath</code> вызовет исключение, если заданный indexpath не указывает на допустимую строку (то есть, если табличное представление пусто)).</p>

<p>не добавляйте uisearchbar в качестве подпредставления uitableview, в этом нет необходимости.</p><p>uitableview имеет свойство tableheaderview, которое идеально подходит для этого:</p><pre><code>- (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; } </code></pre><p>если вы не хотите видеть его по умолчанию:</p><pre><code>- (void) viewwillappear:(bool)animated { [super viewwillappear:animated]; [self.tableview setcontentoffset:cgpointmake(0, 44)]; } </code></pre><p>я также получаю кнопку отмены, чтобы скрыть это снова .... (и удалить клавиатуру)</p><pre><code>- (void)searchbarcancelbuttonclicked:(uisearchbar *)searchbar { [self.tableview setcontentoffset:cgpointmake(0, 44) animated:yes]; [self.searchbar resignfirstresponder]; } </code></pre>

<p>contentoffset был отмечен в комментариях тима и джо д'андреа, но он немного расширен за счет добавления анимации к скрытию панели поиска. я заметил, что для панели поиска немного неожиданно просто исчезнуть.</p><p><em> небольшое обновление для тех, кто хочет ориентироваться на ios 4.0 и выше, попробуйте это: </em></p><pre><code>[uiview animatewithduration:0.4 animations:^{ self.tableview.contentoffset = cgpointmake(0, self.searchdisplaycontroller.searchbar.frame.size.height); } completion:nil]; </code></pre><p>предыдущий ответ:</p><pre><code>[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]; </code></pre><p></p>

<p>есть много ответов на это решение, но ни один из них не сработал для меня.</p><p>это работает отлично. нет анимации, и выполняется по <code>-viewdidload</code></p><pre><code> - (void)viewdidload { [super viewdidload]; self.tableview.contentoffset = cgpointmake(0, self.searchbar.frame.size.height - self.tableview.contentoffset.y); } </code></pre><hr><p><strong> заметка: </strong></p><p>код предполагает, что у вас есть <code>searchbar</code> <code>@property</code>, который связан с панелью поиска . если нет, вы можете использовать вместо него <code>self.searchdisplaycontroller.searchbar</code></p>

<p>для swift 3+</p><p>я сделал это:</p><p>объявите это <code>var</code>:</p><pre><code> var searchcontroller = uisearchcontroller() </code></pre><p>и в методе <code>viewdidload()</code></p><pre><code> 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) </code></pre>

<p>моя цель состояла в том, чтобы скрыть панель поиска, добавленную к tableheaderview стиля обычного tableview, в раскадровке, когда контроллер представления загружен, и показать панель, когда пользователь прокручивает вниз в верхней части uitableview. итак, я использую<strong> swift </strong>и добавил расширение:</p><pre><code>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) } } } } </code></pre><p>в<strong> viewdidload () </strong>просто вызов:</p><pre><code>self.tableview.hidesearchbar() </code></pre>

<p>сдвиг контента - это путь, но он не работает в 100% случаев.</p><p>это не работает, если для <code>estimatedrowheight</code> установлено фиксированное число. я еще не знаю, как это работает. я создал <a href="https://github.com/ryanjm/exampletableview">проект</a> с указанием проблем, и я создал <a href="https://openradar.appspot.com/radar?id=5061770456596480">радар</a> с apple.</p><p>проверьте проект, если вам нужен пример в swift, как все это настроить.</p>

<p>все существующие решения не работают для меня на ios 8, когда не хватает строк для заполнения tableview, так как ios будет автоматически корректировать вставку в этой ситуации. (существующие ответы хороши, когда строк достаточно)</p><p>потратив около 6 часов на эту проблему, я наконец-то нашел решение.</p><p>короче говоря, вам нужно вставить пустые ячейки в tableview, если ячеек недостаточно, поэтому размер содержимого tableview достаточно большой, чтобы ios не корректировала вставку для вас.</p><p>вот как я это сделал в swift:</p><p>1.) объявить переменную <code>minimumcellnum</code> как свойство класса</p><pre><code>var minimumcellnum: int? </code></pre><p>2.) вычислить <code>minimumcellnum</code> и установить <code>tableview.contentoffset</code> в <code>viewwillappear</code></p><pre><code>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) </code></pre><p>3.) в <code>tableview(tableview: uitableview, numberofrowsinsection section: int))</code></p><pre><code>let numofyourrows = your logic if numofyourrows > minimumcellnum { return numofyourrows } else { return minimumcellnum! } </code></pre><p>4.) зарегистрируйте пустую ячейку с атрибутом <code>selection</code> <code>none</code> в раскадровке и в <code>tableview(tableview: uitableview, cellforrowatindexpath indexpath: nsindexpath)</code></p><pre><code>if indexpath.row < numofyourrows { return your custom cell } else { let cell = tableview.dequeuereusablecellwithidentifier("emptycell", forindexpath: indexpath) as! uitableviewcell return cell } </code></pre><p>5.) в <code>tableview(tableview: uitableview, didselectrowatindexpath indexpath: nsindexpath)</code></p><pre><code>if tableview == self.tableview { if numofyourrows < (indexpath.row + 1) { return } your logic of selecting a cell } </code></pre><p>это не идеальное решение, но это единственный обходной путь, который действительно работает для меня на ios 8. я хотел бы знать, есть ли более аккуратное решение.</p>

<p>ничто из вышеперечисленного не сработало для меня, так что на всякий случай, если у кого-то возникнет такая же проблема.</p><p>я установил <code>searchbar</code> как <code>tableheaderview</code> для сгруппированной таблицы на ios7. в <code>viewdidload</code> у меня есть <code>tableview.contentoffset = cgpointmake(0, 44);</code>, чтобы <code>searchbar</code> изначально была «скрытой». когда пользователь прокручивает вниз, отображается <code>searchbar</code></p><p><strong> цель: </strong>скрыть панель поиска при нажатии кнопки отмены</p><p>в <code>searchbarcancelbuttonclicked(searchbar: uisearchbar!)</code></p><p><strong> это не сработало: </strong><code>[yourtableview scrolltorowatindexpath:[nsindexpath indexpathforrow:0 insection:0] atscrollposition:uitableviewscrollpositiontop animated:no];</code> tableview слишком сильно прокручивался, в результате чего строка 0 шла за панелью инструментов.</p><p><strong> это не сработало: </strong><code>tableview.contentoffset = cgpointmake(0, 44)</code> - ничего не происходит</p><p><strong> это не сработало: </strong><code>tableview.contentoffset = cgpointmake(0, searchbar.frame.size.height)</code> - ничего не происходит</p><p><strong> это не сработало: </strong><code>tableview.setcontentoffset(cgpointmake(0, 44), animated: true);</code> снова tableview прокручивал слишком много вверх, в результате чего строка 0 шла за панелью инструментов.</p><p><strong> это работало частично: </strong><code>tableview.setcontentoffset(cgpointmake(0, 0)</code>, но <code>titleforheaderinsection</code> шел за панелью инструментов </p><p><strong> это работает: </strong><code>tableview.setcontentoffset(cgpointmake(0, -20)</code></p><p>кажется нелогичным, но только -20 переместил его в правильное положение</p>

<p>обратите внимание, что принятый ответ потерпит крах, если в табличном представлении нет данных. и добавление его в viewdidload может не работать при определенных обстоятельствах.</p><pre><code>[yourtableview scrolltorowatindexpath:[nsindexpath indexpathforrow:0 insection:0] atscrollposition:uitableviewscrollpositiontop animated:no]; // crashes if no rows/data </code></pre><p>поэтому вместо этого добавьте эту строку кода:</p><pre><code>- (void)viewdidappear:(bool)animated { [super viewdidappear:animated]; [yourtableview setcontentoffset:cgpointmake(0, self.searchdisplaycontroller.searchbar.frame.size.height)]; } </code></pre>

<p>честно говоря, ни один из ответов действительно не решил проблему (для меня). если в вашем табличном представлении нет строк or, высота строки отличается, этих ответов недостаточно.</p><p>единственное, что работает:</p><pre><code>- (void)viewwillappear:(bool)animated { [super viewwillappear:animated]; self.tableview.contentoffset = (cgpoint){.y = self.tableview.contentoffset.y + self.searchcontroller.searchbar.frame.size.height}; } </code></pre>

<p>другие ответы на этот вопрос либо вообще не работали для меня, либо имели странное поведение, когда tableview имел 0 строк, либо испортили положение прокрутки при переходе назад и вперед между родительскими и вложенными позициями. это сработало для меня (цель состоит в том, чтобы скрыть uisearchbar при загрузке):</p><pre><code>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) } } </code></pre><p><strong> объяснение </strong> <br/>в любое время, когда появится <code>tableview</code>, этот код проверяет, видна ли uisearchbar (имеется в виду, что позиция <code>y</code> <code>contentoffset</code>, или позиция прокрутки <code>0</code>). если это так, он просто прокручивается вниз по высоте панели поиска. если searchbar не виден (например, большой список элементов в <code>tableview</code>), он не будет пытаться прокручивать <code>tableview</code>, так как это приведет к путанице в позиционировании при возврате из вложенной позиции.</p><p><strong> примечание </strong> <br/>я бы порекомендовал поместить этот код в отдельный метод, чтобы обеспечить единую ответственность и возможность повторного вызова в любое время в вашем коде.</p>

<p>ниже код работает для меня</p><pre><code>self.tableview.contentoffset = cgpointmake(0.0, 44.0); </code></pre>

<p>если в вашей таблице всегда будет хотя бы одна строка, просто выделите первую строку таблицы, и строка поиска будет скрыта автоматически.</p><pre><code>let firstindexpath = nsindexpath(forrow: 0, insection: 0) </code></pre><p>self.tableview.selectrowatindexpath (firstindexpath, animated: false, scrollposition: .top)</p><p>если вы поместите вышеуказанный код в<strong> viewdidload </strong>, он выдаст ошибку, потому что tableview еще не загрузился, поэтому вы должны поместить его в<strong> viewdidappear </strong>, потому что к этому моменту tableview уже загрузился. </p><p>если вы установите его на<strong> viewdidappear </strong>, каждый раз, когда вы открываете tableview, он будет прокручиваться до самого верха. </p><p>может быть, вам не нужно это поведение, если tableview остается открытым, например, когда это uitabbar view controller или когда вы делаете переход, а затем возвращаетесь. если вы просто хотите, чтобы он прокручивался до вершины при начальной загрузке, вы можете создать переменную, чтобы проверить, является ли она начальной загрузкой, чтобы она прокручивалась до вершины только один раз. </p><p>сначала определите переменную isinitialload в классе контроллера представления и установите ее равной «true»:</p><pre><code>var isinitialload = true </code></pre><p>затем проверьте, является ли isinitialload истинным для<strong> viewdidappear </strong>и, если оно истинно, прокрутите до самого верха и установите для переменной isinitialload значение false:</p><pre><code>if isinitialload { let firstindexpath = nsindexpath(forrow: 0, insection: 0) self.tableview.selectrowatindexpath(firstindexpath, animated: false, scrollposition: .top) isinitialload = false } </code></pre>

<p>метод <code>scrolltorowatindexpath</code> вызывает сбой, если в таблице есть ноль строк.</p><p>uitableview - это просто представление с прокруткой, поэтому вы можете просто изменить смещение содержимого.</p><p>это проверяет, что у вас есть панель поиска как tableheaderview, и предполагается, что вы делаете прокрутки, чтобы скрыть</p><pre><code> if let searchheight = tableview.tableheaderview?.frame.height { tableview.setcontentoffset(cgpoint.init(x: 0, y:searchheight ), animated: false) } </code></pre>

<p>я обнаружил, что приложение «заметки» не может искать элементы, когда tableview пуст. поэтому я думаю, что он просто использует <code>-(void)addsubview:(uiview *)view</code>, чтобы сначала добавить пустую таблицу. когда вы добавляете элемент в его массив источника данных, он запускает другой кусок кода для загрузки tableview.</p><p>итак, мое решение:</p><pre><code>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]; } </code></pre><p>надеюсь это поможет :-)</p>

<p>измените границы tableview, это можно сделать внутри <code>viewdidload</code>, плюс вам не нужно беспокоиться о том, пуста таблица или нет (чтобы избежать исключения).</p><pre><code>cgrect newbounds = self.tableview.bounds; newbounds.origin.y = newbounds.origin.y + self.searchbar.bounds.size.height; self.tableview.bounds = newbounds; </code></pre><p>как только пользователь прокрутит таблицу вниз, появится панель поиска и будет вести себя нормально </p>

<p>у меня была похожая проблема. и единственный способ решить эту проблему - использовать наблюдатель значения ключа для свойства contentoffset uitableview.</p><p>после некоторой отладки я понял, что проблема возникает только в том случае, если размер содержимого табличного представления меньше размера табличного представления. я запускаю kvo, когда на viewwillappear. и диспетчерская остановка кво через 0,3 секунды после появления. каждый раз, когда contentoffset становится 0.0, kvo реагирует и устанавливает contentoffset на высоту строки поиска .. я останавливаю kvo асинхронным через 0,3 секунды, потому что макет представления сбросит contentoffset даже после появления представления. </p><pre><code>- (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"]; } </code></pre>

<p>для свифта:</p><p>просто сделайте табличное представление y смещением, которое должно быть как высота панели поиска.</p><pre><code>self.tableview.contentoffset = cgpointmake(0, self.searchcontroller.searchbar.frame.size.height) </code></pre>

<p>свифт 3</p><p>работает с пустой таблицей тоже!</p><p>в моем окружении я загружаю пользовательские ячейки по xib-файлу, и rowheight устанавливается автоматически.</p><pre><code> 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) } </code></pre>

<p>я попытался установить смещение содержимого и scrolltoindexpath, но ничего не получалось удовлетворительно. я удалил параметр tableheaderview как searchbar из viewdidload и установил его в делегате scrollview, как показано ниже</p><pre><code>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) } } </code></pre><p>это работает как шарм. настройка смещения контента предназначена для плавной прокрутки.</p>

<p>не забудьте учесть<strong> строку состояния </strong>и<strong> панель навигации </strong>. мой контроллер табличного представления встроен в контроллер навигации в <code>viewdidappear</code>:</p><pre><code>tableview.contentoffset = cgpointmake(0, -20) // -20 = 44 (search bar height) - 20 (status bar height) - 44 (navigation bar height) </code></pre><p>работал на меня.</p>