it-swarm.com.ru

Можно ли отключить плавающие заголовки в UITableView с UITableViewStylePlain?

Я использую UITableView для размещения содержимого "страниц". Я использую заголовки табличного представления для размещения определенных изображений и т.д., И я бы предпочел, чтобы они не плавали, а оставались статичными, как они делают, когда стиль установлен в UITableViewStyleGrouped.

Кроме использования UITableViewStyleGrouped, есть ли способ сделать это? Я хотел бы избегать использования группировки, так как это добавляет запас по всем моим ячейкам и требует отключения фонового представления для каждой из ячеек. Я хотел бы получить полный контроль над моим макетом. В идеале они должны быть "UITableViewStyleBareBones", но я не видел эту опцию в документах ...

Большое спасибо,

154
Tricky

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

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

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

26
frankodwyer

Вероятно, более простой способ добиться этого:

Objective-C:

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

Swift:

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)

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

304
samvermette

(Для тех, кто когда-либо попадал сюда из-за неправильного стиля таблицы) Измените стиль таблицы с простого на сгруппированный, через инспектор атрибутов или через код

let tableView = UITableView(frame: .zero, style: .grouped)
202
david72

WARNING: в этом решении реализован зарезервированный метод API. Это может помешать утверждению приложения Apple для распространения в AppStore.

Я описал приватные методы, которые превращают заголовки разделов, плавающие в мой блог

По сути, вам просто нужно создать подкласс UITableView и вернуть NO в двух из его методов:

- (BOOL)allowsHeaderViewsToFloat;
- (BOOL)allowsFooterViewsToFloat;
70
XAK

Хорошо, я знаю, что уже поздно, но я должен был это сделать. Я потратил 10 часов на поиск рабочего решения, но не нашел полного ответа. Нашел некоторые подсказки, но их трудно понять начинающим. Таким образом, я должен был вставить свои 2 цента и закончить ответ.

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

Итак, шаги есть.

  1. Реализуйте UITableView со стилем UITableViewStylePlain.

    -(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;
    }
    
  2. Реализуйте titleForHeaderInSection как обычно (вы можете получить это значение, используя собственную логику, но я предпочитаю использовать стандартные делегаты).

    - (NSString *)tableView: (UITableView *)tableView titleForHeaderInSection:(NSInteger)section
    {
        NSString *headerTitle = [sectionArray objectAtIndex:section];
        return headerTitle;
    }
    
  3. Воплощение numberOfSectionsInTableView как обычно

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    {
        int sectionCount = [sectionArray count];
        return sectionCount;
    }
    
  4. Реализуйте numberOfRowsInSection как обычно.

    - (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
    }
    
  5. Вернуть 0.0f в heightForHeaderInSection.

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
    {
        return 0.0f;
    }
    
  6. НЕ реализуйте viewForHeaderInSection. Удалите метод полностью вместо того, чтобы возвращать ноль.

  7. В высоту ForRowAtIndexPath. Проверьте, если (indexpath.row == 0) и верните желаемую высоту ячейки для заголовка раздела, иначе верните высоту ячейки.

    - (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
        }
    }
    
  8. Теперь в cellForRowAtIndexPath, проверьте, если (indexpath.row == 0), и реализуйте ячейку так, как вы хотите, чтобы был заголовок раздела, и установите стиль выбора равным none. Иначе реализуйте ячейку так, как вы хотите, чтобы была нормальная ячейка.

    - (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;
        }
    }
    
  9. Теперь реализуем willSelectRowAtIndexPath и возвращаем nil, если indexpath.row == 0. Это будет заботиться о том, чтобы didSelectRowAtIndexPath никогда не запускалось для строки заголовка раздела.

    - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            return nil;
        }
    
        return indexPath;
    }
    
  10. И, наконец, в didSelectRowAtIndexPath, проверьте, если (indexpath.row! = 0) и продолжить.

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

С этим вы сделали. Теперь у вас есть идеально прокручиваемый, не плавающий заголовок раздела.

28
Sumit Anantwar

В вашем Интерфейсном Разработчике нажмите на вашу проблему.

problem table view

Затем перейдите в "Инспектор атрибутов" и измените стиль "Простой" на "Сгруппированный";)

easy

25
Hannah Louisa Carney

Интересной особенностью UITableViewStyleGrouped является то, что tableView добавляет стиль к ячейкам, а не к TableView.

Стиль добавляется в ячейки как backgroundView как класс с именем UIGroupTableViewCellBackground, который обрабатывает рисование различного фона в соответствии с положением ячейки в разделе.

Поэтому очень простым решением будет использовать UITableViewStyleGrouped, установить backgroundColor таблицы в clearColor и просто заменить backgroundView ячейки в cellForRow:

cell.backgroundView = [[[UIView alloc] initWithFrame:cell.bounds] autorelease];
17
adamsiton

Измените ваш стиль TableView:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

Согласно Apple документации для UITableView:

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

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

11
Aks

Есть еще один хитрый способ. Основная идея состоит в том, чтобы удвоить номер раздела, и первый показывает только headerView, а второй показывает реальные ячейки.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return sectionCount * 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }
    return _rowCount;
}

Затем необходимо реализовать делегаты headerInSection:

- (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;
}

Этот подход также мало влияет на ваши источники данных:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    int real_section = (int)indexPath.section / 2;
    //your code
}

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

5
Lane

Самый простой способ получить то, что вы хотите, это установить стиль таблицы как UITableViewStyleGrouped, стиль разделителя как UITableViewCellSeparatorStyleNone:

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

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

5
Michail Pidgorodetskiy

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

4
Torbjörn

Для Swift 3 +

Просто используйте UITableViewStyleGrouped и установите высоту нижнего колонтитула в ноль со следующим:

override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return .leastNormalMagnitude
}
3
Tamás Sengel

Думая, как подойти к этой проблеме, я вспомнил очень важную деталь об UITableViewStyleGrouped. Способ, которым UITableView реализует сгруппированный стиль (округленные границы вокруг ячеек), заключается в добавлении пользовательского backgroundView в UITableViewCells, а не в UITableView. В каждую ячейку добавляется backgroundView в соответствии с его положением в разделе (верхние строки получают верхнюю часть границы раздела, средние - боковую, а нижний - ну, в общем, нижнюю часть). Итак, если мы просто хотим простой стиль, и у нас нет собственного backgroundView для наших ячеек (что имеет место в 90% случаев), тогда все, что нам нужно сделать, это использовать UITableViewStyleGrouped и удалить пользовательский фон , Это можно сделать, выполнив эти два шага:

Измените наш стиль tableView на UITableViewStyleGrouped. Добавьте следующую строку в cellForRow непосредственно перед тем, как мы вернем ячейку:

cell.backgroundView = [[[UIView alloc] initWithFrame: cell.bounds] autorelease];

И это все. Стиль tableView станет точно таким же, как UITableViewStylePlain, за исключением плавающих заголовков.

Надеюсь это поможет!

3
James Laurenstin

Вы можете добавить один раздел (с нулевыми строками) выше, а затем установить вышеупомянутый sectionFooterView в качестве headerView текущего раздела, footerView не плавает. Надеюсь, это поможет.

1
Lee Lam

Возможно, вы могли бы использовать scrollViewDidScroll в tableView и изменить contentInset на основе текущего видимого заголовка.

Кажется, работает для моего случая использования!

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

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

1
devguydavid

Проверьте ответ, как реализовать заголовки с помощью StoryBoard: Представления заголовков таблиц в StoryBoards

Также обратите внимание, что если вы не реализуете

viewForHeaderInSection:(NSInteger)section

это не будет плавать, что именно то, что вы хотите.

0
Raphael Oliveira

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

Это проще всего, если вы используете Interface Builder. Вы бы добавили UIView в верхней части представления (куда будут добавляться изображения), а затем добавили свое табличное представление под ним. IB должен измерить это соответственно; то есть верхняя часть табличного вида касается нижней части только что добавленного UIView, а его высота покрывает остальную часть экрана.

Мысль здесь заключается в том, что если этот UIView на самом деле не является частью табличного представления, он не будет прокручиваться вместе с табличным представлением. то есть игнорировать заголовок таблицы.

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

0
imnk

Чтобы полностью удалить заголовки плавающей секции, вы можете сделать это:

- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return [[UIView alloc] init];
}

return nil не работает.

Чтобы отключить плавающие, но по-прежнему показывать заголовки разделов, вы можете предоставить собственное представление с его собственным поведением.

0
djskinner