提问人:DBSoft 提问时间:9/7/2022 更新时间:9/7/2022 访问量:115
在 Objective-C 中使用 UIStackView 扩展 UITableViewCell
Extending UITableViewCell with UIStackView in Objective-C
问:
我有自己的跨平台小部件布局系统,我用它来做大多数事情,所以我以前从未使用过布局约束,所以这是我第一次深入研究它。我的代码是用 Objective-C 而不是 Swift 编写的,可悲的是,我发现的所有示例都是用 Swift 编写的。默认情况下,我的 UITableView 只会显示图标和文本。但是,它具有额外的“列”图标和文本,这些图标和文本将位于 MacOS 或其他桌面平台上的单独列中。我有一个切换开关,允许在水平或垂直 UIStackView 中显示这些附加信息。因此,如果切换,我想将 UIStackView 添加到放置在 textLabel 下的 UITableViewCell 中,将 UITableViewCell 扩展为 UIStackView 所需的任何大小。我的想法是,我可以通过添加 3 个将 UIStackView、contentView 和 textLabel 连接在一起的约束来做到这一点。这没有产生预期的结果。前两个约束正确地将 UIStackView 定位在 textLabel 下方,但未展开 UITableViewCell。添加第三个(底部)约束以展开单元格,则会将 UIStackView 完全剪裁出单元格。有人可以指出我做错了什么吗?
NSLayoutConstraint *constraint;
stack = [[[UIStackView alloc] init] retain];
[stack setTranslatesAutoresizingMaskIntoConstraints:NO];
[stack setSpacing:5.0];
[[self contentView] addSubview:stack];
/* Leading */
constraint = [NSLayoutConstraint constraintWithItem:stack attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual toItem:[self contentView]
attribute:NSLayoutAttributeLeft multiplier: 1.0 constant:0.0];
[[self contentView] addConstraint:constraint];
/* Top */
constraint = [NSLayoutConstraint constraintWithItem:stack attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual toItem:[self textLabel]
attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
[[self contentView] addConstraint:constraint];
/* Bottom */
constraint = [NSLayoutConstraint constraintWithItem:[self contentView] attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual toItem:stack
attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
[[self contentView] addConstraint:constraint];
答:
通过使用“现代”约束语法,您可以节省大量键入工作,并使代码更具可读性。
例如,如果我们创建了标签和堆栈视图,并将它们添加到单元格的内容视图中:
// let's use the default margins
UILayoutGuide *g = self.contentView.layoutMarginsGuide;
[NSLayoutConstraint activateConstraints:@[
// constrain label Top/Leading/Trailing to content margins guide
[label.topAnchor constraintEqualToAnchor:g.topAnchor constant:0.0],
[label.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[label.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
// no Height, because we'll use the label's intrinsic size
// constrain stack view Top to label bottom plus 8-points
[stack.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8.0],
// Leading/Trailing/Bottom to content margins guide
[stack.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[stack.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
[stack.bottomAnchor constraintEqualToAnchor:g.bottomAnchor constant:0.0],
// no Height, because we'll use the arranged subviews heights
]];
因此,我们可以像这样编写一个单元格类:
堆栈TableViewCell.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface StackTableViewCell : UITableViewCell
@end
NS_ASSUME_NONNULL_END
堆栈表视图Cell.m
#import "StackTableViewCell.h"
@interface StackTableViewCell()
{
UIStackView *stack;
UILabel *label;
}
@end
@implementation StackTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)init
{
self = [super init];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
// create a label
label = [UILabel new];
[label setBackgroundColor:UIColor.greenColor];
[label setNumberOfLines:0];
// create a vertical stack view
stack = [UIStackView new];
[stack setAxis:UILayoutConstraintAxisVertical];
[stack setSpacing:5.0];
// add label and stack view to content view
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
[stack setTranslatesAutoresizingMaskIntoConstraints:NO];
[[self contentView] addSubview:label];
[[self contentView] addSubview:stack];
// let's use the default margins
UILayoutGuide *g = self.contentView.layoutMarginsGuide;
[NSLayoutConstraint activateConstraints:@[
// constrain label Top/Leading/Trailing to content margins guide
[label.topAnchor constraintEqualToAnchor:g.topAnchor constant:0.0],
[label.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[label.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
// no Height, because we'll use the label's intrinsic size
// constrain stack view Top to label bottom plus 8-points
[stack.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8.0],
// Leading/Trailing/Bottom to content margins guide
[stack.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[stack.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
[stack.bottomAnchor constraintEqualToAnchor:g.bottomAnchor constant:0.0],
// no Height, because we'll use the arranged subviews heights
]];
// let's give the stack view a border so we can see its frame
stack.layer.borderColor = UIColor.redColor.CGColor;
stack.layer.borderWidth = 1.0;
}
@end
以及一个基本的控制器......
堆栈TableViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface StackTableViewController : UITableViewController
@end
NS_ASSUME_NONNULL_END
堆栈TableViewController.m
#import "StackTableViewController.h"
#import "StackTableViewCell.h"
@interface StackTableViewController ()
@end
@implementation StackTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:StackTableViewCell.class forCellReuseIdentifier:@"c"];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 30;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
StackTableViewCell *cell = (StackTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"c" forIndexPath:indexPath];
// Configure the cell...
return cell;
}
@end
此时,由于我们没有为单元格提供任何数据 - 并且标签和堆栈视图在空时没有固有高度 - 它将如下所示:
因此,让我们添加一个方法:fillData
堆栈TableViewCell.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface StackTableViewCell : UITableViewCell
// add this line
- (void)fillData:(NSInteger)n;
@end
NS_ASSUME_NONNULL_END
堆栈表视图Cell.m
// add this method
- (void)fillData:(NSInteger)n {
label.text = [NSString stringWithFormat:@"Cell %ld", (long)n];
// cells are reused, so first clear any existing labels in the stack view
// this is inefficient, but we're just demonstrating the cell sizing
for (UIView *v in stack.arrangedSubviews) {
[v removeFromSuperview];
}
// now add n number of labels
for (int i = 0; i < n; i++) {
UILabel *v = [UILabel new];
v.text = [NSString stringWithFormat:@"Stack Label %ld", (long)i];
v.backgroundColor = UIColor.yellowColor;
[stack addArrangedSubview:v];
}
}
并修改控制器以设置一些单元格数据:
堆栈TableViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
StackTableViewCell *cell = (StackTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"c" forIndexPath:indexPath];
// Configure the cell...
[cell fillData:(indexPath.row % 4) + 1];
return cell;
}
现在输出如下所示:
评论
.textLabel
contentView
UITableViewCell
评论
UITableView
rowHeight
tableView.rowHeight = UITableViewAutomaticDimension
estimatedRowHeight
tableView.estimatedRowHeight = 40
retain
isActive