Removing White Background from SwiftUI Context Menus
I’m a stickler for good design, especially when it comes to user interfaces. A common pitfall is focusing on how the UI looks "at a glance"—when everything seems fine on the surface but falls short during actual user interaction.
This approach works well in early stages, ensuring everything looks in place for the average user. But we need to remember there are many more user groups beyond the "standard"—including those with accessibility needs like low vision, hearing impairments, or motion sensitivity. While this article isn’t specifically about accessibility, it’s crucial to recognise that thoughtful design extends beyond initial appearances.
One area where this attention to detail often slips is with the white space that appears around a cell in a List
when moving it or a button in a .contextMenu
. It feels like we put all our effort into the initial view, but overlook the finer details when users interact with the app.
Identifying the White Space Issue
When you hold down on a list cell, you might notice a padded area that makes the cell look out of place:
Sample code
Here's the straightforward sample view that produces the list shown above:
// Original code from a Reddit post in /r/swift
struct ContentView: View {
@State var texts = [
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"Pellentesque vitae velit ex. Ut egestas nibh sit amet dolor consectetur, ut egestas lacus convallis.",
"Quisque nec est eget tortor volutpat euismod nec vel dolor.",
"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec sollicitudin velit at lacus pretium, ut aliquet massa volutpat.",
"Sed quis lorem ut nulla vestibulum varius. Nam ac orci neque.",
"Mauris euismod libero vitae libero malesuada, nec dapibus ipsum sagittis.",
"Integer sed sapien sed urna consequat sollicitudin. Nulla facilisi.",
"Ut ut dui ac ligula venenatis vehicula. Duis at magna nec justo consequat tincidunt.",
"Curabitur vestibulum magna nec erat fermentum, a tincidunt erat scelerisque.",
"Suspendisse potenti. Phasellus vel sapien eu urna faucibus cursus."
]
var body: some View {
List {
ForEach(texts, id: \.self) { text in
BlockView(text: text)
}
.onMove { indices, newOffset in
texts.move(fromOffsets: indices, toOffset: newOffset)
}
.listRowSeparator(.hidden)
.listRowSpacing(10)
.listRowBackground(Color.clear)
.scrollContentBackground(.hidden)
.padding(0)
}
}
}
struct BlockView: View {
var text: String
var body: some View {
Text(text)
.multilineTextAlignment(.center)
.padding()
.frame(maxWidth: .infinity)
.background {
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(.quinary)
}
}
}
Removing the White Space
To tackle the white space issue, start by moving some modifiers outside the List
:
...
List {
...
}
.listRowSpacing(10)
.listRowSeparator(.hidden)
.scrollContentBackground(.hidden)
...
Additionally, I removed .padding(0)
as it was unnecessary.
Modifier magic
These changes don’t drastically alter the view, but they clean up the code and layout. One immediate difference is the rounded corners on the white space, indicating that it’s indeed modifiable 🥳.
Here's the magic combination to eliminate the white space entirely:
BlockView(text: text)
.clipShape(.rect(cornerRadius: 10)) // 1
.listRowInsets(EdgeInsets()) // 2
.listRowBackground(Color.clear) // 3
Step-by-step Breakdown
1 - Clip the Shape
.clipShape(.rect(cornerRadius: 10))
clips the cell to a RoundedRect
with a 10-point corner radius. This doesn’t change much on its own, but it sets the stage for the next steps.
2 - Remove Row Insets
.listRowInsets(EdgeInsets())
eliminates any padding around the BlockView
. Ideally, SwiftUI would offer a simpler way to manage this, but for now, EdgeInsets()
gets the job done.
3 - Set Background to Clear
Finally, .listRowBackground(Color.clear)
ensures no unwanted background colours bleed into the view, keeping things crisp and clean.
Applying these tweaks results in a much tidier appearance:
Handy Modifier for Reuse
To streamline this process, I created a custom modifier that cleans up the appearance of list rows. It’s also handy for displaying images without backgrounds:
extension View {
/// Cleans up the appearance of a list row by removing insets and background color.
///
/// - Returns: A modified view with cleaned list row appearance.
func cleanListRow() -> some View {
self.listRowInsets(EdgeInsets())
.listRowBackground(Color.clear)
}
}
Final code
Here’s the final code with all the adjustments for a seamless and clean UI both on initial load and during interaction:
struct ContentView: View {
@State var texts = [ ... ]
var body: some View {
List {
ForEach(texts, id: \.self) { text in
BlockView(text: text)
.clipShape(.rect(cornerRadius: 10))
.cleanListRow()
}
.onMove { indices, newOffset in
texts.move(fromOffsets: indices, toOffset: newOffset)
}
}
.listRowSpacing(10)
.listRowSeparator(.hidden)
.scrollContentBackground(.hidden)
}
}
struct BlockView: View {
var text: String
var body: some View {
Text(text)
.multilineTextAlignment(.center)
.padding()
.frame(maxWidth: .infinity)
.background {
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(.quinary)
}
}
}
If you found this article helpful, you can keep the ideas flowing by supporting me. Buy me a coffee or check out my apps to help me create more content like this!
Coffee Check out my apps