Swiftui - Add Border to One Edge of an Image

SwiftUI - Add Border to One Edge of an Image


Demo

Demo



Implementation

You can use this modifier on any View:

.border(width: 5, edges: [.top, .leading], color: .yellow)

With the help of this simple extension:

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

And here is the magic struct behind this:

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
}
}

Add a border with cornerRadius to an Image in SwiftUI Xcode beta 5


SwiftUI 1.0

Using cornerRadius & overlay Modifiers

Here is another way in which we can use a cornerRadius modifier (which clips the view) and then overlay a stroke with a color.

VStack(spacing: 40) {
Text("Image Border").font(.largeTitle)
Text("Using cornerRadius & overlay").font(.title).foregroundColor(.gray)
Text("Using cornerRadius will also clip the image. Then overlay a border.")
.frame(minWidth: 0, maxWidth: .infinity)
.font(.title)
.padding()
.background(Color.orange)
.foregroundColor(.black)

Image("profile")
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.orange, lineWidth: 4))
.shadow(radius: 10)
}

Result

Sample Image

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

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)
}

SwiftUI - showing an image in a List/Form without a border around the cell

OK... I had some ideas while typing out the question.

I've solved this (at least partly) by providing the listRowInsets on the Section.

Section {
// the content
}
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))

This then displays like...

Sample Image

Now to work on fixing the aspect ratio. /p>

I fixed the aspect ratio using this answer...

How to center crop an image in SwiftUI

Apply leading edge border to repeated view, variable height

Here is a solution. Tested with Xcode 11.4 / iOS 13.4

demo

var body: some View {
HStack(alignment: .top, spacing: 0) {
VStack(alignment: .leading) {
Text(communityName.uppercased())
.font(.custom("Raleway-Regular", size: 10))
.foregroundColor(Color(.sRGB, red: 0.4, green: 0.4, blue: 0.4))
.kerning(1.2)
.padding(.top, 20)
Text(name)
.font(.custom("DMSerifDisplay-Regular", size: 20))
.foregroundColor(Color(.sRGB, red: 0.2, green: 0.2, blue: 0.2))
.lineLimit(2)
.fixedSize(horizontal: false, vertical: true)
}
.padding(.leading, 10.0)
Spacer()
}
.overlay(
Rectangle().fill(accentColor).frame(width: 2.0), // << here !!
alignment: .leading)
.padding(7.0)
.background(Color(.sRGB, red: 0.969, green: 0.969, blue: 0.969))

}

SwiftUI view with rounded corners AND border

SwiftUI borders have straight edges no matter what corner radius you apply (.cornerRadius simply clips the view to a rounded mask and doesn't adjust the border's appearance).

If you want a rounded border, you'll need to overlay and .stroke a rounded rectangle:

VStack {
Text("some text")
Button("Ok") {}
.foregroundColor(.cyan)
.padding()
}
.padding()
.background(.red)
.cornerRadius(20) /// make the background rounded
.overlay( /// apply a rounded border
RoundedRectangle(cornerRadius: 20)
.stroke(.blue, lineWidth: 5)
)

Result:

Rounded blue border

Border for Specific Sides SwiftUI

The solution in this case is to use explicitly specified edges to which padding is applied

Rectangle()
.overlay(Rectangle()
.fill(Color.white).padding([.leading, .trailing], 2)) // << here !!
.foregroundColor(Color.red)

How to add a border just on the top side of a UIView

I consider subclassing UIView and overriding drawRect overkill here. Why not add an extension on UIView and add border subviews?

@discardableResult
func addBorders(edges: UIRectEdge,
color: UIColor,
inset: CGFloat = 0.0,
thickness: CGFloat = 1.0) -> [UIView] {

var borders = [UIView]()

@discardableResult
func addBorder(formats: String...) -> UIView {
let border = UIView(frame: .zero)
border.backgroundColor = color
border.translatesAutoresizingMaskIntoConstraints = false
addSubview(border)
addConstraints(formats.flatMap {
NSLayoutConstraint.constraints(withVisualFormat: $0,
options: [],
metrics: ["inset": inset, "thickness": thickness],
views: ["border": border]) })
borders.append(border)
return border
}


if edges.contains(.top) || edges.contains(.all) {
addBorder(formats: "V:|-0-[border(==thickness)]", "H:|-inset-[border]-inset-|")
}

if edges.contains(.bottom) || edges.contains(.all) {
addBorder(formats: "V:[border(==thickness)]-0-|", "H:|-inset-[border]-inset-|")
}

if edges.contains(.left) || edges.contains(.all) {
addBorder(formats: "V:|-inset-[border]-inset-|", "H:|-0-[border(==thickness)]")
}

if edges.contains(.right) || edges.contains(.all) {
addBorder(formats: "V:|-inset-[border]-inset-|", "H:[border(==thickness)]-0-|")
}

return borders
}

// Usage:
view.addBorder(edges: [.all]) // All with default arguments
view.addBorder(edges: [.top], color: .green) // Just Top, green, default thickness
view.addBorder(edges: [.left, .right, .bottom], color: .red, thickness: 3) // All except Top, red, thickness 3

With this code you're not tied to your subclass too, you can apply it to anything and everything that inherits from UIView - reusable in your project, and any others. Pass in other arguments to your methods to define other colours and widths. Many options.



Related Topics



Leave a reply



Submit