ios 在 Interface Builder 中设计 UITableView 的部分标题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31693901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 07:02:23  来源:igfitidea点击:

Design UITableView's section header in Interface Builder

iosobjective-ciphoneuitableviewinterface-builder

提问by Iulian Onofrei

I have a xibfile with a UITableViewfor which I want to add a custom section header view using the delegate method tableView:viewForHeaderInSection:. Is there any possibility to design it in Interface Builderand then change some of it's subview's properties programmatically?

我有一个xib文件UITableView,我想使用委托方法为其添加自定义节标题视图tableView:viewForHeaderInSection:。有没有可能设计它Interface Builder然后以编程方式更改它的一些子视图的属性?

My UITableViewhas more section headers so creating one UIViewin Interface Builderand returning it doesn't work, because I'd have to duplicate it, but there isn't any good method of doing it. Archiving and unarchiving it doesn't work for UIImages so UIImageViews would show up blank.

UITableView有更多的章节标题,以便创建一个UIViewInterface Builder和返回这是行不通的,因为我不得不重复它,但没有做任何好的方法。存档和取消存档它对UIImages不起作用,因此UIImageViews 会显示为空白。

Also, I don't want to create them programmatically because they are too complex and the resulting code would be hard to read and maintain.

此外,我不想以编程方式创建它们,因为它们太复杂,并且生成的代码难以阅读和维护。

Edit 1: Here is my tableView:viewForHeaderInSection:method:

编辑1:这是我的tableView:viewForHeaderInSection:方法:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return nil;
    }

    CGSize headerSize = CGSizeMake(self.view.frame.size.width, 100);

    /* wrapper */

    UIView *wrapperView = [UIView viewWithSize:headerSize];

    wrapperView.backgroundColor = [UIColor colorWithHexString:@"2670ce"];

    /* title */

    CGPoint titleMargin = CGPointMake(15, 8);

    UILabel *titleLabel = [UILabel labelWithText:self.categoriesNames[section] andFrame:CGEasyRectMake(titleMargin, CGSizeMake(headerSize.width - titleMargin.x * 2, 20))];

    titleLabel.textColor = [UIColor whiteColor];
    titleLabel.font = [UIFont fontWithStyle:FontStyleRegular andSize:14];

    [wrapperView addSubview:titleLabel];

    /* body wrapper */

    CGPoint bodyWrapperMargin = CGPointMake(10, 8);

    CGPoint bodyWrapperViewOrigin = CGPointMake(bodyWrapperMargin.x, CGRectGetMaxY(titleLabel.frame) + bodyWrapperMargin.y);
    CGSize bodyWrapperViewSize = CGSizeMake(headerSize.width - bodyWrapperMargin.x * 2, headerSize.height - bodyWrapperViewOrigin.y - bodyWrapperMargin.y);

    UIView *bodyWrapperView = [UIView viewWithFrame:CGEasyRectMake(bodyWrapperViewOrigin, bodyWrapperViewSize)];

    [wrapperView addSubview:bodyWrapperView];

    /* image */

    NSInteger imageSize = 56;
    NSString *imageName = [self getCategoryResourceItem:section + 1][@"image"];

    UIImageView *imageView = [UIImageView imageViewWithImage:[UIImage imageNamed:imageName] andFrame:CGEasyRectMake(CGPointZero, CGEqualSizeMake(imageSize))];

    imageView.layer.masksToBounds = YES;
    imageView.layer.cornerRadius = imageSize / 2;

    [bodyWrapperView addSubview:imageView];

    /* labels */

    NSInteger labelsWidth = 60;

    UILabel *firstLabel = [UILabel labelWithText:@"first" andFrame:CGRectMake(imageSize + bodyWrapperMargin.x, 0, labelsWidth, 16)];

    [bodyWrapperView addSubview:firstLabel];

    UILabel *secondLabel = [UILabel labelWithText:@"second" andFrame:CGRectMake(imageSize + bodyWrapperMargin.x, 20, labelsWidth, 16)];

    [bodyWrapperView addSubview:secondLabel];

    UILabel *thirdLabel = [UILabel labelWithText:@"third" andFrame:CGRectMake(imageSize + bodyWrapperMargin.x, 40, labelsWidth, 16)];

    [bodyWrapperView addSubview:thirdLabel];

    [@[ firstLabel, secondLabel, thirdLabel ] forEachView:^(UIView *view) {
        UILabel *label = (UILabel *)view;

        label.textColor = [UIColor whiteColor];
        label.font = [UIFont fontWithStyle:FontStyleLight andSize:11];
    }];

    /* line */

    UIView *lineView = [UIView viewWithFrame:CGRectMake(imageSize + labelsWidth + bodyWrapperMargin.x * 2, bodyWrapperMargin.y, 1, bodyWrapperView.frame.size.height - bodyWrapperMargin.y * 2)];

    lineView.backgroundColor = [UIColor whiteColorWithAlpha:0.2];

    [bodyWrapperView addSubview:lineView];

    /* progress */

    CGPoint progressSliderOrigin = CGPointMake(imageSize + labelsWidth + bodyWrapperMargin.x * 3 + 1, bodyWrapperView.frame.size.height / 2 - 15);
    CGSize progressSliderSize = CGSizeMake(bodyWrapperViewSize.width - bodyWrapperMargin.x - progressSliderOrigin.x, 30);

    UISlider *progressSlider = [UISlider viewWithFrame:CGEasyRectMake(progressSliderOrigin, progressSliderSize)];

    progressSlider.value = [self getCategoryProgress];

    [bodyWrapperView addSubview:progressSlider];

    return wrapperView;
}

and I would want it to look something like this:

我希望它看起来像这样:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return nil;
    }

    SectionView *sectionView = ... // get the view that is already designed in the Interface Builder

    sectionView.headerText = self.categoriesNames[section];
    sectionView.headerImage = [self getCategoryResourceItem:section + 1][@"image"];

    sectionView.firstLabelText = @"first";
    sectionView.secondLabelText = @"second";
    sectionView.thirdLabelText = @"third";

    sectionView.progress = [self getCategoryProgress];

    return wrapperView;
}

Edit 2: I'm not using a Storyboard, just .xibfiles. Also, I don't have an UITableViewController, just an UIViewControllerin which I added an UITableView.

编辑 2:我没有使用Storyboard,只是.xib文件。另外,我没有UITableViewController,只是UIViewController在其中添加了UITableView.

采纳答案by Iulian Onofrei

I finally solved it using this tutorial, which, largely consists of the following (adapted to my example):

我最终使用本教程解决了它,该教程主要包括以下内容(适用于我的示例)

  1. Create SectionHeaderViewclass that subclasses UIView.
  2. Create SectionHeaderView.xibfile and set it's File's Owner's CustomClassto the SectionHeaderViewclass.
  3. Create an UIViewproperty in the .mfile like: @property (strong, nonatomic) IBOutlet UIView *viewContent;
  4. Connect the .xib's Viewto this viewContentoutlet.
  5. Add an initializer method that looks like this:

    + (instancetype)header {
    
        SectionHeaderView *sectionHeaderView = [[SectionHeaderView alloc] init];
    
        if (sectionHeaderView) { // important part
            sectionHeaderView.viewContent = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:sectionHeaderView options:nil] firstObject];
    
            [sectionHeaderView addSubview:sectionHeaderView.viewContent];
    
            return sectionHeaderView;
        }
    
        return nil;
    }
    
  1. 创建SectionHeaderView子类的类UIView
  2. 创建SectionHeaderView.xib文件,并设置它File's OwnerCustomClassSectionHeaderView课。
  3. UIView.m文件中创建一个属性,如:@property (strong, nonatomic) IBOutlet UIView *viewContent;
  4. .xib'sView连接到此viewContent插座。
  5. 添加一个如下所示的初始化方法:

    + (instancetype)header {
    
        SectionHeaderView *sectionHeaderView = [[SectionHeaderView alloc] init];
    
        if (sectionHeaderView) { // important part
            sectionHeaderView.viewContent = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:sectionHeaderView options:nil] firstObject];
    
            [sectionHeaderView addSubview:sectionHeaderView.viewContent];
    
            return sectionHeaderView;
        }
    
        return nil;
    }
    

Then, I added an UILabelinside the .xibfile and connected it to the labelCategoryNameoutlet and implemented the setCategoryName:method inside the SectionHeaderViewclass like this:

然后,我UILabel.xib文件内部添加了一个并将其连接到labelCategoryName插座并setCategoryName:SectionHeaderView类中实现了这样的方法:

- (void)setCategoryName:(NSString *)categoryName {

    self.labelCategoryName.text = categoryName;
}

I then implemented the tableView:viewForHeaderInSection:method like this:

然后我实现了tableView:viewForHeaderInSection:这样的方法:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    SectionHeaderView *sectionHeaderView = [SectionHeaderView header];

    [sectionHeaderView setCategoryName:self.categoriesNames[section]];

    return sectionHeaderView;
}

And it finally worked. Every section has it's own name, and also UIImageViews show up properly.

它终于奏效了。每个部分都有自己的名称,并且UIImageView显示正确。

Hope it helps others that stumble over the same wrong solutions over and over again, all over the web, like I did.

希望它可以帮助像我一样在整个网络上一遍又一遍地绊倒相同错误解决方案的其他人。

回答by SwiftArchitect

Storyboard or XIB

故事板或 XIB

  1. Same Storyboard:

    return tableView.dequeueReusableCell(withIdentifier: "header")
    

    Two Section Headers

  2. Separate XIB(Additional step: you must register that Nibfirst):

    tableView.register(UINib(nibName: "XIBSectionHeader", bundle:nil),
                       forCellReuseIdentifier: "xibheader")
    
  1. 相同Storyboard

    return tableView.dequeueReusableCell(withIdentifier: "header")
    

    两个部分标题

  2. 分开XIB(附加步骤:您必须先注册Nib):

    tableView.register(UINib(nibName: "XIBSectionHeader", bundle:nil),
                       forCellReuseIdentifier: "xibheader")
    

To load from a Storyboardinstead of a XIB, see this Stack Overflow answer.

要从 aStoryboard而不是 a加载XIB,请参阅此堆栈溢出答案



Using UITableViewCell to create Section Header in IB

在IB中使用UITableViewCell创建Section Header

Take advantage of the fact that a section header is a regular UIView, and that UITableViewCellis, too, a UIView. In Interface Builder, drag & drop a Table View Cellfrom the Object Libraryonto your Table View Prototype Content.

利用节标题是常规UIView,也UITableViewCell就是UIView. 在Interface Builder 中,从Object Library 中拖放一个Table View Cell到您的Table View Prototype Content

Add an Identifierto the newly added Table View Cell, and customize its appearance to suit your needs. For this example, I used header.

标识符添加到新添加的Table View Cell,并自定义其外观以满足您的需求。在这个例子中,我使用了header.

Edit the cell

编辑单元格

Use dequeueReusableCell:withIdentifierto locate your section header, just like you would any table view cell. You will need to supply heightForHeaderInSection, which is hardcoded as 44for clarity:

使用dequeueReusableCell:withIdentifier找到您的节标题,就像您在任何表格视图单元格。您将需要提供heightForHeaderInSection,为清楚起见,硬编码为44

//MARK: UITableViewDelegate
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
    // This is where you would change section header content
    return tableView.dequeueReusableCell(withIdentifier: "header")
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
    return 44
}


Swift 2 & earlier:

Swift 2 及更早版本:

return tableView.dequeueReusableCellWithIdentifier("header") as? UIView
self.tableView.registerNib(UINib(nibName: "XIBSectionHeader", bundle:nil),
    forCellReuseIdentifier: "xibheader")


? Find this solution on GitHuband additional details on Swift Recipes.

? 在GitHub 上找到此解决方案,在Swift Recipes找到更多详细信息。

回答by Bevan

Solution Is way simple

解决方法很简单

Create one xib, make UI according to your Documentation then in viewForHeaderInSection get xib

创建一个 xib,根据您的文档制作 UI,然后在 viewForHeaderInSection 中获取 xib

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

        NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"HeaderView" owner:self options:nil];

       HeaderView *headerView = [nibArray objectAtIndex:0];

    return headerView;
} 

回答by Michael Dautermann

As far as I understand your problem, you want to have the same UIView duplicated multiple times for the multiple section headers you want to be able to display.

据我了解您的问题,您希望为希望能够显示的多个部分标题多次复制相同的 UIView。

If this were my problem, here is how I would solve it.

如果这是我的问题,这里是我将如何解决它。

ORIGINAL SOLUTION

原始解决方案

1)

1)

In my UIViewController that owns the table view, I'd also create a view that's a template for the header. Assign that to a IBOutlet. This will be the view you can edit via Interface Builder.

在拥有表视图的 UIViewController 中,我还将创建一个视图,该视图是标题的模板。将其分配给 IBOutlet。 这将是您可以通过 Interface Builder 编辑的视图

2)

2)

In your ViewDidLoador (maybe better) ViewWillAppearmethod, you'll want to make as many copies of that header template UIView as you'll need to display for section headers.

在您的ViewDidLoad或(也许更好)ViewWillAppear方法中,您需要制作尽可能多的该标题模板 UIView 的副本,因为您需要为节标题显示。

Making copies of UIViews in memory isn't trivial, but it isn't hard either. Here is an answer from a related questionthat shows you how to do it.

在内存中复制 UIViews 并非易事,但也不难。 这是来自相关问题的答案,向您展示了如何做到这一点。

Add the copies to a NSMutableArray(where the index of each object will correspond to the sections... the view in index 0 of the array will be what you return for section 0, view 1 in the array for section 1, ec.).

将副本添加到 a NSMutableArray(其中每个对象的索引将对应于部分...数组索引 0 中的视图将是您为部分 0 返回的内容,对于部分 1 的数组中的视图 1,等等)。

3)

3)

You will not be able to use IBOutlets for the elementsof that section header (because your code only associates outlets with one particular view from the XIB file).

您将无法将 IBOutlets 用于该部分标题的元素(因为您的代码仅将出口与 XIB 文件中的一个特定视图相关联)。

So for this, you'll probably want to use view tag properties for each of the UI elements in your header view that you'll want to modify/change for each different section. You can set these tags via Interface Builder and then refer to them programmatically in your code.

因此,为此,您可能希望为标题视图中的每个 UI 元素使用视图标记属性,您需要为每个不同的部分修改/更改这些元素。您可以通过 Interface Builder 设置这些标签,然后在代码中以编程方式引用它们。

In your viewForHeaderInSectionmethod, you'll do something like:

在您的viewForHeaderInSection方法中,您将执行以下操作:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return nil;
    }

    SectionView *sectionView = [self.arrayOfDuplicatedHeaderViews objectAtIndex: section];
    // my title view has a tag of 10
    UILabel *titleToModify = [sectionView viewWithTag: 10]; 
    if(titleToModify)
    {
        titleToModify.text = [NSString stringWithFormat:@"section %d", section];
    }

    return sectionView;
}

Makes sense?

说得通?

DIFFERENT SOLUTION

不同的解决方案

1)

1)

You'd still need an array of UIViews (or "Section View" subclassed UIViews) but you could create each of those with successive calls to load the view from it's own XIB file.

您仍然需要一组 UIViews(或“ Section View”子类 UIViews),但您可以通过连续调用创建每个 UIViews 以从它自己的 XIB 文件加载视图。

Something like this:

像这样的东西:

@implementation SectionView

+ (SectionView*) getSectionView
{
  NSArray* array = [[NSBundle mainBundle] loadNibNamed:@"SectionView" owner:nil options:nil];
  return [array objectAtIndex:0]; // assume that SectionView is the only object in the xib
}

@end

(more detail found in the answer to this related question)

在此相关问题的答案中可以找到更多详细信息)

2)

2)

You mightbe able to use IBOutlets on this (but I'm not 100% certain), but tag properties once again might work pretty well.

也许可以在这方面使用 IBOutlets(但我不是 100% 确定),但标签属性再次可能工作得很好。