можно ли отключить плавающие заголовки в uitableview с uitableviewstyleplain?

<p>я использую <code>uitableview</code> для размещения содержимого "страниц". я использую заголовки табличного представления для размещения определенных изображений и т.д., и я бы предпочел, чтобы они не плавали, а оставались статичными, как они делают, когда стиль установлен в <code>uitableviewstylegrouped</code>.</p><p>кроме использования <code>uitableviewstylegrouped</code>, есть ли способ сделать это? я хотел бы избегать использования группировки, так как это добавляет запас по всем моим ячейкам и требует отключения фонового представления для каждой из ячеек. я хотел бы получить полный контроль над моим макетом. в идеале они должны быть "uitableviewstylebarebones", но я не видел эту опцию в документах ...</p><p>большое спасибо,</p>

<p>вы должны иметь возможность подделать это, используя пользовательскую ячейку для создания строк заголовка. затем они будут прокручиваться, как и любая другая ячейка в табличном представлении.</p><p>вам просто нужно добавить некоторую логику в ваше <code>cellforrowatindexpath</code>, чтобы вернуть правильный тип ячейки, когда это строка заголовка.</p><p>вам, вероятно, придется самим управлять своими разделами, то есть иметь все в одном разделе и подделывать заголовки. (вы также можете попытаться вернуть скрытое представление для представления заголовка, но я не знаю, будет ли это работать)</p>

<p>вероятно, более простой способ добиться этого:</p><p>objective-c:</p><pre><code>cgfloat dummyviewheight = 40; uiview *dummyview = [[uiview alloc] initwithframe:cgrectmake(0, 0, self.tableview.bounds.size.width, dummyviewheight)]; self.tableview.tableheaderview = dummyview; self.tableview.contentinset = uiedgeinsetsmake(-dummyviewheight, 0, 0, 0); </code></pre><p>swift:</p><pre><code>let dummyviewheight = cgfloat(40) self.tableview.tableheaderview = uiview(frame: cgrect(x: 0, y: 0, width: self.tableview.bounds.size.width, height: dummyviewheight)) self.tableview.contentinset = uiedgeinsetsmake(-dummyviewheight, 0, 0, 0) </code></pre><p>заголовки разделов теперь будут прокручиваться, как и любая обычная ячейка.</p>

<p>(для тех, кто когда-либо попадал сюда из-за неправильного стиля таблицы) измените стиль таблицы с простого на сгруппированный, через инспектор атрибутов или через код</p><pre><code>let tableview = uitableview(frame: .zero, style: .grouped) </code></pre>

<p><strong>warning</strong>: в этом решении реализован зарезервированный метод api. это может помешать утверждению приложения apple для распространения в appstore.</p><p>я описал приватные методы, которые превращают заголовки разделов, плавающие в <a href="http://meeu.me/blog/disable-section-header-floating">мой блог</a> </p><p>по сути, вам просто нужно создать подкласс <code>uitableview</code> и вернуть <code>no</code> в двух из его методов:</p><pre><code>- (bool)allowsheaderviewstofloat; - (bool)allowsfooterviewstofloat; </code></pre>

<p>хорошо, я знаю, что уже поздно, но я должен был это сделать. я потратил 10 часов на поиск рабочего решения, но не нашел полного ответа. нашел некоторые подсказки, но их трудно понять начинающим. таким образом, я должен был вставить свои 2 цента и закончить ответ.</p><p>как было предложено в нескольких ответах, единственное рабочее решение, которое мне удалось реализовать, - это вставить нормальные ячейки в табличное представление и обработать их как заголовки разделов, но лучший способ добиться этого - вставить эти ячейки в строка 0 каждого раздела. таким образом, мы можем легко обрабатывать эти нестандартные заголовки без плава.</p><p>итак, шаги есть.</p><ol> <li> <p>реализуйте uitableview со стилем uitableviewstyleplain.</p> <pre><code>-(void) loadview { [super loadview]; uitableview *tblview =[[uitableview alloc] initwithframe:cgrectmake(0, frame.origin.y, frame.size.width, frame.size.height-44-61-frame.origin.y) style:uitableviewstyleplain]; tblview.delegate=self; tblview.datasource=self; tblview.tag=2; tblview.backgroundcolor=[uicolor clearcolor]; tblview.separatorstyle = uitableviewcellseparatorstylenone; } </code></pre></li> <li> <p>реализуйте titleforheaderinsection как обычно (вы можете получить это значение, используя собственную логику, но я предпочитаю использовать стандартные делегаты).</p> <pre><code>- (nsstring *)tableview: (uitableview *)tableview titleforheaderinsection:(nsinteger)section { nsstring *headertitle = [sectionarray objectatindex:section]; return headertitle; } </code></pre></li> <li> <p>воплощение numberofsectionsintableview как обычно</p> <pre><code>- (nsinteger)numberofsectionsintableview:(uitableview *)tableview { int sectioncount = [sectionarray count]; return sectioncount; } </code></pre></li> <li> <p>реализуйте numberofrowsinsection как обычно.</p> <pre><code>- (nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section { int rowcount = [[cellarray objectatindex:section] count]; return rowcount +1; //+1 for the extra row which we will fake for the section header } </code></pre></li> <li> <p>вернуть 0.0f в heightforheaderinsection.</p> <pre><code>- (cgfloat)tableview:(uitableview *)tableview heightforheaderinsection:(nsinteger)section { return 0.0f; } </code></pre></li> <li> <p>не реализуйте viewforheaderinsection. удалите метод полностью вместо того, чтобы возвращать ноль.</p> </li> <li> <p>в высоту forrowatindexpath. проверьте, если (indexpath.row == 0) и верните желаемую высоту ячейки для заголовка раздела, иначе верните высоту ячейки.</p> <pre><code>- (cgfloat)tableview:(uitableview *)tableview heightforrowatindexpath:(nsindexpath *)indexpath { if(indexpath.row == 0) { return 80; //height for the section header } else { return 70; //height for the normal cell } } </code></pre></li> <li> <p>теперь в cellforrowatindexpath, проверьте, если (indexpath.row == 0), и реализуйте ячейку так, как вы хотите, чтобы был заголовок раздела, и установите стиль выбора равным none. иначе реализуйте ячейку так, как вы хотите, чтобы была нормальная ячейка.</p> <pre><code>- (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath { if (indexpath.row == 0) { uitableviewcell *cell = [tableview dequeuereusablecellwithidentifier:@"sectioncell"]; if (cell == nil) { cell = [[[uitableviewcell alloc] initwithstyle:uitableviewcellstyledefault reuseidentifier:@"sectioncell"] autorelease]; cell.selectionstyle = uitableviewcellselectionstylenone; //so that the section header does not appear selected cell.backgroundview = [[uiimageview alloc] initwithimage:[uiimage imagenamed:@"sectionheaderbackground"]]; } cell.textlabel.text = [tableview.datasource tableview:tableview titleforheaderinsection:indexpath.section]; return cell; } else { uitableviewcell *cell = [tableview dequeuereusablecellwithidentifier:@"cell"]; if (cell == nil) { cell = [[[uitableviewcell alloc] initwithstyle:uitableviewcellstyledefault reuseidentifier:@"cell"] autorelease]; cell.selectionstyle = uitableviewcellselectionstylegray; //so that the normal cell looks selected cell.backgroundview =[[[uiimageview alloc] initwithimage:[uiimage imagenamed:@"cellbackground"]]autorelease]; cell.selectedbackgroundview=[[[uiimageview alloc] initwithimage:[uiimage imagenamed:@"selectedcellbackground"]] autorelease]; } cell.textlabel.text = [[cellarray objectatindex:indexpath.section] objectatindex:indexpath.row -1]; //row -1 to compensate for the extra header row return cell; } } </code></pre></li> <li> <p>теперь реализуем willselectrowatindexpath и возвращаем nil, если indexpath.row == 0. это будет заботиться о том, чтобы didselectrowatindexpath никогда не запускалось для строки заголовка раздела.</p> <pre><code>- (nsindexpath *)tableview:(uitableview *)tableview willselectrowatindexpath:(nsindexpath *)indexpath { if (indexpath.row == 0) { return nil; } return indexpath; } </code></pre></li> <li> <p>и, наконец, в didselectrowatindexpath, проверьте, если (indexpath.row! = 0) и продолжить.</p> <pre><code>- (void)tableview:(uitableview *)tableview didselectrowatindexpath:(nsindexpath *)indexpath { if (indexpath.row != 0) { int row = indexpath.row -1; //now use 'row' in place of indexpath.row //do what ever you want the selection to perform } } </code></pre></li> </ol><p>с этим вы сделали. теперь у вас есть идеально прокручиваемый, не плавающий заголовок раздела.</p>

<p>в вашем интерфейсном разработчике нажмите на вашу проблему.</p><p> <a href="https://i.stack.imgur.com/mpwok.png"><img src="https://i.stack.imgur.com/mpwok.png" alt="problem table view"></a> </p><p>затем перейдите в "инспектор атрибутов" и измените стиль "простой" на "сгруппированный";)</p><p> <a href="https://i.stack.imgur.com/j49vq.png"><img src="https://i.stack.imgur.com/j49vq.png" alt="easy"></a> </p>

<p>интересной особенностью uitableviewstylegrouped является то, что tableview добавляет стиль к <strong>ячейкам</strong>, а не к tableview.</p><p>стиль добавляется в ячейки как backgroundview как класс с именем uigrouptableviewcellbackground, который обрабатывает рисование различного фона в соответствии с положением ячейки в разделе.</p><p>поэтому очень простым решением будет использовать uitableviewstylegrouped, установить backgroundcolor таблицы в clearcolor и просто заменить backgroundview ячейки в cellforrow:</p><pre><code>cell.backgroundview = [[[uiview alloc] initwithframe:cell.bounds] autorelease]; </code></pre>

<p>измените ваш стиль tableview:</p><pre><code>self.tableview = [[uitableview alloc] initwithframe:frame style:uitableviewstylegrouped]; </code></pre><p>согласно apple документации для uitableview:</p><blockquote> <p>uitableviewstyleplain - простое табличное представление. любые верхние или нижние колонтитулы отображаются в виде встроенных разделителей и плавают при прокрутке табличного представления.</p> <p>uitableviewstylegrouped - табличное представление, разделы которого представляют различные группы строк. верхние и нижние колонтитулы раздела не плавают.</p> </blockquote>

<p>есть еще один хитрый способ. основная идея состоит в том, чтобы удвоить номер раздела, и первый показывает только headerview, а второй показывает реальные ячейки.</p><pre><code>- (nsinteger)numberofsectionsintableview:(uitableview *)tableview { return sectioncount * 2; } - (nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section { if (section%2 == 0) { return 0; } return \_rowcount; } </code></pre><p>затем необходимо реализовать делегаты headerinsection:</p><pre><code>- (uiview *)tableview:(uitableview *)tableview viewforheaderinsection:(nsinteger)section { if (section%2 == 0) { //return headerview; } return nil; } - (cgfloat)tableview:(uitableview *)tableview heightforheaderinsection:(nsinteger)section { if (section%2 == 0) { //return headerheight; } return 0; } </code></pre><p>этот подход также мало влияет на ваши источники данных:</p><pre><code>- (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath { int real\_section = (int)indexpath.section / 2; //your code } </code></pre><p>по сравнению с другими подходами этот способ безопасен, не изменяя фрейм или наборы содержимого табличного представления. надеюсь, что это может помочь.</p>

<p>самый простой способ получить то, что вы хотите, это установить стиль таблицы как <code>uitableviewstylegrouped</code>, стиль разделителя как <code>uitableviewcellseparatorstylenone</code>:</p><pre><code>- (cgfloat)tableview:(uitableview *)tableview heightforfooterinsection:(nsinteger)section { return cgfloat\_min; // return 0.01f; would work same } - (uiview *)tableview:(uitableview *)tableview viewforfooterinsection:(nsinteger)section { return [[uiview alloc] initwithframe:cgrectzero]; } </code></pre><p>не пытайтесь возвращать представление нижнего колонтитула как <code>nil</code>, не забывайте устанавливать высоту заголовка и представление заголовка, после того, как вы получите желаемое.</p>

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

<p><strong>для swift 3 +</strong></p><p>просто используйте <code>uitableviewstylegrouped</code> и установите высоту нижнего колонтитула в ноль со следующим:</p><pre><code>override func tableview(\_ tableview: uitableview, heightforfooterinsection section: int) -> cgfloat { return .leastnormalmagnitude } </code></pre>

<p>думая, как подойти к этой проблеме, я вспомнил очень важную деталь об uitableviewstylegrouped. способ, которым uitableview реализует сгруппированный стиль (округленные границы вокруг ячеек), заключается в добавлении пользовательского backgroundview в uitableviewcells, а не в uitableview. в каждую ячейку добавляется backgroundview в соответствии с его положением в разделе (верхние строки получают верхнюю часть границы раздела, средние - боковую, а нижний - ну, в общем, нижнюю часть). итак, если мы просто хотим простой стиль, и у нас нет собственного backgroundview для наших ячеек (что имеет место в 90% случаев), тогда все, что нам нужно сделать, это использовать uitableviewstylegrouped и удалить пользовательский фон , это можно сделать, выполнив эти два шага:</p><p>измените наш стиль tableview на uitableviewstylegrouped. добавьте следующую строку в cellforrow непосредственно перед тем, как мы вернем ячейку:</p><p>cell.backgroundview = [[[uiview alloc] initwithframe: cell.bounds] autorelease];</p><p>и это все. стиль tableview станет точно таким же, как uitableviewstyleplain, за исключением плавающих заголовков.</p><p>надеюсь это поможет!</p>

<p>вы можете добавить один раздел (с нулевыми строками) выше, а затем установить вышеупомянутый sectionfooterview в качестве headerview текущего раздела, footerview не плавает. надеюсь, это поможет.</p>

<p>возможно, вы могли бы использовать <code>scrollviewdidscroll</code> в tableview и изменить <code>contentinset</code> на основе текущего видимого заголовка.</p><p>кажется, работает для моего случая использования!</p><pre><code>extension viewcontroller : uiscrollviewdelegate{ func scrollviewdidscroll(\_ scrollview: uiscrollview) { guard let tableview = scrollview as? uitableview, let visible = tableview.indexpathsforvisiblerows, let first = visible.first else { return } let headerheight = tableview.rectforheader(insection: first.section).size.height let offset = max(min(0, -tableview.contentoffset.y), -headerheight) self.tableview.contentinset = uiedgeinsetsmake(offset, 0, -offset, 0) } } </code></pre>

<p>другой способ сделать это - создать пустой раздел прямо перед тем, на котором вы хотите заголовок, и поместить свой заголовок в этот раздел. поскольку раздел пуст, заголовок будет прокручиваться немедленно.</p>

<p>проверьте ответ, как реализовать заголовки с помощью storyboard: <a href="https://stackoverflow.com/questions/7841167/table-header-views-in-storyboards">представления заголовков таблиц в storyboards</a> </p><p>также обратите внимание, что если вы не реализуете</p><pre><code>viewforheaderinsection:(nsinteger)section </code></pre><p>это не будет плавать, что именно то, что вы хотите.</p>

<p>игнорировать xak. не изучайте какие-либо частные методы, если хотите, чтобы ваше приложение имело шанс быть принятым apple.</p><p>это проще всего, если вы используете interface builder. вы бы добавили uiview в верхней части представления (куда будут добавляться изображения), а затем добавили свое табличное представление под ним. ib должен измерить это соответственно; то есть верхняя часть табличного вида касается нижней части только что добавленного uiview, а его высота покрывает остальную часть экрана.</p><p>мысль здесь заключается в том, что если этот uiview на самом деле не является частью табличного представления, он не будет прокручиваться вместе с табличным представлением. то есть игнорировать заголовок таблицы.</p><p>если вы не используете конструктор интерфейса, это немного сложнее, потому что вы должны получить правильное расположение и высоту для таблицы.</p>

<p>чтобы полностью удалить заголовки плавающей секции, вы можете сделать это:</p><pre><code>- (uiview *) tableview:(uitableview *)tableview viewforheaderinsection:(nsinteger)section { return [[uiview alloc] init]; } </code></pre><p><code>return nil</code> не работает.</p><p>чтобы отключить плавающие, но по-прежнему показывать заголовки разделов, вы можете предоставить собственное представление с его собственным поведением.</p>