在继承的类中覆盖 NSLayoutConstraint
Posted
技术标签:
【中文标题】在继承的类中覆盖 NSLayoutConstraint【英文标题】:Overriding NSLayoutConstraint in an inherited class 【发布时间】:2018-06-18 16:13:55 【问题描述】:我的代码中有两个类,一个是NameCell
,它包含一个带有文本的简单UILabel。第二个是NameValueCell
,它继承自该类,但还添加了属性UIView *valueView
。
需要更改一个布局约束。我正在寻找一种方法来覆盖:
H:|[nameView]|
- nameView
应该占据NameCell
的全宽
与
H:|[nameView][valueView(==nameView)]|
- nameView
与 valueView
的宽度比应为 NameValueCell
中的 1:1
重写 NSLayoutConstraint 的最佳实践是什么?我必须在我的代码中坚持继承,因为我的应用程序需要许多不同的 UITableViewCell 特化。
NameCell.h:
@interface NameCell : UITableViewCell
@property (nonatomic, retain) IBOutlet UIView *nameView;
@property (nonatomic, retain) IBOutlet UILabel *nameLabel;
@end
NameValueCell.h:
@interface NameValueCell : NameCell
@property (nonatomic, retain) IBOutlet UIView *valueView;
@end
NameCell.m:
@implementation NameCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
UIView *nameView = [[UIView alloc] init];
self.nameView = nameView;
[self.contentView addSubview:self.nameView];
UILabel *nameLabel = [[UILabel alloc] init];
self.nameLabel = nameLabel;
[self.nameView addSubview:self.nameLabel];
NSDictionary *views = NSDictionaryOfVariableBindings(nameView, nameLabel);
NSArray *constraints;
// The constraint that should be overridden
constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameView]|"
options: 0
metrics:nil
views:views];
[self.contentView addConstraints:constraints];
return self;
@end
NameValueCell.m:
@implementation NameValueCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
NSString *reuseID = reuseIdentifier;
UIView *valueView = [[UIView alloc] init];
self.valueView = valueView;
[self.contentView addSubview:self.valueView];
NSDictionary *views = @
@"nameView": self.nameView,
@"nameLabel": self.nameLabel,
@"valueView": self.valueView
;
NSArray *constraints;
// The overriding constraint
constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameView][valueView(==nameView)]|"
options: 0
metrics:nil
views:views];
[self.contentView addConstraints:constraints];
return self;
@end
【问题讨论】:
【参考方案1】:子类想要增强超类的行为,例如,通过添加额外的子视图;但在创建约束时,它也希望覆盖超类。要做到这两点,最好的方法是将视图创建和约束创建代码分解出来,然后在子类中,通过有选择地调用super
来控制我们是扩充还是覆盖。
首先,排除...
// in NameCell.m initWithStyle
// super initWithStyle..., if (self) ...
[self addCustomSubviews];
[self addCustomConstraints];
在NameCell
中,这些新方法应该完全按照您在问题中内联的方式实现,但在子类中:(1) 根本不实现 init,允许超类 init 调用分解后的代码, (2) 覆盖分解后的代码,如下所示...
// NameValueCell.m
- (void)addCustomSubviews
// augmenting super here, so call super...
[super addCustomSubviews];
// add the value view
UIView *valueView = [[UIView alloc] init];
// and so on
- (void)addCustomConstraints
// overriding super here, so don't call super, just add constraints
NSDictionary *views = @
// and so in
在一个不太繁琐但不太清晰的替代方案中,您可以保持 init 保持原样,但在子类 init 中,删除刚刚在超级类中创建的约束...
// in NameValueCell.m, in the initWithStyle method, before creating constraints
[self removeConstraints:self.constraints]; // then ...
NSDictionary *views = @ // and so on...
我不会将这种替代方法称为最佳(甚至是好的)做法,但我认为它应该有效。
【讨论】:
【参考方案2】:第一:不要添加约束; 激活他们。它更简单,更不容易出错。
好的,那么。只需在 NSArray 实例变量中保留对可能需要替换的约束的引用:
constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameView]|"
options: 0
metrics:nil
views:views];
self.removeableConstraints = constraints; // an instance property
[NSLayoutConstraint activateConstraints: constraints];
现在子类要做的就是停用 self.removeableConstraints
并激活它的替代约束。
[NSLayoutConstraint deactivateConstraints: self.removeableConstraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameView][valueView(==nameView)]|"
options: 0
metrics:nil
views:views];
[NSLayoutConstraint activateConstraints: constraints];
这是换出约束的一般模式,这里的类/子类关系没有理由不使用它。
【讨论】:
另外,我认为视觉格式语言很适合,因为事实证明它越来越不够;我建议您切换到较新的基于锚的符号。以上是关于在继承的类中覆盖 NSLayoutConstraint的主要内容,如果未能解决你的问题,请参考以下文章