SwiftUI - Add Border to One Edge of an Image
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
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
Add one more param
cornerRadius
when init the shapeRework 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...
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
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:
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
How to Bring Nswindow to Front and to the Current Space
How to Convert Anyclass to a Specific Class and Init It Dynamically in Swift
How to Change Navigationbar Font in Swift
Repeating Animation on Swiftui Image
How to Shuffle an Array So That No Two Consecutive Values Are the Same
Swift Protocol Implements Equatable
How to Get Indexpath.Row in Cell.Swift
Swift Add Badge to Navigation Barbuttonitem and Uibutton
Convert Date to Integer in Swift
Uitableview Scrolls to Top on Reload
Swift: How to Get Everything After a Certain Set of Characters
Singleton and Init with Parameter
Get "Does Not Implement Methodsignatureforselector" When Try to Store Array in Nsuserdefaults,Swift
How to Detect and Make Clickable Links in a Uilabel Not Using Uitextview