Граница вокруг раздела UITableView

Как мы можем иметь границу вокруг каждого раздела таблицы? Прикрепленное изображение, чтобы показать, что я ищу. Если вы посмотрите на изображение, у него есть граница вокруг каждого раздела таблицы.

enter image description here

Ответ 1

В этом выпуске я адаптировал ответ jvanmetre для округления углов tableviews, просто добавьте метод делегата tableView:willDisplayCell:forRowAtIndexPath: со следующим кодом (простая копия/вставка должен работать), он должен работать и для сгруппированных таблиц. Я прокомментировал, где вы должны установить ширину и цвет границы.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
    {

    if ([cell respondsToSelector:@selector(tintColor)]) {
        CGFloat cornerRadius = 5.f;
        cell.backgroundColor = UIColor.clearColor;
        CAShapeLayer *layer = [[CAShapeLayer alloc] init];
        CGMutablePathRef pathRef = CGPathCreateMutable();
        CGRect bounds = CGRectInset(cell.bounds, 10, 0);
        BOOL addLine = NO;
        if (indexPath.row == 0 && indexPath.row == [tableView numberOfRowsInSection:indexPath.section]-1) {
            CGPathAddRoundedRect(pathRef, nil, bounds, cornerRadius, cornerRadius);
        } else if (indexPath.row == 0) {
            CGPathMoveToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMaxY(bounds));
            CGPathAddArcToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMinY(bounds), CGRectGetMidX(bounds), CGRectGetMinY(bounds), cornerRadius);
            CGPathAddArcToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMinY(bounds), CGRectGetMaxX(bounds), CGRectGetMidY(bounds), cornerRadius);
            CGPathAddLineToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMaxY(bounds));
            addLine = YES;
        } else if (indexPath.row == [tableView numberOfRowsInSection:indexPath.section]-1) {
            CGPathMoveToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
            CGPathAddArcToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMaxY(bounds), CGRectGetMidX(bounds), CGRectGetMaxY(bounds), cornerRadius);
            CGPathAddArcToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMaxY(bounds), CGRectGetMaxX(bounds), CGRectGetMidY(bounds), cornerRadius);
            CGPathAddLineToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMinY(bounds));
        } else {
            CGPathAddRect(pathRef, nil, bounds);
            addLine = YES;
        }
        layer.path = pathRef;
        CFRelease(pathRef);
        //set the border color
        layer.strokeColor = [UIColor lightGrayColor].CGColor;
        //set the border width
        layer.lineWidth = 1;
        layer.fillColor = [UIColor colorWithWhite:1.f alpha:1.0f].CGColor;


        if (addLine == YES) {
            CALayer *lineLayer = [[CALayer alloc] init];
            CGFloat lineHeight = (1.f / [UIScreen mainScreen].scale);
            lineLayer.frame = CGRectMake(CGRectGetMinX(bounds), bounds.size.height-lineHeight, bounds.size.width, lineHeight);
            lineLayer.backgroundColor = tableView.separatorColor.CGColor;
            [layer addSublayer:lineLayer];
        }

        UIView *testView = [[UIView alloc] initWithFrame:bounds];
        [testView.layer insertSublayer:layer atIndex:0];
        testView.backgroundColor = UIColor.clearColor;
        cell.backgroundView = testView;
    }
}

Также не забудьте установить для свойства разделителя таблицы значение none в построителе интерфейса (по умолчанию это одна строка), если вы создаете таблицу программно, вы должны установить свойство следующим образом:

yourTableView.separatorStyle = UITableViewCellSeparatorStyleNone

Ответ 2

Вот ответ Асада обновлен до скорости 3

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if (cell.responds(to: #selector(getter: UIView.tintColor))) {
        let cornerRadius: CGFloat = 5
        cell.backgroundColor = UIColor.clear
        let layer: CAShapeLayer  = CAShapeLayer()
        let pathRef: CGMutablePath  = CGMutablePath()
        let bounds: CGRect  = cell.bounds.insetBy(dx: 10, dy: 0)
        var addLine: Bool  = false
        if (indexPath.row == 0 && indexPath.row == tableView.numberOfRows(inSection: indexPath.section)-1) {
            pathRef.__addRoundedRect(transform: nil, rect: bounds, cornerWidth: cornerRadius, cornerHeight: cornerRadius)
        } else if (indexPath.row == 0) {
            pathRef.move(to: CGPoint(x:bounds.minX,y:bounds.maxY))
            pathRef.addArc(tangent1End: CGPoint(x:bounds.minX,y:bounds.minY), tangent2End: CGPoint(x:bounds.midX,y:bounds.minY), radius: cornerRadius)

            pathRef.addArc(tangent1End: CGPoint(x:bounds.maxX,y:bounds.minY), tangent2End: CGPoint(x:bounds.maxX,y:bounds.midY), radius: cornerRadius)
            pathRef.addLine(to: CGPoint(x:bounds.maxX,y:bounds.maxY))
            addLine = true;
        } else if (indexPath.row == tableView.numberOfRows(inSection: indexPath.section)-1) {

            pathRef.move(to: CGPoint(x:bounds.minX,y:bounds.minY))
            pathRef.addArc(tangent1End: CGPoint(x:bounds.minX,y:bounds.maxY), tangent2End: CGPoint(x:bounds.midX,y:bounds.maxY), radius: cornerRadius)

            pathRef.addArc(tangent1End: CGPoint(x:bounds.maxX,y:bounds.maxY), tangent2End: CGPoint(x:bounds.maxX,y:bounds.midY), radius: cornerRadius)
            pathRef.addLine(to: CGPoint(x:bounds.maxX,y:bounds.minY))

        } else {
            pathRef.addRect(bounds)
            addLine = true
        }
        layer.path = pathRef
        //CFRelease(pathRef)
        //set the border color
        layer.strokeColor = UIColor.lightGray.cgColor;
        //set the border width
        layer.lineWidth = 1
        layer.fillColor = UIColor(white: 1, alpha: 1.0).cgColor


        if (addLine == true) {
            let lineLayer: CALayer = CALayer()
            let lineHeight: CGFloat  = (1 / UIScreen.main.scale)
            lineLayer.frame = CGRect(x:bounds.minX, y:bounds.size.height-lineHeight, width:bounds.size.width, height:lineHeight)
            lineLayer.backgroundColor = tableView.separatorColor!.cgColor
            layer.addSublayer(lineLayer)
        }

        let testView: UIView = UIView(frame:bounds)
        testView.layer.insertSublayer(layer, at: 0)
        testView.backgroundColor = UIColor.clear
        cell.backgroundView = testView
    }

}

Ответ 3

Вы можете использовать стиль UITableViewStyleGrouped tableview.

Ответ 4

Вот еще один мой подход, который может помочь кому-то. Он рисует границу для каждого раздела с предопределенным цветом и шириной без изменения других свойств ячеек. Это не дает вам округленный раздел (возможно, его можно соответствующим образом изменить), но он дает хорошую степень контроля линий, которые нужно рисовать. Подход также относится к вращению устройства.

typedef enum CellBorderMask{
    CellBorderMaskLeft      = 1 << 0,
    CellBorderMaskRigth     = 1 << 1,
    CellBorderMaskTop       = 1 << 2,
    CellBorderMaskBottom    = 1 << 3
}CellBorderMask;




- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

    CellBorderMask mask;
    if (indexPath.row == 0){
        mask |= CellBorderMaskTop;
    }

    if(indexPath.row == [tableView numberOfRowsInSection:indexPath.section] - 1) {
        mask |= CellBorderMaskBottom;
    }
    mask |= CellBorderMaskRigth | CellBorderMaskLeft;
    [self addBorder:mask forView:cell.contentView];
}

-(void)addBorder:(CellBorderMask)mask forView:(UIView *)view{
    float onePixel = (1.f / [UIScreen mainScreen].scale);
    float lineWidth = 1 * onePixel;
    CGColorRef cgBorderColor = [UIColor redColor].CGColor;


    CALayer *topBorder = [CALayer layer];
    CALayer *bottomBorder = [CALayer layer];
    CALayer *leftBorder = [CALayer layer];
    CALayer *rightBorder = [CALayer layer];

    //tag layers so it possible to find and remove them later 
    topBorder.name = @"Border";
    bottomBorder.name = @"Border";
    leftBorder.name = @"Border";
    rightBorder.name = @"Border";

    //remove previously set border layers so they doesn't produce unwanted effect on orientation change
    [self cleanUpOldBorderLayers:view];

    topBorder.frame = CGRectMake(0.0f, 0.0f, view.bounds.size.width, lineWidth);
    topBorder.backgroundColor = cgBorderColor;

    bottomBorder.frame = CGRectMake(0.0f, view.bounds.size.height - lineWidth, view.bounds.size.width, lineWidth);
    bottomBorder.backgroundColor = cgBorderColor;

    leftBorder.frame = CGRectMake(0.0f, 0.0f, lineWidth, view.bounds.size.height);
    leftBorder.backgroundColor = cgBorderColor;

    rightBorder.frame = CGRectMake(view.bounds.size.width - lineWidth, 0.0f, lineWidth, view.bounds.size.height);
    rightBorder.backgroundColor = cgBorderColor;
    if(mask & CellBorderMaskTop){
        [view.layer addSublayer:topBorder];
    }
    if(mask & CellBorderMaskBottom){
        [view.layer addSublayer:bottomBorder];
    }
    if(mask & CellBorderMaskLeft){
        [view.layer addSublayer:leftBorder];
    }
    if(mask & CellBorderMaskRigth){
        [view.layer addSublayer:rightBorder];
    }
}

-(void)cleanUpOldBorderLayers:(UIView *)view{
    NSMutableArray *layerArray = [NSMutableArray new];
    for (CALayer *layer in view.layer.sublayers) {
        if([@"Border" isEqualToString:layer.name]){
            [layerArray addObject:layer];
        }
    }
    for (CALayer *layer in layerArray) {
        [layer removeFromSuperlayer];
    }
}


-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
    //need to trigger tableView:willDisplayCell: method on orientation change. 
    //Suggest a better method for this if there is one
    [self.tableView reloadData];
}

Я использую метод tableView reloadData для перерисовки ячеек, которые, я считаю, не лучший подход. Проконсультируйтесь с альтернативным методом в комментариях, и я обновлю код.

Чтобы добавить границу в верхний/нижний колонтитул, просто создайте собственное представление в методах делегатов viewForHeaderInSection/viewForFooterInSection и передайте представление методу addBorder:forView выше, прежде чем возвращать его из вашего делегата.

Ответ 5

Вот быстрая версия принятого ответа.

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    if (cell.respondsToSelector("tintColor")) {
        var cornerRadius: CGFloat = 5;
        cell.backgroundColor = UIColor.clearColor()
        var layer: CAShapeLayer  = CAShapeLayer()
        var pathRef: CGMutablePathRef  = CGPathCreateMutable()
        var bounds: CGRect  = CGRectInset(cell.bounds, 10, 0)
        var addLine: Bool  = false
        if (indexPath.row == 0 && indexPath.row == tableView.numberOfRowsInSection(indexPath.section)-1) {
            CGPathAddRoundedRect(pathRef, nil, bounds, cornerRadius, cornerRadius);
        } else if (indexPath.row == 0) {
            CGPathMoveToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMaxY(bounds));
            CGPathAddArcToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMinY(bounds), CGRectGetMidX(bounds), CGRectGetMinY(bounds), cornerRadius);
            CGPathAddArcToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMinY(bounds), CGRectGetMaxX(bounds), CGRectGetMidY(bounds), cornerRadius);
            CGPathAddLineToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMaxY(bounds));
            addLine = true;
        } else if (indexPath.row == tableView.numberOfRowsInSection(indexPath.section)-1) {
            CGPathMoveToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
            CGPathAddArcToPoint(pathRef, nil, CGRectGetMinX(bounds), CGRectGetMaxY(bounds), CGRectGetMidX(bounds), CGRectGetMaxY(bounds), cornerRadius);
            CGPathAddArcToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMaxY(bounds), CGRectGetMaxX(bounds), CGRectGetMidY(bounds), cornerRadius);
            CGPathAddLineToPoint(pathRef, nil, CGRectGetMaxX(bounds), CGRectGetMinY(bounds));
        } else {
            CGPathAddRect(pathRef, nil, bounds);
            addLine = true;
        }
        layer.path = pathRef;
        //CFRelease(pathRef);
        //set the border color
        layer.strokeColor = UIColor.lightGrayColor().CGColor;
        //set the border width
        layer.lineWidth = 1;
        layer.fillColor = UIColor(white: 1, alpha: 1.0).CGColor;


        if (addLine == true) {
            var lineLayer: CALayer = CALayer();
            var lineHeight: CGFloat  = (1 / UIScreen.mainScreen().scale);
            lineLayer.frame = CGRectMake(CGRectGetMinX(bounds), bounds.size.height-lineHeight, bounds.size.width, lineHeight);
            lineLayer.backgroundColor = tableView.separatorColor!.CGColor;
            layer.addSublayer(lineLayer);
        }

        var testView: UIView = UIView(frame:bounds)
        testView.layer.insertSublayer(layer, atIndex: 0)
        testView.backgroundColor = UIColor.clearColor()
        cell.backgroundView = testView
    }

}

Ответ 6

Вот версия ответа Swift3:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let cornerRadius: CGFloat = 0.0
    cell.backgroundColor = UIColor.clear
    let layer: CAShapeLayer = CAShapeLayer()
    let pathRef: CGMutablePath = CGMutablePath()
    let bounds: CGRect = cell.bounds.insetBy(dx: 10, dy: 0)
    var addLine: Bool = false

    if indexPath.row == 0 && indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
        pathRef.__addRoundedRect(transform: nil, rect: bounds, cornerWidth: cornerRadius, cornerHeight: cornerRadius)
    } else if indexPath.row == 0 {
        pathRef.move(to: CGPoint(x: bounds.minX, y: bounds.maxY))
        pathRef.addArc(tangent1End: CGPoint(x: bounds.minX, y: bounds.minY), tangent2End: CGPoint(x: bounds.midX, y: bounds.minY), radius: cornerRadius)
        pathRef.addArc(tangent1End: CGPoint(x: bounds.maxX, y: bounds.minY), tangent2End: CGPoint(x: bounds.maxX, y: bounds.midY), radius: cornerRadius)
        pathRef.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
        addLine = true
    } else if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
        pathRef.move(to: CGPoint(x: bounds.minX, y: bounds.minY))
        pathRef.addArc(tangent1End: CGPoint(x: bounds.minX, y: bounds.maxY), tangent2End: CGPoint(x: bounds.midX, y: bounds.maxY), radius: cornerRadius)
        pathRef.addArc(tangent1End: CGPoint(x: bounds.maxX, y: bounds.maxY), tangent2End: CGPoint(x: bounds.maxX, y: bounds.midY), radius: cornerRadius)
        pathRef.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
    } else {
        pathRef.addRect(bounds)
        addLine = true
    }

    layer.path = pathRef
    layer.strokeColor = UIColor.black.cgColor
    layer.lineWidth = 0.5
    layer.fillColor = UIColor(white: 1, alpha: 1.0).cgColor

    if addLine == true {
        let lineLayer: CALayer = CALayer()
        let lineHeight: CGFloat = (1 / UIScreen.main.scale)
        lineLayer.frame = CGRect(x: bounds.minX, y: bounds.size.height - lineHeight, width: bounds.size.width, height: lineHeight)
        lineLayer.backgroundColor = tableView.separatorColor!.cgColor
        layer.addSublayer(lineLayer)
    }

    let backgroundView: UIView = UIView(frame: bounds)
    backgroundView.layer.insertSublayer(layer, at: 0)
    backgroundView.backgroundColor = UIColor.clear
    cell.backgroundView = backgroundView
}

Ответ 7

Я модифицировал выше размещенный код, теперь этот код создает границу для раздела. Протестировано в swift4.2

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let cornerRadius: CGFloat = 24
        cell.backgroundColor = .clear

        let layer = CAShapeLayer()
        let pathRef = CGMutablePath()
        let bounds = cell.bounds.insetBy(dx: 10, dy: 0)
        var addLine = false

        if indexPath.row == 0 && indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
            pathRef.__addRoundedRect(transform: nil, rect: bounds, cornerWidth: cornerRadius, cornerHeight: cornerRadius)
        } else if indexPath.row == 0 {
            pathRef.move(to: .init(x: bounds.minX, y: bounds.maxY))
            pathRef.addArc(tangent1End: .init(x: bounds.minX, y: bounds.minY), tangent2End: .init(x: bounds.midX, y: bounds.minY), radius: cornerRadius)
            pathRef.addArc(tangent1End: .init(x: bounds.maxX, y: bounds.minY), tangent2End: .init(x: bounds.maxX, y: bounds.midY), radius: cornerRadius)
            pathRef.addLine(to: .init(x: bounds.maxX, y: bounds.maxY))
            addLine = true
        } else if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
            pathRef.move(to: .init(x: bounds.minX, y: bounds.minY))
            pathRef.addArc(tangent1End: .init(x: bounds.minX, y: bounds.maxY), tangent2End: .init(x: bounds.midX, y: bounds.maxY), radius: cornerRadius)
            pathRef.addArc(tangent1End: .init(x: bounds.maxX, y: bounds.maxY), tangent2End: .init(x: bounds.maxX, y: bounds.midY), radius: cornerRadius)
            pathRef.addLine(to: .init(x: bounds.maxX, y: bounds.minY))
        } else {
           //pathRef.addRect(bounds)

            let pathInnerRef = CGMutablePath()
            pathInnerRef.move(to: .init(x: bounds.minX, y: bounds.minY))
            pathInnerRef.addLine(to: .init(x: bounds.minX, y: bounds.maxY))

            pathInnerRef.move(to: .init(x: bounds.maxX, y: bounds.minY))
            pathInnerRef.addLine(to: .init(x: bounds.maxX, y: bounds.maxY))
            pathRef.addPath(pathInnerRef)
            addLine = true
        }

        layer.path = pathRef
        layer.fillColor = UIColor.clear.cgColor
        layer.strokeColor = UIColor.black.cgColor


        if (addLine == true) {
            let lineLayer = CALayer()
            let lineHeight = 1.0 / UIScreen.main.scale
            lineLayer.frame = CGRect(x: bounds.minX + 10, y: bounds.size.height - lineHeight, width: bounds.size.width - 10, height: lineHeight)
            lineLayer.backgroundColor = UIColor.clear.cgColor//tableView.separatorColor?.cgColor
            lineLayer.addBorder(edge: .left, color: UIColor.black, thickness: 2.0)
            lineLayer.addBorder(edge: .right, color: UIColor.black, thickness: 2.0)
            layer.addSublayer(lineLayer)
        }


        let testView = UIView(frame: bounds)
        testView.layer.insertSublayer(layer, at: 0)
        testView.backgroundColor = .clear

        cell.backgroundView = testView   
}