iOS UICollectionViewCell 可调整大小的虚线边框

iOS UICollectionViewCell resizable dashed border

我有一个 UICollectionView,其中一些单元格应该有虚线边框,而一些单元格应该有实线边框。此外,根据数据模型中存在的内容,单元格的大小可以不同。

我遇到的问题是我无法使虚线边框与集合视图单元格大小相同,并且单元格大小可以根据内容更改。但基本上,单元格应该有虚线边框或实线边框。实心边框很容易调整到正确的大小。

这是它现在的样子的照片。虚线边框为绿色只是为了便于查看。

这里是视图层次调试视图。这里有两条虚线边框,因为我一直在试验。绿色边框是 UICollectionViewCell 根层上的子层。灰色边框是单独视图的子层,该单独视图是集合视图单元格的 contentView 属性.

的子视图

代码

方法 1 - 添加带有子图层的专用视图

我在这里尝试添加一个带有虚线边框的 UIView subclass。然后,当我需要显示虚线边框或隐藏虚线边框时,我只需相应地设置视图的 hidden 属性 即可。这工作正常,除了我无法调整虚线边框子层的大小。

视图正在根据 AutoLayout 约束调整为正确的宽度和高度,如上面的视图层次结构调试器屏幕截图所示。但是子层仍然是原始大小(大约 50px x 50px,我猜这是来自 UICollectionView 因为我没有在任何地方指定那个大小)。

对于这个实现,我有一个名为 MyResizableSublayerView 的自定义 UIView subclass。它覆盖 layoutSublayersOfLayer 来处理子图层的大小调整,或者至少这是应该发生的事情,但显然它不起作用。

但是随后 MyResizableSublayerView class 在集合视图单元格中用于将虚线边框添加到视图层次结构中。

MyResizableSublayerView
@interface MyResizableSublayerView : UIView

@property (strong, nonatomic) CAShapeLayer *borderLayer;

+ (instancetype)viewWithBorderSublayer:(CAShapeLayer *)shapeLayer;

@end




@implementation MyResizableSublayerView

+ (instancetype)viewWithBorderSublayer:(CAShapeLayer *)shapeLayer {
    CIResizableSublayerView *view = [[MyResizableSublayerView alloc] init];
    view.borderLayer = shapeLayer;
    return view;
}

- (void)setBorderLayer:(CAShapeLayer *)borderLayer {
    if (self->_borderLayer) {
        [self->_borderLayer removeFromSuperlayer];
    }
    
    self->_borderLayer = borderLayer;
    [self.layer addSublayer:self->_borderLayer];
}

- (void)layoutSublayersOfLayer:(CALayer *)layer {
    [super layoutSublayersOfLayer:layer];
    self.borderLayer.frame = layer.bounds;
}


@end
MyCollectionViewCell
@interface MyCollectionViewCell ()

@property (strong, nonatomic) MyResizableSublayerView *unavailableBorderView;

@end



@implementation MyCollectionViewCell

- (instancetype)init {
    return [self initWithFrame:CGRectZero];
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    if (self = [super initWithCoder:coder]) {
        [self initialize];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initialize];
    }
    return self;
}

- (void)initialize {
    self.contentView.layer.cornerRadius = 4.0f;
    self.contentView.layer.borderWidth = 1.0f;

    //... add other subviews
    [self.contentView addSubview:self.unavailableBorderView];
    
    [NSLayoutConstraint activateConstraints:@[
        [self.contentView.widthAnchor constraintLessThanOrEqualToConstant:250.0],
        [self.contentView.widthAnchor constraintGreaterThanOrEqualToConstant:100.0],
        
        [self.unavailableBorderView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
        [self.unavailableBorderView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
        [self.unavailableBorderView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
        [self.unavailableBorderView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor],
        
        //... constraints for other views
    ]];
}

- (MyResizableSublayerView *)unavailableBorderView {
    if (!self->_unavailableBorderView) {
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.strokeColor = [UIColor colorWithRed:0xE0/255.0 green:0xE0/255.0 blue:0xE0/255.0 alpha:1.0].CGColor;
        layer.lineWidth = 4.0;
        layer.lineJoin = kCALineJoinRound;
        layer.fillColor = [UIColor clearColor].CGColor;
        layer.lineDashPattern = @[@4, @4];
        layer.frame = self.contentView.bounds;
        layer.path = [UIBezierPath bezierPathWithRoundedRect:self.contentView.bounds cornerRadius:self.contentView.layer.cornerRadius].CGPath;

        self->_unavailableBorderView = [MyResizableSublayerView viewWithBorderSublayer:layer];
        self->_unavailableBorderView.translatesAutoresizingMaskIntoConstraints = NO;
        self->_unavailableBorderView.layer.cornerRadius = self.contentView.layer.cornerRadius;
        self->_unavailableBorderView.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.0];
    }
    return self->_unavailableBorderView;
}

//... more logic

@end

方法 2 - 直接添加到 UICollectionViewCell

对于这种方法,我将 CAShapeLayer 直接添加到 UICollectionViewCell,然后覆盖 layoutSublayersOfLayer 以尝试调整虚线边框子层的大小,但这也不起作用。

@interface MyCollectionViewCell ()

@property (strong, nonatomic) CAShapeLayer *unavailableBorderLayer;

@end



@implementation MyCollectionViewCell

- (instancetype)init {
    return [self initWithFrame:CGRectZero];
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    if (self = [super initWithCoder:coder]) {
        [self initialize];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initialize];
    }
    return self;
}

- (void)initialize {
    self.contentView.layer.cornerRadius = 4.0f;
    self.contentView.layer.borderWidth = 1.0f;

    //... add other subviews
    
    [NSLayoutConstraint activateConstraints:@[
        [self.contentView.widthAnchor constraintLessThanOrEqualToConstant:250.0],
        [self.contentView.widthAnchor constraintGreaterThanOrEqualToConstant:100.0],
        
        //... constraints for other views
    ]];
    
    
    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.strokeColor = [UIColor greenColor].CGColor;
    layer.lineWidth = 2.0;
    layer.lineJoin = kCALineJoinRound;
    layer.fillColor = [UIColor clearColor].CGColor;
    layer.lineDashPattern = @[@4, @4];
    layer.frame = self.contentView.bounds;
    layer.path = [UIBezierPath bezierPathWithRoundedRect:self.contentView.bounds cornerRadius:self.contentView.layer.cornerRadius].CGPath;
    self->_unavailableBorderLayer = layer;
    [self.layer addSublayer:self->_unavailableBorderLayer];
}

- (void)layoutSublayersOfLayer:(CALayer *)layer {
    [super layoutSublayersOfLayer:layer];
    self.unavailableBorderLayer.frame = self.bounds;
}

//... more logic

@end

问题

我对此有几个问题。

  1. 我的代码有什么问题不允许虚线边框调整为与集合视图单元格相同的大小?
  2. 哪种方法是向集合视图单元格添加虚线边框的最佳方法。还是有比我在这里列出的方法更好的方法?同样,我的目标是能够显示或隐藏虚线边框,并使其与动态调整大小的集合视图单元格大小相同。

不太清楚您对内容视图的约束做了什么...但是,如果您得到想要的布局(虚线边框除外),请试一试。

首先,代替layoutSublayersOfLayer,使用:

- (void)layoutSubviews {
    [super layoutSubviews];
    _unavailableBorderLayer.frame = self.bounds;
}