是否有可能禁用浮动头在UITableView与UITableViewStylePlain?

我正在使用UITableView来布局内容“页面”。我使用表视图的标题来布局某些图像等,我更喜欢它,如果它们不浮动,但当样式设置为UITableViewStyleGrouped时,它们会保持静态。

除了使用UITableViewStyleGrouped之外,还有其他方法吗?我想避免使用分组,因为它增加了我所有的单元格的边缘,并要求为每个单元格禁用背景视图。我想完全控制我的布局。理想情况下,它们应该是“UITableViewStyleBareBones”,但我在文档中没有看到这个选项…

非常感谢,

141403 次浏览

你可以通过在tableview delegate类中实现viewForHeaderInSection方法轻松实现。这个方法期望一个UIView作为返回对象(这是你的头视图)。我在代码中也做了同样的事情

您应该能够通过使用自定义单元格来处理标题行来伪造这一点。然后,这些单元格将像表视图中的任何其他单元格一样滚动。

你只需要在你的cellForRowAtIndexPath中添加一些逻辑,当单元格是标题行时,返回正确的单元格类型。

你可能不得不自己管理你的部分,也就是说,把所有的东西都放在一个部分,并伪造标题。(你也可以尝试为头视图返回一个隐藏视图,但我不知道这是否会工作)

警告:这个解决方案实现了一个保留的API方法。这可能会阻止苹果批准该应用程序在AppStore上发布。

我已经描述了在我的博客中使节头浮动的私有方法

基本上,你只需要子类化UITableView并在它的两个方法中返回NO:

- (BOOL)allowsHeaderViewsToFloat;
- (BOOL)allowsFooterViewsToFloat;

忽略XAK。如果你想让你的应用有机会被苹果接受,就不要探索任何私有方法。

如果您正在使用Interface Builder,这是最简单的。你可以在视图的顶部(图像所在的位置)添加一个UIView,然后在下面添加你的tableview。IB应相应地调整其大小;也就是说,tableview的顶部接触到你刚刚添加的UIView的底部,它的高度覆盖了屏幕的其余部分。

这里的想法是,如果UIView不是表格视图的一部分,它不会随着tableview滚动。即忽略tableview头。

如果你不使用界面构建器,它会更复杂一些因为你必须为tableview获得正确的定位和高度。

实现这一点的一个可能更简单的方法:

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

迅速:

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 = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: 0, right: 0)

分段标题现在将像任何常规单元格一样滚动。

关于UITableViewStyleGrouped有趣的事情是,tableView将样式添加到细胞,而不是添加到tableView。

样式作为backgroundView添加到单元格中,作为一个名为UIGroupTableViewCellBackground的类,它根据单元格在section中的位置来处理绘制不同的背景。

所以一个非常简单的解决方案是使用UITableViewStyleGrouped,设置表格的backgroundColor为clearColor,并简单地替换cellForRow中单元格的backgroundView:

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

虽然这可能不能解决您的问题,但当我想做类似的事情时,它确实解决了我的问题。我没有设置页眉,而是使用了上面部分的页脚。拯救我的是这个部分很小而且本质上是静态的,所以它不会滚动到视图底部以下。

这可以通过在UITableViewController的viewDidLoad方法中手动分配头视图来实现,而不是使用委托的viewForHeaderInSection和hightforheaderinsection。例如,在UITableViewController的子类中,你可以这样做:

- (void)viewDidLoad {
[super viewDidLoad];


UILabel *headerView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 40)];
[headerView setBackgroundColor:[UIColor magentaColor]];
[headerView setTextAlignment:NSTextAlignmentCenter];
[headerView setText:@"Hello World"];
[[self tableView] setTableHeaderView:headerView];
}

当用户滚动时,头视图将消失。我不知道为什么这样工作,但它似乎达到了你想要做的。

好吧,我知道很晚了,但我必须这么做。 到目前为止,我已经花了10个小时寻找一个可行的解决方案,但没有找到一个完整的答案。我发现了一些提示,但初学者很难理解。

.

.

.

正如在少数的答案中所建议的,我能够实现的唯一工作解决方案是通过在表视图中插入正常的单元格,并将它们作为Section Headers处理,但更好的方法是通过在每个Section的第0行插入这些单元格。这样我们就可以很容易地处理这些自定义的非浮动标头。

步骤是。

  1. 使用UITableViewStylePlain样式实现UITableView。

    -(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. Implement titleForHeaderInSection as usual ( you can get this value by using your own logic, but I prefer to use standard delegates ).

    - (NSString *)tableView: (UITableView *)tableView titleForHeaderInSection:(NSInteger)section
    {
    NSString *headerTitle = [sectionArray objectAtIndex:section];
    return headerTitle;
    }
    
  3. Immplement numberOfSectionsInTableView as usual

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
    int sectionCount = [sectionArray count];
    return sectionCount;
    }
    
  4. Implement numberOfRowsInSection as usual.

    - (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. Return 0.0f in heightForHeaderInSection.

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
    {
    return 0.0f;
    }
    
  6. DO NOT implement viewForHeaderInSection. Remove the method completely instead of returning nil.

  7. In heightForRowAtIndexPath. Check if(indexpath.row == 0) and return the desired cell height for the section header, else return the height of the cell.

    - (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. Now in cellForRowAtIndexPath, check if(indexpath.row == 0) and implement the cell as you want the section header to be and set the selection style to none. ELSE implement the cell as you want the normal cell to be.

    - (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. Now implement willSelectRowAtIndexPath and return nil if indexpath.row == 0. This will care that didSelectRowAtIndexPath never gets fired for the Section header row.

    - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    if (indexPath.row == 0)
    {
    return nil;
    }
    
    
    return indexPath;
    }
    
  10. And finally in didSelectRowAtIndexPath, check if(indexpath.row != 0) and proceed.

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

With this you are done. You now have a perfectly scrolling, non-floating section header.

检查如何用StoryBoard: 故事板中的表头视图实现头文件的答案

还要注意,如果您没有实现

viewForHeaderInSection:(NSInteger)section

它不会浮动,这正是你想要的。

在思考如何解决这个问题时,我想起了关于UITableViewStyleGrouped的一个非常重要的细节。 UITableView实现分组样式(单元格周围的圆形边框)的方式是通过添加一个自定义的backgroundView到UITableViewCells,而不是UITableView。每个单元格根据其在分段中的位置添加一个backgroundView(上面的行得到分段边界的上部,中间的行得到侧面边界,下面的行得到——好吧,底部部分)。 所以,如果我们只是想要一个普通的样式,并且我们没有为我们的单元格定制backgroundView(这是90%的情况),那么我们所需要做的就是使用UITableViewStyleGrouped,并删除定制的背景。这可以通过以下两个步骤来完成:

将我们的tableView样式改为UITableViewStyleGrouped 在cellForRow中添加以下一行,就在返回单元格之前:

细胞。backgroundView=[[UIView alloc] initWithFrame:cell。边界]autorelease);

就是这样。tableView样式将变得完全像UITableViewStylePlain,除了浮动头。

希望这能有所帮助!

要完全删除浮动section头部分,你可以这样做:

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

return nil不工作。

要禁用浮动,但仍然显示节头,您可以提供一个具有自己行为的自定义视图。

另一种方法是在你想要放置标题的区域之前创建一个空区域,然后把你的标题放在这个区域上。因为部分是空的,所以标题会立即滚动。

我有另一个更简单的解决方案,使用没有自动布局和一切通过XIB完成:

1/把你的头放在tableview中,直接拖放到tableview上。

2/在新建头部的尺寸检查器中,改变它的自动大小:你应该只保留顶部,左右锚,加上水平填充。

这就是标题的设置方式

这应该能奏效!

(对于那些因为错误的表格样式而来到这里的人)将表格样式从普通改为分组,通过属性检查器,或通过代码:

let tableView = UITableView(frame: .zero, style: .grouped)
得到你想要的最简单的方法是将你的表样式设置为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,不要忘记设置页眉高度和页眉视图,之后你必须得到你想要的。

在界面构建器中单击问题表视图

problem table view

然后导航到属性检查器,将样式纯改为分组 ,)简单的< / p >

easy

你可以在上面添加一个Section(零行),然后将上面的sectionFooterView设置为当前Section的headerView, footerView不浮动。

改变你的TableView样式:

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

根据苹果UITableView的文档:

UITableViewStylePlain一个普通的表视图。任何节头或 页脚显示为内联分隔符,并在表中浮动

UITableViewStyleGrouped-一个表视图,它的部分呈现不同的 行组。section页眉和页脚不浮动

还有另一种棘手的方法。主要思想是将节数加倍,第一个只显示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
}
与其他方法相比,这种方法是安全的,同时不改变tableview的frame或contentInsets。

.

.

也许你可以简单地让头视图背景透明:

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
view.tintColor = [UIColor clearColor];
}

或者在全球范围内应用:

    [UITableViewHeaderFooterView appearance].tintColor = [UIColor clearColor];
也许你可以在tableView上使用scrollViewDidScroll 并根据当前可见头文件更改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)
}
}

Swift 3+

简单地使用UITableViewStyleGrouped并将页脚的高度设置为0,如下所示:

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

根据@samvermette的回答,我已经在Swift中实现了上面的代码,以便于编码员使用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)

裁判:https://stackoverflow.com/a/26306212

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

脉冲

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 40
}else{
tableView.sectionHeaderHeight = 0
}


return 0
}

这有助于使用标题空间

@samvermette的解决方案的变体:

/// Allows for disabling scrolling headers in plain-styled tableviews
extension UITableView {


static let shouldScrollSectionHeadersDummyViewHeight = CGFloat(40)


var shouldScrollSectionHeaders: Bool {
set {
if newValue {
tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: bounds.size.width, height: UITableView.shouldScrollSectionHeadersDummyViewHeight))
contentInset = UIEdgeInsets(top: -UITableView.shouldScrollSectionHeadersDummyViewHeight, left: 0, bottom: 0, right: 0)
} else {
tableHeaderView = nil
contentInset = .zero
}
}


get {
return tableHeaderView != nil && contentInset.top == UITableView.shouldScrollSectionHeadersDummyViewHeight
}
}


}

代码片段仅为第一部分显示粘头。其他的节头将与单元格一起浮动。

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {


if section == 1 {
tableView.contentInset = .zero
}


}


func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) {
if section == 0 {
tableView.contentInset = .init(top: -tableView.sectionHeaderHeight, left: 0, bottom: 0, right: 0)
}
}

一个棘手的方法是为标题添加一个空部分。因为section没有单元格,所以它根本不会浮动。

**Swift 5.3 |编程**

private func buildTableView() -> UITableView {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.rowHeight = UITableView.automaticDimension
tableView.showsVerticalScrollIndicator = false
tableView.separatorStyle = .none
let dummyViewHeight: CGFloat = 80
tableView.tableFooterView = UIView(
frame: CGRect(x: .zero,
y: .zero,
width: tableView.bounds.size.width,
height: dummyViewHeight))


tableView.contentInset = UIEdgeInsets(top: .zero, left: .zero, bottom: -dummyViewHeight, right: .zero)
return tableView
}

以下2种场景的最佳解决方案:

场景1:如果tableView样式不是.grouped。改成分组没问题。页脚不会浮动。

场景2:如果你只想在tableView的末尾有一个页脚。它的风格是朴素的

步骤:

  1. 最后再加一节。
  2. 确保在添加的部分有(nuberOfRowsInSection方法)零行数。
  3. 向该节添加自定义页脚。
  4. 将highightforfooterinsection设置为自定义页脚,将. 0设置为其他部分的页脚。

例子:

func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {

//注意:过滤你想要添加自定义section的section

    let footerView = UIView()
submitOrganiser?.showFooterAtTheEnd(view: footerView) //my method to customise the footer, use your implementation
return footerView
}

如果你不介意使用UICollectionView,这也可以在iOS 13.0+中使用UICollectionView列表配置并将headerMode设置为.firstItemInSection来实现。

override func viewDidLoad() {
super.viewDidLoad()
var config = UICollectionLayoutListConfiguration.init(appearance: .plain)
config.headerMode = .firstItemInSection
let listLayout = UICollectionViewCompositionalLayout.list(using: config)
self.collectionView.collectionViewLayout = listLayout
}