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

5

44

how can I add a border with a cornerRadius to an Image. I get a deprecation warning saying that i should use a RoundedRectange Shape, but i don't know how to use that exactly

Beta 4:

Image(uiImage: ...)
   .border(Color.black, width: 2, cornerRadius: 10)
Holna answered 30/7, 2019 at 10:31 Comment(0)
N
88

SwiftUI 5.0

Using clipShape & overlay Modifiers

Here is another way in which we can use a clipShape 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(maxWidth: .infinity)
        .font(.title)
        .padding()
        .background(Color.orange)
        .foregroundColor(.black)
    
    Image("profile")
        .clipShape(.rect(cornerRadius: 10))
        .overlay(.rect(cornerRadius: 10)
            .stroke(Color.orange, lineWidth: 4))
        .shadow(radius: 10)
}

Result

screenshot of clipshape

Nonpartisan answered 13/9, 2019 at 7:15 Comment(3)
What does the layoutPriority(1)?Holna
It gives priority to assigning available space over other views within the same parent. (By default, the rest of the child views have a layoutPriority of zero.) Without this, that text view was only showing one line of text and getting truncated.Nonpartisan
I can confirm this still works perfectly well with SwiftUI 2.0Elastance
H
31

Consider this: adding a modifier to a view will return a new View instance that wraps the previous instance. This is also why the order in which you add modifiers matters.

We can use this to our advantage: by adding a padding, then adding a background to our new View, we can create our own additional layers:

Image("cat")
    .cornerRadius(7) // Inner corner radius
    .padding(5) // Width of the border
    .background(Color.primary) // Color of the border
    .cornerRadius(10) // Outer corner radius

Results in:

enter image description here

You can even turn this in a ViewModifier to be reusable more easily:

struct RoundedEdge: ViewModifier {
    let width: CGFloat
    let color: Color
    let cornerRadius: CGFloat

    func body(content: Content) -> some View {
        content.cornerRadius(cornerRadius - width)
            .padding(width)
            .background(color)
            .cornerRadius(cornerRadius)
    }
}

Using it would become:

Image("cat").modifier(RoundedEdge(width: 5, color: .black, cornerRadius: 20))

This workd for any SwiftUI View, like Text:

Text("Some text")
    .padding(15)
    .background(Color.red)
    .modifier(RoundedEdge(width: 5, color: .black, cornerRadius: 20))

Results in:

enter image description here

Havelock answered 29/9, 2019 at 15:41 Comment(1)
Compared to the accepted answer, this one uses .padding to avoid clipping of Image edges. Keep that in mind when deciding whether to add .padding depending on what you want to achieve visually.Smart
M
16

First, note that the way you were doing it, was not clipping the image. Maybe you did not noticed if the image was too small, or if it had a background of the same color of your canvas. But even while using your beta 4 syntax, you needed to add .clipShape().


Back to your question, according to Beta 5 release notes:

Complex overloads for the background(:alignment:) and border(:width:) modifiers are deprecated. Use shapes in a background(:alignment:) or overlay(:alignment:) to draw these instead. (53067530)

So the pattern would be something like this:

.overlay(RoundedRectangle(...).stroke(...).foregroundColor(...))

In your specific case:

Image("mypic").resizable().frame(width: 300, height: 300)
    .clipShape(RoundedRectangle(cornerRadius: 30))
    .overlay(RoundedRectangle(cornerRadius: 30).stroke(lineWidth: 2).foregroundColor(Color.black))
Mould answered 30/7, 2019 at 11:37 Comment(2)
Thanks for considering clipping. Still having problems with this, but then I'm using a hosting controller from a UIViewController. I had to do the corner rounding and clipping from the UIViewController.Excipient
I think .clipShape(RoundedRectangle(cornerRadius: 30)) is better than corner radiusWyler
C
9

Write a rounded text view which is same with Image View

struct RoundedTextView: View {
    var body: some View {
        Text("Rounded Text View")
            .frame(width: 200, height: 200, alignment: .center)
            .foregroundColor(.white)
            .background(Color.blue)
            .cornerRadius(16)
            .overlay(
                RoundedRectangle(cornerRadius: 16).stroke(Color.yellow, lineWidth: 8)
            )
    }
}

Preview like this image:

enter image description here

Calcar answered 19/4, 2020 at 18:7 Comment(0)
C
1

I really liked kontiki's answer but not the length so I wrote:

import SwiftUI

func strokedRoundedRectangle(
        cornerRadius r: CGFloat,
        lineWidth w: CGFloat = 1,
        color c: Color = .primary
    ) -> some View {

    return RoundedRectangle(cornerRadius: r).stroke(lineWidth: w).foregroundColor(c)
}
Chrysoprase answered 30/7, 2019 at 13:20 Comment(1)
cornerRadius is also deprecated for background(). You can use the same RoundedRectangle expression but without the stroke()Chrysoprase

© 2022 - 2024 — McMap. All rights reserved.