I am running my app on iOS 17 and have added swipe actions on list row. I wish to show the image of the action and text below it, but I noticed that only image gets displayed.
Code:
import SwiftUI
import Foundation
struct ContentView: View {
var body: some View {
NavigationStack {
VStack {
List {
Section {
HStack {
Text("Hello World").font(.body)
Spacer()
Divider().frame(width: 3.0)
.overlay(Color.blue).padding(.trailing, -50.0).padding([.top, .bottom], -1.5)
}
.padding([.top, .bottom], 1.5)
.swipeActions(allowsFullSwipe: false) {
Button(role: .destructive) {
print("Deleting row")
} label: {
/// Note: Want to show Image and text below it.
VStack(spacing: 2.0) {
Image(systemName: "trash")
Text("Delete").font(.caption2)
}
/// Note: Using label as well doesn't show both text and icon. It only shows the icon.
// Label("Delete", systemImage: "trash")
}
// TODO: Add more swipe action buttons
}
}
}
.listStyle(.insetGrouped)
}
}
}
}
How do I show image and text for swipe action?
2
Answers
One way to force an image to be shown together with a label is to build an image yourself that combines the two. This can be done by creating an
Image
with drawing instructions, seeinit(size:label:opaque:colorMode:renderer:)
For example:
You can then use this as the label for the swipe action:
EDIT Following from your comment, longer labels could be addressed in various ways:
.font
modifier to the result:However, this also impacts the size of the symbol. So it works better to set the font when rendering the label:
in
a rectangle of fixed size:.multilineTextAlignment
to the result:.lineLimit
and.minimumScaleFactor
also work. So this opens the way for…A more generic solution
Ideally, the generation of this type of composite icon should be able to handle longer labels automatically. This is possible by resolving the text and the image inside the function and then examining their sizes.
So here is a more general-purpose solution for generating a swipe icon. It includes the following logic:
.body
as default, but it is scaled automatically to fit.I found that if the result has square proportions, it is able to fill the full height of the list row. So the function below generates an image of size 60×60. This gets scaled-to-fit, which probably means it gets shrunk a bit when actually used:
Here’s how it can be used:
For each item add a vertical padding of 5.
.padding(.vertical, 5)
This will show the image and label, just like in Mail app.