Kenny Kurochkin
Kenny Kurochkin

Reputation: 1108

How to create spacing between items in a SwiftUI List view?

I can't seem to find a way to create spacing between List items. Maybe I shouldn't put in a list in the first place?

enter image description here

What do you think?

This the code that generates the view:

struct ContentView: View {
    
    @ObservedObject var ratesVM = RatesViewModel()
    
    init() {
        UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor: UIColor.white]
        UITableView.appearance().backgroundColor = UIColor.init(named: "midnight blue")
        UITableViewCell.appearance().backgroundColor = .green
        UITableView.appearance().separatorStyle = .none
    }
    
    var body: some View {
        
        NavigationView {
            List (ratesVM.ratesList, id: \.self) { rate in
                    Image(systemName: "plus")
                    
                    Button(action: {print("pressed")}) {
                        Text(rate.hebrewCurrencyName)
                }
            }
            .navigationBarTitle("המרות מטבע")
            .navigationBarItems(leading:
                Button(action: {
                    print("button pressed")
                    self.ratesVM.callService()
                }) {
                    Image(systemName: "plus")
                        .foregroundColor(Color.orange)})
        }.environment(\.layoutDirection, .rightToLeft)
    }
}

Upvotes: 25

Views: 45257

Answers (8)

David Alvarado
David Alvarado

Reputation: 11

** For Swift 5 **

In my case, only need to add, two properties to my code listRowSeparator() and listRowSpacing(). The first one was used inside of ForEach and the second one was used at the end of List View.

var content: some View {
    List {
        Section {
            ForEach (itemList) { item in
                ScheduledTransfer365Row(item: item)
                    .listRowSeparator(.hidden)
            }
        }
    }
    .listRowSpacing(2)
}

See preview

Upvotes: 1

Sam Doggett
Sam Doggett

Reputation: 530

In my case I needed to add the following to my Section:

List {
    Section {
        ForEach(nearbyLocations, id: \.id) { location in
            LocationCard(location: location)
        }
    }
    .listRowSeparator(.hidden)
}
.listStyle(.plain)
.listRowBackground(Color.clear)
.listRowSpacing(10)

To change the spacing of the cards, update the listRowSpacing as needed.

Posting this here incase someone needs a similar result.

enter image description here

Upvotes: 11

Hezy Ziv
Hezy Ziv

Reputation: 5550

Working example

struct AuthView_Previews: PreviewProvider {
static var previews: some View {
    let t = ["1","2","3","4","5","6","7","8","9"]
    GeometryReader { geometry in
        List(t, id: \.self){ item in
                HStack{
                    Text(item)
                }
                .frame(width: geometry.size.width - 40, height: 39, alignment: .center)
                .padding(10)
                .background(Color("AppRecommended"))
                .cornerRadius(10)
                .listRowSeparator(.hidden)
        }
        .listStyle(.plain)
    }
}

}

Note: .listRowSeparator(.hidden) works for iOS 15 and above

Upvotes: 0

kafran
kafran

Reputation: 834

The SwiftUI way

You can achieve it by removing the list row separators .listRowSeparator(.hidden) and setting the list row background to an InsettableShape .listRowBackground() defining top and bottom EdgeInsets padding. Btw, don't forget to set the .listStyle(.plain) to .plain.

You can find a better implementation on this dev post SwiftUI List Card View.

//
//  ContentView.swift
//  My App
//
//  Created by Kolmar Kafran on 25/08/22.
//  https://www.linkedin.com/in/kolmar-kafran/
//

import SwiftUI

struct ContentView: View {
    @State private var someList = [0, 1, 2, 3, 4]
    
    var body: some View {
        List {
            ForEach(someList, id: \.self) { n in
                Text("\(n)")
                    .foregroundColor(.white)
                    .listRowBackground(
                        RoundedRectangle(cornerRadius: 5)
                            .background(.clear)
                            .foregroundColor(.blue)
                            .padding(
                                EdgeInsets(
                                    top: 2,
                                    leading: 10,
                                    bottom: 2,
                                    trailing: 10
                                )
                            )
                    )
                    .listRowSeparator(.hidden)
            }
            .onDelete { idx in
                someList.remove(atOffsets: idx)
            }
        }
        .listStyle(.plain)
    }
}

Upvotes: 12

Paulo Mattos
Paulo Mattos

Reputation: 19339

A quick solution here is to just set the corresponding listStyle to SidebarListStyle. For instance:

List {
    Text("First row")
    Text("Second row")
    Text("Third row")
}
.listStyle(SidebarListStyle())

But please note that on macOS and iOS, the sidebar list style also displays disclosure indicators in the section headers that allow the user to collapse and expand sections.

Another quick alternative would be to use the Divider() helper view instead (i.e., a visual element that can be used to separate other content):

List {
    Text("First row")
    Divider()
    Text("Second row")
    Divider()
    Text("Third row")
}

Both solutions won't allow direct control of the vertical spacing but at least provides a more spacious layout alternative.

Upvotes: 0

Kenny Kurochkin
Kenny Kurochkin

Reputation: 1108

padding didn't work since it only increased the size of the item/cell and not the spacing between, but .environment(.defaultMinListRowHeight, 20) seemed to work

I also implemented a custom view for the button styling to adjust the frame and "pressable area" of the button relative to the item/cell.

struct MyButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .foregroundColor(.black)
            .opacity(configuration.isPressed ? 0.5 : 1.0)
            .frame(width: 350, height: 50, alignment: Alignment.center)
            .background(Color.orange)
    }
}

struct ContentView: View {

    @ObservedObject var ratesVM = RatesViewModel()

    init() {
        UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor: UIColor.white]
        UITableView.appearance().backgroundColor = UIColor.init(named: "midnight blue")
        UITableViewCell.appearance().backgroundColor = .clear
        UITableView.appearance().separatorStyle = .none
    }

    var body: some View {

        NavigationView {
            List (ratesVM.ratesList, id: \.self) { rate in
                Button(action: {print("pressed")}) {
                    Text(rate.hebrewCurrencyName)
                }.buttonStyle(MyButtonStyle())
            }
            .navigationBarTitle("המרות מטבע")
            .navigationBarItems(leading:
                Button(action: {
                    print("button pressed")
                    self.ratesVM.callService()
                }) {
                    Image(systemName: "plus")
                        .foregroundColor(Color.orange)})
        }.environment(\.layoutDirection, .rightToLeft)
            .environment(\.defaultMinListRowHeight, 150)
    }
}

enter image description here

Upvotes: 2

Pau Senabre
Pau Senabre

Reputation: 4209

You can define the minimum list row height to be bigger so you'll have more separation between rows.

List (ratesVM.ratesList, id: \.self) { rate in
       Image(systemName: "plus")
       Button(action: {print("pressed")}) {
                    Text(rate.hebrewCurrencyName)
       }
}.environment(\.defaultMinListRowHeight, 50) //minimum row height

Alternatively you can build your row as a HStack and specify a frame height.

List (ratesVM.ratesList, id: \.self) { rate in
    HStack {
       Image(systemName: "plus")
       Button(action: {print("pressed")}) {
          Text(rate.hebrewCurrencyName)
       }
    }.frame(height: 50) //your row height 
}.environment(\.defaultMinListRowHeight, 20) 

Or as a VStack and and use Spacers

List (ratesVM.ratesList, id: \.self) { rate in
    VStack{
        Spacer()
        HStack {
           Image(systemName: "plus")
           Button(action: {print("pressed")}) {
              Text(rate.hebrewCurrencyName)
           }
        }
        Spacer()
    }.frame(height: 50)
}.environment(\.defaultMinListRowHeight, 20) 

Upvotes: 17

Jawad Ali
Jawad Ali

Reputation: 14397

SwiftUI lets us set individual padding around views using the padding() modifier. If you use this with no parameters you’ll get system-default padding on all sides, like this:

VStack {
    Text("SwiftUI")
        .padding()
    Text("rocks")
}

But you can also customize how much padding to apply and where. So, you might want to apply system padding to only one side:

Text("SwiftUI")
    .padding(.bottom)

Or you might want to control how much padding is applied to all sides:

Text("SwiftUI")
    .padding(100)

Or you can combine the two to add a specific amount of padding to one side of the view:

Text("SwiftUI")
    .padding(.bottom, 100)

so you can do

Text(rate.hebrewCurrencyName)
      .padding(50)

Upvotes: 1

Related Questions