Search code examples
swiftuiswiftui-list

SwiftUI: Spacing between List Items


I have some code that displays a list with a title and a rectangle and if the user taps on it, it navigates to a detail page.

But my code seems to have multiple problems:

  1. The spacer is not working. I want the name on the left and the rectangle on the right like a spacer in a Stack would push it.
  2. The background color of the rectangle is not applied. It is just black
  3. If I add a navigationTitle, I see constraint errors (see below)

( "<NSLayoutConstraint:0x600002438f50 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7fd15fe088b0]-(6)-[_UIModernBarButton:0x7fd15fe064d0'World Clock'] (active)>", "<NSLayoutConstraint:0x600002438fa0 'CB_Trailing_Trailing' _UIModernBarButton:0x7fd15fe064d0'World Clock'.trailing <= _UIButtonBarButton:0x7fd15fe05310.trailing (active)>", "<NSLayoutConstraint:0x600002439d10 'UINav_static_button_horiz_position' _UIModernBarButton:0x7fd15fe088b0.leading == UILayoutGuide:0x600003e04a80'UIViewLayoutMarginsGuide'.leading (active)>", "<NSLayoutConstraint:0x600002439d60 'UINavItemContentGuide-leading' H:[_UIButtonBarButton:0x7fd15fe05310]-(0)-[UILayoutGuide:0x600003e049a0'UINavigationBarItemContentLayoutGuide'] (active)>", "<NSLayoutConstraint:0x6000024350e0 'UINavItemContentGuide-trailing' UILayoutGuide:0x600003e049a0'UINavigationBarItemContentLayoutGuide'.trailing == _UINavigationBarContentView:0x7fd15fd0a640.trailing (active)>", "<NSLayoutConstraint:0x60000243a4e0 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x7fd15fd0a640.width == 0 (active)>", "<NSLayoutConstraint:0x6000024354a0 'UIView-leftMargin-guide-constraint' H:|-(0)-UILayoutGuide:0x600003e04a80'UIViewLayoutMarginsGuide' (active, names: '|':_UINavigationBarContentView:0x7fd15fd0a640 )>" )

enter image description here

Here is my code:

    var body: some View {
    NavigationView {
        
        List {
            ForEach(viewModel.cityList, id: \.name) { city in
                NavigationLink(
                    destination: CityDetailView(city: city)){
                    HStack{
                        Text(city.name)
                        Spacer()
                        CustomView()
                    }
                }.frame(height: 100)
            }
        }
        .navigationTitle("World Clock")
    }
}

    CustomView:
        var body: some View {
            GeometryReader { geo in
                Circle().frame(width: geo.size.height, height: geo.size.height)
                .background(Color.green)
            }
        }

Any idea what is wrong?


Solution

  • Your spacer doesn't work because there's NavigationLink in the HStack which took all the space: it has bigger priority than Spacer.

    If you wanna use NavigationLink with EmptyView, it should be placed in a ZStack with nearby view, but in this case you can simply place content of your cell inside NavigationLink label.

    Rectangle is not a view but a Shape, and as with any other Shape its color should be set with foregroundColor, not with background.

    GeometryReader always takes all available space, unless you're adding size modifiers to it. It cannot wrap around content by itself. So you can use following trick: add onAppear for an item inside GeometryReader and pass width to state variable, which will then add width modifier to GeometryReader.

    struct ContentView: View {
        @ObservedObject var viewModel = CityListViewModel()
    
        
        var body: some View {
            NavigationView {
                List {
                    ForEach(viewModel.cityList, id: \.name) { city in
                        NavigationLink(
                            destination: Text(city.name)){
                            HStack{
                                Text(city.name)
                                Spacer()
                                CustomView()
                            }
                        }.frame(height: 100)
                    }
                }
                .navigationTitle("World Clock")
            }
        }
    }
    
    struct CustomView: View {
        @State
        var width: CGFloat?
        
        var body: some View {
            GeometryReader { geo in
                Circle().frame(width: geo.size.height, height: geo.size.height)
                    .background(Color.green)
                    .onAppear {
                        width = geo.size.height
                    }
            }.frame(width: width)
        }
    }
    

    If you see some NSLayoutConstraint broken which you didn't create, like when using SwiftUI, you can ignore it: it's not your bug.