nickcoding
nickcoding

Reputation: 475

Changing function with borders on specific sides to also be able to accommodate corner radius?

So I have the following struct and view extension to be able to add to any view, any ideas how to fix the corner of the view in the image below?

extension View {
    func border(width: CGFloat, edges: [Edge], color: Color) -> some View {
        overlay(EdgeBorder(width: width, edges: edges).foregroundColor(color))
    }
}

struct EdgeBorder: Shape {

    var width: CGFloat
    var edges: [Edge]

    func path(in rect: CGRect) -> Path {
        var path = Path()
        for edge in edges {
            var x: CGFloat {
                switch edge {
                case .top, .bottom, .leading: return rect.minX
                case .trailing: return rect.maxX - width
                }
            }

            var y: CGFloat {
                switch edge {
                case .top, .leading, .trailing: return rect.minY
                case .bottom: return rect.maxY - width
                }
            }

            var w: CGFloat {
                switch edge {
                case .top, .bottom: return rect.width
                case .leading, .trailing: return self.width
                }
            }

            var h: CGFloat {
                switch edge {
                case .top, .bottom: return self.width
                case .leading, .trailing: return rect.height
                }
            }
            path.addPath(Path(CGRect(x: x, y: y, width: w, height: h)))
        }
        return path
    }
}

I tried attaching it to a VStack as follows:

VStack {
    // content
}
.clipShape(RoundedRectangle(cornerRadius: 20))
.border(width: 1, edges: [.leading, .bottom, .trailing], color: Color.black)

The only problem is that it ends up looking like this:

As you can see, the bottom right corner edge doesn't conform to the clipped shape

Upvotes: 4

Views: 591

Answers (3)

Quang Hà
Quang Hà

Reputation: 4746

The problem is from your EdgeBorder shape. It ONLY support borders without corners, it just adds one line for each edge to the final Path. To resolve please try

  1. Add one more param cornerRadius when init the shape

  2. Rework the algorithm to create Path

    c - c
    |   |
    c - c
    

Enable a corner only when 2 side exist.

    var width: CGFloat
    var edges: [Edge]
    var cornerRadius: CGFloat

    func path(in rect: CGRect) -> Path {
        //1. build new rect
        //2. build corners
        let path = UIBezierPath(roundedRect: newRect, byRoundingCorners: corners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
        return Path(path.cgPath)
    }

Upvotes: 2

wonder
wonder

Reputation: 186

I round the borders like this with the view

                layerMinY
             O ----------- O
  layerMinX  |             | layerMaxX
             |             |
             O ----------- O
               layerMaxY

    self.myView.layer.borderWidth = 2
    self.myView.layer.borderColor = UIColor.black.cgColor
    self.myView.clipsToBounds = true
    self.myView.layer.cornerRadius = 10
    self.myView.layer.maskedCorners = [.layerMaxXMinYCorner, 
                                      .layerMaxXMaxYCorner]

Looks like this:

enter image description here

Upvotes: 3

a43nigam
a43nigam

Reputation: 188

As the comment said, clipping doesn't change the drawing path of the initial rectangle. Changing the border width to 0 should remove the outline rectangle.

Upvotes: 2

Related Questions