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
Design UITableView's section header in Interface Builder
提问by Iulian Onofrei
I have a xib
file with a UITableView
for 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 Builder
and then change some of it's subview's properties programmatically?
我有一个xib
文件UITableView
,我想使用委托方法为其添加自定义节标题视图tableView:viewForHeaderInSection:
。有没有可能设计它Interface Builder
然后以编程方式更改它的一些子视图的属性?
My UITableView
has more section headers so creating one UIView
in Interface Builder
and 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 UIImage
s so UIImageView
s would show up blank.
我UITableView
有更多的章节标题,以便创建一个UIView
在Interface Builder
和返回这是行不通的,因为我不得不重复它,但没有做任何好的方法。存档和取消存档它对UIImage
s不起作用,因此UIImageView
s 会显示为空白。
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 .xib
files. Also, I don't have an UITableViewController
, just an UIViewController
in 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):
我最终使用本教程解决了它,该教程主要包括以下内容(适用于我的示例):
- Create
SectionHeaderView
class that subclassesUIView
. - Create
SectionHeaderView.xib
file and set it'sFile's Owner
'sCustomClass
to theSectionHeaderView
class. - Create an
UIView
property in the.m
file like:@property (strong, nonatomic) IBOutlet UIView *viewContent;
- Connect the
.xib
'sView
to thisviewContent
outlet. 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; }
- 创建
SectionHeaderView
子类的类UIView
。 - 创建
SectionHeaderView.xib
文件,并设置它File's Owner
的CustomClass
到SectionHeaderView
课。 UIView
在.m
文件中创建一个属性,如:@property (strong, nonatomic) IBOutlet UIView *viewContent;
- 将
.xib
'sView
连接到此viewContent
插座。 添加一个如下所示的初始化方法:
+ (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 UILabel
inside the .xib
file and connected it to the labelCategoryName
outlet and implemented the setCategoryName:
method inside the SectionHeaderView
class 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 UIImageView
s 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
Same
Storyboard
:return tableView.dequeueReusableCell(withIdentifier: "header")
Separate
XIB
(Additional step: you must register thatNib
first):tableView.register(UINib(nibName: "XIBSectionHeader", bundle:nil), forCellReuseIdentifier: "xibheader")
相同
Storyboard
:return tableView.dequeueReusableCell(withIdentifier: "header")
分开
XIB
(附加步骤:您必须先注册Nib
):tableView.register(UINib(nibName: "XIBSectionHeader", bundle:nil), forCellReuseIdentifier: "xibheader")
To load from a Storyboard
instead 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 UITableViewCell
is, 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
.
Use dequeueReusableCell:withIdentifier
to 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 ViewDidLoad
or (maybe better) ViewWillAppear
method, 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 viewForHeaderInSection
method, 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% 确定),但标签属性再次可能工作得很好。