【问题标题】:Overriding NSLayoutConstraint in an inherited class在继承的类中覆盖 NSLayoutConstraint
【发布时间】:2018-06-18 16:13:55
【问题描述】:

我的代码中有两个类,一个是NameCell,它包含一个带有文本的简单UILabel。第二个是NameValueCell,它继承自该类,但还添加了属性UIView *valueView

需要更改一个布局约束。我正在寻找一种方法来覆盖:

H:|[nameView]| - nameView 应该占据NameCell 的全宽

H:|[nameView][valueView(==nameView)]| - nameViewvalueView 的宽度比应为 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

【问题讨论】:

    标签: ios objective-c autolayout nslayoutconstraint ios-autolayout


    【解决方案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];
      

      这是换出约束的一般模式,这里的类/子类关系没有理由不使用它。

      【讨论】:

      • 另外,我认为视觉格式语言很适合,因为事实证明它越来越不够;我建议您切换到较新的基于锚的符号。
      猜你喜欢
      • 1970-01-01
      • 2012-11-20
      • 2019-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多