带选择器的 SwiftUI 表单 - NavigationView 创建额外的返回功能
SwiftUI Form with Picker - NavigationView creating additional back function
我正在向用于查看和添加某些事件的应用程序添加侧面加载视图。基本布局如下:
这是此内容的代码:
var body: some View {
List {
Section(header: Text("Existing incidents")) {
ForEach(incidents) { incident in
VStack(alignment: .leading) {
HStack {
Image.General.incident
.foregroundColor(Constants.iconColor)
Text(incident.name)
.foregroundColor(Constants.textColor)
.bold()
}
Spacer()
HStack {
Image.General.clock
.foregroundColor(Constants.iconColor)
Text(incident.date)
.foregroundColor(Constants.textColor)
}
Spacer()
HStack {
Image.General.message
.foregroundColor(Constants.iconColor)
Text(incident.message)
.foregroundColor(Constants.textColor)
}
}
}
}
}
.listStyle(PlainListStyle())
NavigationView {
Section(header: Text("Add an incident")
.bold()
.foregroundColor(Constants.textColor)
.padding()) {
Form {
HStack {
Image.General.select
Spacer()
Picker("Incident selection", selection: $selectedIncident)
{
ForEach(incidentsList, id: \.self) {
Text([=10=])
}
}
}.padding()
HStack {
Image.General.write
Spacer()
TextField("Additional comments", text: $comments)
}.padding()
Button {
}
label: {
Text("Submit")
.foregroundColor(Color(UIColor.white))
}
.padding(.top, 5)
.padding(.bottom, 5)
.padding(.leading, 20)
.padding(.trailing, 20)
.background(Color(UIColor.i6.blue))
.cornerRadius(5)
}
}
}
.padding(.bottom)
}
}
我显然打算将这段代码分开,但是我对表单在 NavigationView 中的工作方式感到困惑。如您所见,我在该部分的顶部有一个后退箭头,可以返回到仅包含部分 header 的视图。但是,我不想从该点向后导航。我打算在这个主页上有 header 部分和表单,而 NavigationView 只是为了使选择器能够工作,它的作用如下:
但是如何删除初始后退箭头并在第一个视图中显示部分 header?
我有两个可能的答案。首先是修复您尝试做的事情。它有效,但它很老套,我只会在视图层次结构的末尾使用它,如果有的话。我将您的代码模拟成一个最小的、可重现的示例:
struct SplitViewFormPicker: View {
@State var incidents = [
Incident(name: "Incident 1", date: "1/1/21", message: "message 1 is here"),
Incident(name: "Incident 2", date: "1/2/21", message: "message 2 is here"),
Incident(name: "Incident 3", date: "1/3/21", message: "message 3 is here"),
Incident(name: "Incident 4", date: "1/4/21", message: "message 4 is here"),
Incident(name: "Incident 5", date: "1/5/21", message: "message 5 is here"),
Incident(name: "Incident 6", date: "1/6/21", message: "message 6 is here"),
Incident(name: "Incident 7", date: "1/7/21", message: "message 7 is here"),
Incident(name: "Incident 8", date: "1/8/21", message: "message 8 is here"),
Incident(name: "Incident 9", date: "1/9/21", message: "message 9 is here"),
Incident(name: "Incident 10", date: "1/10/21", message: "message 10 is here"),
Incident(name: "Incident 11", date: "1/11/21", message: "message 11 is here"),
]
@State var selectedIncident = ""
@State var comments = ""
let incidentsList = ["Incident 1", "Incident 2", "Incident 3", "Incident 4"]
var body: some View {
VStack {
List {
Section(header: Text("Existing incidents")) {
ForEach(incidents) { incident in
VStack(alignment: .leading) {
HStack {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(Constants.iconColor)
Text(incident.name)
.foregroundColor(Constants.textColor)
.bold()
}
Spacer()
HStack {
Image(systemName: "clock")
.foregroundColor(Constants.iconColor)
Text(incident.date)
.foregroundColor(Constants.textColor)
}
Spacer()
HStack {
Image(systemName: "message.fill")
.foregroundColor(Constants.iconColor)
Text(incident.message)
.foregroundColor(Constants.textColor)
}
}
}
}
}
.listStyle(PlainListStyle())
NavigationView {
Form {
Section(header: Text("Add an incident")
.bold()
.foregroundColor(Constants.textColor)
.padding()) {
HStack {
Image(systemName: "hand.draw")
Spacer()
Picker("Incident selection", selection: $selectedIncident)
{
ForEach(incidentsList, id: \.self) {
Text([=10=])
}
}
}.padding()
HStack {
Image(systemName: "rectangle.and.pencil.and.ellipsis")
Spacer()
TextField("Additional comments", text: $comments)
}.padding()
Button {
}
label: {
Text("Submit")
.foregroundColor(Color(UIColor.white))
}
.padding(.top, 5)
.padding(.bottom, 5)
.padding(.leading, 20)
.padding(.trailing, 20)
.background(Color(UIColor.systemGray6))
.cornerRadius(5)
}
.navigationBarHidden(true)
}
}
.padding(.bottom)
}
}
}
struct Constants {
static var textColor = Color.black
static let iconColor = Color.orange
}
struct Incident: Identifiable {
let id = UUID()
let name: String
let date: String
let message: String
}
第二个是灵活的、可重复使用的,而且一点也不难用:
struct PartialSheet<Content: View>: View {
var sheetSize: SheetSize
let content: () -> Content
init(_ sheetSize: SheetSize, @ViewBuilder content: @escaping () -> Content) {
self.sheetSize = sheetSize
self.content = content
}
var body: some View {
content()
.frame(height: sheetHeight())
.animation(.spring(), value: sheetSize)
}
private func sheetHeight() -> CGFloat {
switch sheetSize {
case .quarter:
return UIScreen.main.bounds.height / 4
case .half:
return UIScreen.main.bounds.height / 2
case .threeQuarter:
return UIScreen.main.bounds.height * 3 / 4
case .full:
return .infinity
}
}
}
enum SheetSize: Equatable {
case quarter, half, threeQuarter, full
}
在 SplitViewFormPicker 中这样使用:
NavigationView {
VStack {
List {
// The same code as above not repeated to save space
}
.listStyle(PlainListStyle())
// Call PartialSheet, tell it what size you want, and pass the view as a closure.
// It will return that view sized to the screen.
PartialSheet(.half) {
IncidentForm()
}
}
}
}
您可能会注意到,由于视图在 VStack
中,因此 IncidentForm
将获得的最大屏幕的一半。但是,如果您将它放在 ZStack
中,它会根据您的要求限制自己。根据需要使用它。
我正在向用于查看和添加某些事件的应用程序添加侧面加载视图。基本布局如下:
这是此内容的代码:
var body: some View {
List {
Section(header: Text("Existing incidents")) {
ForEach(incidents) { incident in
VStack(alignment: .leading) {
HStack {
Image.General.incident
.foregroundColor(Constants.iconColor)
Text(incident.name)
.foregroundColor(Constants.textColor)
.bold()
}
Spacer()
HStack {
Image.General.clock
.foregroundColor(Constants.iconColor)
Text(incident.date)
.foregroundColor(Constants.textColor)
}
Spacer()
HStack {
Image.General.message
.foregroundColor(Constants.iconColor)
Text(incident.message)
.foregroundColor(Constants.textColor)
}
}
}
}
}
.listStyle(PlainListStyle())
NavigationView {
Section(header: Text("Add an incident")
.bold()
.foregroundColor(Constants.textColor)
.padding()) {
Form {
HStack {
Image.General.select
Spacer()
Picker("Incident selection", selection: $selectedIncident)
{
ForEach(incidentsList, id: \.self) {
Text([=10=])
}
}
}.padding()
HStack {
Image.General.write
Spacer()
TextField("Additional comments", text: $comments)
}.padding()
Button {
}
label: {
Text("Submit")
.foregroundColor(Color(UIColor.white))
}
.padding(.top, 5)
.padding(.bottom, 5)
.padding(.leading, 20)
.padding(.trailing, 20)
.background(Color(UIColor.i6.blue))
.cornerRadius(5)
}
}
}
.padding(.bottom)
}
}
我显然打算将这段代码分开,但是我对表单在 NavigationView 中的工作方式感到困惑。如您所见,我在该部分的顶部有一个后退箭头,可以返回到仅包含部分 header 的视图。但是,我不想从该点向后导航。我打算在这个主页上有 header 部分和表单,而 NavigationView 只是为了使选择器能够工作,它的作用如下:
但是如何删除初始后退箭头并在第一个视图中显示部分 header?
我有两个可能的答案。首先是修复您尝试做的事情。它有效,但它很老套,我只会在视图层次结构的末尾使用它,如果有的话。我将您的代码模拟成一个最小的、可重现的示例:
struct SplitViewFormPicker: View {
@State var incidents = [
Incident(name: "Incident 1", date: "1/1/21", message: "message 1 is here"),
Incident(name: "Incident 2", date: "1/2/21", message: "message 2 is here"),
Incident(name: "Incident 3", date: "1/3/21", message: "message 3 is here"),
Incident(name: "Incident 4", date: "1/4/21", message: "message 4 is here"),
Incident(name: "Incident 5", date: "1/5/21", message: "message 5 is here"),
Incident(name: "Incident 6", date: "1/6/21", message: "message 6 is here"),
Incident(name: "Incident 7", date: "1/7/21", message: "message 7 is here"),
Incident(name: "Incident 8", date: "1/8/21", message: "message 8 is here"),
Incident(name: "Incident 9", date: "1/9/21", message: "message 9 is here"),
Incident(name: "Incident 10", date: "1/10/21", message: "message 10 is here"),
Incident(name: "Incident 11", date: "1/11/21", message: "message 11 is here"),
]
@State var selectedIncident = ""
@State var comments = ""
let incidentsList = ["Incident 1", "Incident 2", "Incident 3", "Incident 4"]
var body: some View {
VStack {
List {
Section(header: Text("Existing incidents")) {
ForEach(incidents) { incident in
VStack(alignment: .leading) {
HStack {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(Constants.iconColor)
Text(incident.name)
.foregroundColor(Constants.textColor)
.bold()
}
Spacer()
HStack {
Image(systemName: "clock")
.foregroundColor(Constants.iconColor)
Text(incident.date)
.foregroundColor(Constants.textColor)
}
Spacer()
HStack {
Image(systemName: "message.fill")
.foregroundColor(Constants.iconColor)
Text(incident.message)
.foregroundColor(Constants.textColor)
}
}
}
}
}
.listStyle(PlainListStyle())
NavigationView {
Form {
Section(header: Text("Add an incident")
.bold()
.foregroundColor(Constants.textColor)
.padding()) {
HStack {
Image(systemName: "hand.draw")
Spacer()
Picker("Incident selection", selection: $selectedIncident)
{
ForEach(incidentsList, id: \.self) {
Text([=10=])
}
}
}.padding()
HStack {
Image(systemName: "rectangle.and.pencil.and.ellipsis")
Spacer()
TextField("Additional comments", text: $comments)
}.padding()
Button {
}
label: {
Text("Submit")
.foregroundColor(Color(UIColor.white))
}
.padding(.top, 5)
.padding(.bottom, 5)
.padding(.leading, 20)
.padding(.trailing, 20)
.background(Color(UIColor.systemGray6))
.cornerRadius(5)
}
.navigationBarHidden(true)
}
}
.padding(.bottom)
}
}
}
struct Constants {
static var textColor = Color.black
static let iconColor = Color.orange
}
struct Incident: Identifiable {
let id = UUID()
let name: String
let date: String
let message: String
}
第二个是灵活的、可重复使用的,而且一点也不难用:
struct PartialSheet<Content: View>: View {
var sheetSize: SheetSize
let content: () -> Content
init(_ sheetSize: SheetSize, @ViewBuilder content: @escaping () -> Content) {
self.sheetSize = sheetSize
self.content = content
}
var body: some View {
content()
.frame(height: sheetHeight())
.animation(.spring(), value: sheetSize)
}
private func sheetHeight() -> CGFloat {
switch sheetSize {
case .quarter:
return UIScreen.main.bounds.height / 4
case .half:
return UIScreen.main.bounds.height / 2
case .threeQuarter:
return UIScreen.main.bounds.height * 3 / 4
case .full:
return .infinity
}
}
}
enum SheetSize: Equatable {
case quarter, half, threeQuarter, full
}
在 SplitViewFormPicker 中这样使用:
NavigationView {
VStack {
List {
// The same code as above not repeated to save space
}
.listStyle(PlainListStyle())
// Call PartialSheet, tell it what size you want, and pass the view as a closure.
// It will return that view sized to the screen.
PartialSheet(.half) {
IncidentForm()
}
}
}
}
您可能会注意到,由于视图在 VStack
中,因此 IncidentForm
将获得的最大屏幕的一半。但是,如果您将它放在 ZStack
中,它会根据您的要求限制自己。根据需要使用它。