What would be the code to add leading and trailing icons to the swift UI view, which I display with the view controller, as displayed on the picture with the close icon and the Clear button?
Is this a built-in behavior, or a custom stack view? Can this be made sticky to the top, so that it would overlay the content if it gets scrolled?
Here is my code for the view:
struct CustomSheetView: View {
@Environment(\.presentationMode) var presentationMode
@State private var isNewInMenuSelected = false
@State private var isOurFavoritesSelected = false
@State private var isCustomerPickSelected = false
var filtersLabel: String
var useOurSearchFiltersLabel: String
var applyFiltersLabel: String
var body: some View {
VStack(alignment: .leading, spacing: 20) {
Text(filtersLabel)
.font(.largeTitle)
.fontWeight(.bold)
.padding(.top, 20)
.padding(.leading, 20)
Text(useOurSearchFiltersLabel)
.font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.padding(.leading, 20)
VStack(alignment: .leading, spacing: 10) {
Toggle("New in menu", isOn: $isNewInMenuSelected)
Toggle("Our favorites", isOn: $isOurFavoritesSelected)
Toggle("Customer pick", isOn: $isCustomerPickSelected)
}
.padding(.horizontal, 20)
Spacer()
Button(action: {
let checkboxStates = [
"new_in_menu": isNewInMenuSelected,
"our_favorites": isOurFavoritesSelected,
"customer_pick": isCustomerPickSelected
]
let keyWindow = UIApplication.shared.connectedScenes
.filter { $0.activationState == .foregroundActive }
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
.first { $0.isKeyWindow }
if let flutterViewController = keyWindow?.rootViewController as? FlutterViewController {
let channel = FlutterMethodChannel(name: "custom_sheet_channel", binaryMessenger: flutterViewController.binaryMessenger)
channel.invokeMethod("checkboxStates", arguments: checkboxStates) { result in
// Handle any response if necessary
}
}
presentationMode.wrappedValue.dismiss()
}) {
Text(applyFiltersLabel)
.padding()
.frame(maxWidth: .infinity)
.background(Color(hex: "#d0a771"))
.foregroundColor(.white)
.cornerRadius(8)
.padding(.horizontal, 20)
}
.padding(.bottom, 20)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) // Align content to top-left
}
}
I assume this question is about implementing a header for a view shown as a .sheet
.
Here are two techniques you can use:
1. Add a toolbar
You can apply a toolbar to the sheet content. This means nesting the sheet content inside some kind of container that supports a toolbar, for example, a NavigationStack
:
struct SimpleSheetView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
NavigationStack {
ScrollView {
Color.red.frame(height: 200)
Color.orange.frame(height: 200)
Color.yellow.frame(height: 200)
Color.green.frame(height: 200)
Color.blue.frame(height: 200)
Color.indigo.frame(height: 200)
Color.purple.frame(height: 200)
}
.navigationTitle("Filters")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
dismiss()
} label: {
Image(systemName: "xmark")
.imageScale(.large)
.foregroundStyle(.foreground)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Clear") {}
.buttonStyle(.plain)
.fontWeight(.bold)
.foregroundStyle(.green)
}
}
}
}
}
When the main content is scrolled, it goes behind the toolbar and the toolbar automatically takes on a translucent background:
2. A custom header
If you don't especially need the translucent effect behind the header, then it is simple to build a custom header with a plain background.
Show the header as the first item in a VStack
, with the scrolled region below it.
Use an HStack
to combine the two buttons, with a Spacer
between them. Then use a ZStack
to combine the buttons with the title. This way, the title is always centered, both horizontally and vertically.
var body: some View {
VStack(spacing: 0) {
ZStack {
Text("Filters")
.font(.headline)
.padding(.vertical, 20)
HStack {
Button {
dismiss()
} label: {
Image(systemName: "xmark")
.imageScale(.large)
.foregroundStyle(.foreground)
}
Spacer()
Button("Clear") {}
.buttonStyle(.plain)
.fontWeight(.bold)
.foregroundStyle(.green)
}
.padding(.horizontal, 20)
}
ScrollView {
// ...
}
}
}