iosswiftuiforeachimage-rotation

How to display vertically an array of turned images in SwiftUI?


My app has an array hand of a struct with a property let blatt: Image. During init, an Image is assigned to this property.

To display such an array horizontally, I use the following code (scaledWidth is some constant):

HStack(spacing: 10) {
    ForEach((0 ..< hand.count), id: \.self) {
        hand[$0].blatt
            .resizable()
            .scaledToFit() 
            .frame(height: scaledWidth)
            .border(.black)
    }
}

This gives the following output:

enter image description here

This is what I expected.

Another array is to be displayed vertically. For this, I am using the following code:

VStack(spacing: 10) {
    ForEach((0 ..< hand.count), id: \.self) {
        hand[$0].blatt
            .resizable()
            .scaledToFit() 
            .rotationEffect(Angle(degrees: 90))
            .frame(width: scaledWidth, height: scaledWidth)
            .border(.black)
    }
}

This gives additionally the following output:

enter image description here

Here, the height of each frame, as indicated by the bordered frame, is the height of the unrotated image.

Since I know the aspect ratio aspectRatioBlatt of the images, I can adjust the output using .frame(width: scaledWidth, height: scaledWidth / aspectRatioBlatt). Then, the frames have the correct dimensions and the correct spacing, but the images inside are too little. Again, the height of the frame is then equal to the height of the inner unrotated image.

I have tried many view modifiers and multiple combinations, but failed. I even tried to use a custom layout as suggested here for vertical Text, but this did not work either.
Of course I could use a 2nd set of turned images, but it should be possible without it in SwiftUI.

How to do it right?


Solution

  • It might be simplest to rotate the HStack instead.

    Otherwise, if you know the aspect ratio then you can set the frame size before rotation and then adjust the height after rotation:

    struct ContentView: View {
    
        let width = CGFloat(50)
        let aspectRatio = 31.0 / 15.0
        let colors: [Color] = [.green, .blue, .mint, .teal, .purple]
    
        var body: some View {
            VStack(spacing: 50) {
                HStack(spacing: 10) {
                    ForEach((0 ..< colors.count), id: \.self) { index in
                        Image(systemName: "figure.wave")
                            .resizable()
                            .scaledToFit()
                            .frame(width: width, height: width * aspectRatio)
                            .background { colors[index] }
                            .border(.black)
                    }
                }
                VStack(spacing: 10) {
                    ForEach((0 ..< colors.count), id: \.self) { index in
                        Image(systemName: "figure.wave")
                            .resizable()
                            .scaledToFit()
                            .frame(width: width, height: width * aspectRatio)
                            .background { colors[index] }
                            .border(.black)
                            .rotationEffect(.degrees(90))
                            .frame(height: width)
                    }
                }
            }
        }
    }
    

    RotatedImages

    I found it also worked by setting only the width and then using scaledToFill:

    struct ContentView: View {
    
        let width = CGFloat(50)
        let colors: [Color] = [.green, .blue, .mint, .teal, .purple]
    
        var body: some View {
            VStack(spacing: 50) {
                HStack(spacing: 10) {
                    ForEach((0 ..< colors.count), id: \.self) { index in
                        Image(systemName: "figure.wave")
                            .resizable()
                            .scaledToFit()
                            .frame(width: width)
                            .background { colors[index] }
                            .border(.black)
                    }
                }
                VStack(spacing: 10) {
                    ForEach((0 ..< colors.count), id: \.self) { index in
                        Image(systemName: "figure.wave")
                            .resizable()
                            .scaledToFill()
                            .frame(width: width)
                            .background { colors[index] }
                            .border(.black)
                            .rotationEffect(.degrees(90))
                            .frame(height: width)
                    }
                }
            }
        }
    }