Clip image to square in SwiftUI

后端 未结 2 837
夕颜
夕颜 2021-02-20 07:57

I am trying to put multiple cells next to each other where each cell consists of an image and a text below. The cell itself should be a square and the image should be scaled to

相关标签:
2条回答
  • 2021-02-20 08:33

    It works for me, but I don't know why cornerRadius is necessary...

    import SwiftUI
    
    struct ClippedImage: View {
        let imageName: String
        let width: CGFloat
        let height: CGFloat
    
        init(_ imageName: String, width: CGFloat, height: CGFloat) {
            self.imageName = imageName
            self.width = width
            self.height = height
        }
        var body: some View {
            ZStack {
                Image(imageName)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: width, height: height)
            }
            .cornerRadius(0) // Necessary for working
            .frame(width: width, height: height)
        }
    }
    
    struct ClippedImage_Previews: PreviewProvider {
        static var previews: some View {
            ClippedImage("dishLarge1", width: 100, height: 100)
        }
    }
    

    0 讨论(0)
  • 2021-02-20 08:56

    A ZStack will help solve this by allowing us to layer views without one effecting the layout of the other.

    For the text:

    .frame(minWidth: 0, maxWidth: .infinity) to expand the text horizontally to its parent's size

    .frame(minHeight: 0, maxHeight: .infinity) is useful in other situations

    As for the image:

    .aspectRatio(contentMode: .fill) to make the image maintain its aspect ratio rather than squashing to the size of its frame.

    .layoutPriority(-1) to de-prioritize laying out the image to prevent it from expanding its parent (the ZStack within the ForEach in our case).

    The value for layoutPriority just needs to be lower than the parent views which will be set to 0 by default. We have to do this because SwiftUI will layout a child before its parent, and the parent has to deal with the child size unless we manually prioritize differently.

    The .clipped() modifier uses the bounding frame to mask the view so you'll need to set it to clip any images that aren't already 1:1 aspect ratio.

        var body: some View {
            HStack {
                ForEach(0..<3, id: \.self) { index in
                    ZStack {
                        Image(systemName: "doc.plaintext")
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .layoutPriority(-1)
                        VStack {
                            Spacer()
                            Text("yes")
                                .frame(minWidth: 0, maxWidth: .infinity)
                                .background(Color.white)
                        }
                    }
                    .clipped()
                    .aspectRatio(1, contentMode: .fit)
                    .border(Color.red)
                }
            }
        }
    

    Edit: While geometry readers are super useful I think they should be avoided whenever possible. It's cleaner to let SwiftUI do the work. This is my initial solution with a Geometry Reader that works just as well.

            HStack {
                ForEach(0..<3, id: \.self) { index in
                    ZStack {
                        GeometryReader { proxy in
                            Image(systemName: "pencil")
                                .resizable()
                                .scaledToFill()
                                .frame(width: proxy.size.width)
                            VStack {
                                Spacer()
                                Text("yes")
                                    .frame(width: proxy.size.width)
                                    .background(Color.white)
                            }
                        }
                    }
                    .clipped()
                    .aspectRatio(1, contentMode: .fit)
                    .border(Color.red)
                }
            }
    
    
    0 讨论(0)
提交回复
热议问题