3

I want to transform a web app into an iOS app and I am struggling with alignments and the GeometryReader().

This a a screenshot from my original web app template: enter image description here

This is my current version in SwiftUI: enter image description here With the related code:

struct PileRow: View {
    var body: some View {

        HStack() {
            VStack(alignment: .leading, spacing: 3) {
                Text("Adobe".uppercased())
                Bar(backgroundColor: Color.gray, width: 200)
                Bar(backgroundColor: Color.black, width: 200)
                HStack(alignment: .top, spacing: 3) {
                    Text("EUR 1,00")
                        .fontWeight(.light)
                    Text(" / ")
                        .fontWeight(.light)
                        .foregroundColor(Color.gray)
                    Text("35,69")
                        .fontWeight(.light)
                        .foregroundColor(Color.gray)
                }
                Image("dots")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 20)
            }
            .font(.system(size: 14))
            .padding()
        }
        .background(Color.white)
        .cornerRadius(15)
        .shadow(radius: 6, x: 5, y: 5)
    }
}

struct Bar: View {

    var backgroundColor: Color
    var width: CGFloat

    var body: some View {
        Rectangle()
            .fill(backgroundColor)
            .frame(width: width, height: 3)
            .cornerRadius(1.5)
    }
}

I want:

  • the dots image to be centered in the parent VStack (like margin: 0 auto; in CSS)
  • the outer HStack to be 100% width with a left and right margin of 10px (like width: 100%; margin: auto 10px; in CSS)

The special challenge I want to realize is, to give the second Bar() view a percentage with (or some equivalent) to have a with of 1.00/35.69*100%.

In the apple documentation of Swift UI I found:

GeometryReader A container view that defines its content as a function of its own size and coordinate space. 3

With a change from:

Bar(backgroundColor: Color.gray, width: 200)
Bar(backgroundColor: Color.black, width: 200)

to

GeometryReader { g in
    Bar(backgroundColor: Color.gray, width: g.size.width)
}
.frame(height: 3)
GeometryReader { g in
    Bar(backgroundColor: Color.black, width: g.size.width*(1/35.69))
}
.frame(height: 3)

it is going close to what I look for:

enter image description here

I don't understand what is happening here. My purpose was to give the Bar views a size relationship to the parent. But the parent itself gets a different width and hight that I need to fix with a frame with a height of 3 and the parent HStack fills the entire screen.

When I try this:

GeometryReader { g in
    Bar(backgroundColor: Color.gray, width: g.size.width)
    Bar(backgroundColor: Color.black, width: g.size.width*(1/35.69))
}

I am completely out, because the bars get somehow vstacked:

enter image description here

T. Karter
  • 428
  • 2
  • 16

1 Answers1

1

I think this is what you want:

Example

import SwiftUI

struct ContentView: View {
    var body: some View {

        GeometryReader { g in
            HStack {
                VStack(alignment: .leading, spacing: 3) {
                    Text("Adobe".uppercased())
                    Bar(backgroundColor: Color.gray, width: g.size.width)
                    Bar(backgroundColor: Color.black, width: g.size.width*(1/35.69))
                    HStack(alignment: .top, spacing: 3) {
                        Text("EUR 1,00")
                            .fontWeight(.light)
                        Text(" / ")
                            .fontWeight(.light)
                            .foregroundColor(Color.gray)
                        Text("35,69")
                            .fontWeight(.light)
                            .foregroundColor(Color.gray)
                    }
                    HStack {
                        Spacer()
                        Image(systemName: "dots")
                            .resizable()
                            .scaledToFit()
                            .frame(width: 20)
                        Spacer()
                    }
                }
                .font(.system(size: 14))
                .padding()
            }
            .background(Color.white)
            .cornerRadius(15)
            .shadow(radius: 6, x: 5, y: 5)
        }
        .padding()
    }
}

struct Bar: View {

    var backgroundColor: Color
    var width: CGFloat

    var body: some View {
        Rectangle()
            .fill(backgroundColor)
            .frame(width: width, height: 3)
            .cornerRadius(1.5)
    }
}
Kuhlemann
  • 2,090
  • 3
  • 8
  • 25
  • Thank you for your answer. This is generating the correct margin (with the padding on the GeometryView) but the Image("dots") is not centered within the VStack and when I create your ContentView() as a child of a List() view the items are overlapping – T. Karter May 15 '20 at 15:07
  • 1
    See my updated answer which centers the "dots" correctly now. For your additional question I can't help in the comments and don't know the code you use, but my answer answers all you have asked for in your original question. Maybe ask the new question in a separate question and mark this one as solved. – Kuhlemann May 15 '20 at 15:53
  • The answer is so easy - I love it! Thank you! Everything is resolved. – T. Karter May 15 '20 at 16:03
  • No problem! Just vote the answer up, if it helped you. :-) – Kuhlemann May 15 '20 at 16:10