从屏幕截图中可以看到,“语言”选项卡出现了两次。
我有以下 HostingTabBar 代码,该代码在应用程序启动时调用:
struct HostingTabBar: View {
private enum Tab: Hashable {
case language
case canvas
case homework
case test
case more
}
@State private var selectedTab: Tab = .language
var body: some View {
TabView(selection: $selectedTab) {
LanguageView()
.tag(0)
.tabItem {
Text("Language")
Image("language")
}
CanvasView()
.tag(1)
.tabItem {
Text("Canvas")
Image("canvas")
}
HomeworkView()
.tag(2)
.tabItem {
Text("Homework")
Image("homework")
}
TestView()
.tag(3)
.tabItem {
Text("Test")
Image("test")
}
MoreView()
.tag(4)
.tabItem {
Text("More")
Image("more")
}
}
.accentColor(nil)
}
}
struct HostingTabBar_Previews: PreviewProvider {
static var previews: some View {
HostingTabBar()
}
}
LanguageView() 是:
import CoreData
struct LanguageView: View {
@State private var addLanguageIsPresented: Bool = false
@State private var text: String = ""
@State private var languageDuplicateIsPresented: Bool = false
@State private var selectAll: Bool = false
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Languages.entity(), sortDescriptors: [])
var languages: FetchedResults<Languages>
var body: some View {
NavigationView {
List {
ForEach(languages) { language in
NavigationLink(destination: WordsView(language: language)) {
LanguageRowView(language: language, selectAll: selectAll)
}
}
.onDelete { indexSet in
for index in indexSet {
viewContext.delete(languages[index])
}
do {
try viewContext.save()
} catch {
print(error.localizedDescription)
}
}
.frame(width: screenSize.width - 30, height: 50)
}
.navigationBarTitle("Language", displayMode: .inline).opacity(0.8)
.background(Color.init(.systemGroupedBackground))
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Button(action: {
self.text = ""
self.addLanguageIsPresented = true
print("add Language is presented is: \(self.addLanguageIsPresented)")
}, label: {
Image("addLanguage")
.foregroundColor(.green)
})
Button(action: {
print("selectAll is: \(selectAll) before selection")
if selectAll { selectAll = false } else { selectAll = true }
print("selectAll is: \(selectAll) after selection")
}, label: {
if selectAll { Image("unSelectAll") } else { Image("selectAll") }
})
}
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: {
}, label: {
Image("keyboards")
.foregroundColor(.green)
})
Button(action: {
}, label: {
Image("delete")
.foregroundColor(.green)
})
}
}
}
addLanguageAlert(title: "Add Language or Sub-Division", isShown: $addLanguageIsPresented, text: $text, onDone: { text in
print("Inside addLanguageAlert")
guard text.count > 0 else { return }
// submit language to the addAndSaveLanguage method
let newLanguage = Languages(context: viewContext)
newLanguage.name = text
newLanguage.ckupload = true
let selectedLanguage = languages.filter { $0.selected == true }
if selectedLanguage.count > 0 {
newLanguage.selected = false
} else {
newLanguage.selected = true
}
newLanguage.setAsHomework = false
newLanguage.selectedHomework = false
newLanguage.tickedHomework = false
newLanguage.tickedLanguage = false
let recordName = "idlanguage-\(UUID())"
let zone = CKRecordZone(zoneName: "languagesList")
let identification = CKRecord.ID(recordName: recordName, zoneID: zone.zoneID)
let record = CKRecord(recordType: "Languages", recordID: identification)
let coder = NSKeyedArchiver(requiringSecureCoding: true)
record.encodeSystemFields(with: coder)
let metadata = coder.encodedData
newLanguage.ckmetadata = metadata
newLanguage.ckrecordname = recordName
do {
try viewContext.save()
print("Language saved.")
} catch {
print(error.localizedDescription)
}
})
}
}
struct LanguageView_Previews: PreviewProvider {
static var previews: some View {
LanguageView()
}
}
点击一行会加载wordsView():
import CoreData
struct WordsView: View {
@State private var addWordIsPresented: Bool = false
@State private var english: String = ""
@State private var foreign: String = ""
@State private var selectAll: Bool = false
@Environment(\.managedObjectContext) private var viewContext
let language: Languages
var body: some View {
var words = language.words?.allObjects as! [Words]
NavigationView {
List {
ForEach(words) { word in
WordRowView(word: word, selectAll: selectAll)
}
.onDelete { indexSet in
for index in indexSet {
viewContext.delete(words[index])
}
do {
try viewContext.save()
} catch {
print(error.localizedDescription)
}
}
.frame(width: screenSize.width, height: 75)
}
.navigationBarTitle("\(language.name ?? "")", displayMode: .inline).opacity(0.8)
.background(Color.init(.systemGroupedBackground))
}
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Button(action: {
self.english = ""
self.foreign = ""
self.addWordIsPresented = true
}, label: {
Image("addWord")
})
Button(action: {
print("selectAll is: \(selectAll) before selection")
if selectAll { selectAll = false } else { selectAll = true }
print("selectAll is: \(selectAll) after selection")
}, label: {
if selectAll { Image("unSelectAll") } else { Image("selectAll") }
})
}
ToolbarItemGroup(placement: .primaryAction) {
Button(action: {
}, label: {
Image("delete")
})
}
}
addWordAlert(title: "Add Word/Phrase", isShown: $addWordIsPresented, english: $english, foreign: $foreign, onDone: { _,_ in
// pull out the English and foreign words, or an empty string if there was a problem
english = english.trimmingCharacters(in: .whitespacesAndNewlines)
foreign = foreign.trimmingCharacters(in: .whitespacesAndNewlines)
guard english.count > 0 && foreign.count > 0 else { return }
let newWord = Words(context: viewContext)
newWord.english = english
newWord.foreign = foreign
newWord.language = language
newWord.ckimage = false
newWord.ckupload = true
newWord.bonusCorrectAnswers = 0
newWord.ckreference = self.language.ckrecordname
newWord.homeworkAttempts = 0
newWord.homeworkCorrectAnswers = 0
newWord.image = nil
newWord.languageAttempts = 0
newWord.languageCorrectAnswers = 0
newWord.tickedWord = false
newWord.tickedSearchWord = false
newWord.tickedHomework = false
newWord.tickedSearchHomework = false
words.append(newWord)
let selectedWord = words.filter{ $0.language == language && $0.selected == true }
if selectedWord.count > 0 {
newWord.selected = false
} else {
newWord.selected = true
}
let recordName = "idword-\(UUID())"
let zone = CKRecordZone(zoneName: "languagesList")
let id = CKRecord.ID(recordName: recordName, zoneID: zone.zoneID)
let record = CKRecord(recordType: "Words", recordID: id)
let coder = NSKeyedArchiver(requiringSecureCoding: true)
record.encodeSystemFields(with: coder)
let metadata = coder.encodedData
newWord.ckmetadata = metadata
newWord.ckrecordname = recordName
do {
try viewContext.save()
print("Word saved.")
} catch {
print(error.localizedDescription)
}
})
}
}
struct wordsView_Previews: PreviewProvider {
static var previews: some View {
HostingTabBar().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
let screenSize = UIScreen.main.bounds
“测试”选项卡栏项目被移动到“更多”选项卡中的列表,并带有一个在点击时加载 TestView 的显示指示器。导航栏中还有一个编辑按钮,点击时如下所示:
将您的视图包装在将内部视图分组为一个的任何视图中。
ZStack{ //HStack, ScrollView, NavigationStack,etc.
LanguageView()
}
addLanguageAlert 没有主页。你必须把它放在某个地方。
您对
.tag(_:)
的使用不一致。 TabView
的 selection
是 Tab
绑定,而您的标签都是 Int
(0、1、2 等)。要解决此问题,请将您的 tag(_:)
更改为 Tab
枚举值。
代码:
struct HostingTabBar: View {
private enum Tab: Hashable {
case language
case canvas
case homework
case test
case more
}
@State private var selectedTab: Tab = .language
var body: some View {
TabView(selection: $selectedTab) {
LanguageView()
.tag(Tab.language)
.tabItem {
Text("Language")
Image("language")
}
CanvasView()
.tag(Tab.canvas)
.tabItem {
Text("Canvas")
Image("canvas")
}
HomeworkView()
.tag(Tab.homework)
.tabItem {
Text("Homework")
Image("homework")
}
TestView()
.tag(Tab.test)
.tabItem {
Text("Test")
Image("test")
}
MoreView()
.tag(Tab.more)
.tabItem {
Text("More")
Image("more")
}
}
.accentColor(nil)
}
}
如果更改顺序,是否会获得两个 LanguageView() 选项卡?
我遇到了这个问题。我的 SwatchListView() 包含 ScrollView() 和 Spacer()。当我将它们都包含在 VStack 中时,问题就消失了。我怀疑在我的例子中 Spacer() 可能已被赋予第二个选项卡。
这是因为
Spacer()
在重复的页面中,查找不需要的
Spacer()
并将其删除。通常,错误的 Spacer()
将位于视图的顶部或底部。删除和检查时记得预览
好吧,我也遇到了同样的问题,基于这个答案,您需要做的是将导航视图(复制的选项卡项 - LanguageView)包装在 VStack/HStack 中,这可以解决问题。然而,这对我来说没有意义,但确实有效。如果有人知道如何解释为什么会发生这种情况,请这样做。
import CoreData
struct LanguageView: View {
@State private var addLanguageIsPresented: Bool = false
@State private var text: String = ""
@State private var languageDuplicateIsPresented: Bool = false
@State private var selectAll: Bool = false
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Languages.entity(), sortDescriptors: [])
var languages: FetchedResults<Languages>
var body: some View {
VStack {
NavigationView {
List {
ForEach(languages) { language in
NavigationLink(destination: WordsView(language: language)) {
LanguageRowView(language: language, selectAll: selectAll)
}
}
.onDelete { indexSet in
for index in indexSet {
viewContext.delete(languages[index])
}
do {
try viewContext.save()
} catch {
print(error.localizedDescription)
}
}
.frame(width: screenSize.width - 30, height: 50)
}
.navigationBarTitle("Language", displayMode: .inline).opacity(0.8)
.background(Color.init(.systemGroupedBackground))
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Button(action: {
self.text = ""
self.addLanguageIsPresented = true
print("add Language is presented is: \(self.addLanguageIsPresented)")
}, label: {
Image("addLanguage")
.foregroundColor(.green)
})
Button(action: {
print("selectAll is: \(selectAll) before selection")
if selectAll { selectAll = false } else { selectAll = true }
print("selectAll is: \(selectAll) after selection")
}, label: {
if selectAll { Image("unSelectAll") } else { Image("selectAll") }
})
}
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: {
}, label: {
Image("keyboards")
.foregroundColor(.green)
})
Button(action: {
}, label: {
Image("delete")
.foregroundColor(.green)
})
}
}
}
}
addLanguageAlert(title: "Add Language or Sub-Division", isShown: $addLanguageIsPresented, text: $text, onDone: { text in
print("Inside addLanguageAlert")
guard text.count > 0 else { return }
// submit language to the addAndSaveLanguage method
let newLanguage = Languages(context: viewContext)
newLanguage.name = text
newLanguage.ckupload = true
let selectedLanguage = languages.filter { $0.selected == true }
if selectedLanguage.count > 0 {
newLanguage.selected = false
} else {
newLanguage.selected = true
}
newLanguage.setAsHomework = false
newLanguage.selectedHomework = false
newLanguage.tickedHomework = false
newLanguage.tickedLanguage = false
let recordName = "idlanguage-\(UUID())"
let zone = CKRecordZone(zoneName: "languagesList")
let identification = CKRecord.ID(recordName: recordName, zoneID: zone.zoneID)
let record = CKRecord(recordType: "Languages", recordID: identification)
let coder = NSKeyedArchiver(requiringSecureCoding: true)
record.encodeSystemFields(with: coder)
let metadata = coder.encodedData
newLanguage.ckmetadata = metadata
newLanguage.ckrecordname = recordName
do {
try viewContext.save()
print("Language saved.")
} catch {
print(error.localizedDescription)
}
})
}
}
struct LanguageView_Previews: PreviewProvider {
static var previews: some View {
LanguageView()
}
}
真正的答案是: 每个
SwiftUI
View
在其 View
变量中应该只有一个根 var body: some View {}
,如下所示:
var body: some View {
NavigationStack { /* Other views inside root view */ } <-- NavigationStack as Root View
}
或
var body: some View {
VStack { /* Other views inside root view */ } <-- VStack as Root View
}
或
var body: some View {
ScrollView { /* Other views inside root view */ } <-- ScrollView as Root View
}
请勿将多个根
View
放入body
变量中,这会增加重复选项卡的数量,就像增加根视图数量一样多。请参阅以下内容 SwiftUI
View
有两个根 View
,这将创建相同的选项卡项目两次。
错误声明:
var body: some View {
NavigationStack { /* Other views view */ } <-- First Root View
VStack { /* Other views */ } <-- Second Root View
}
// This is wrong bro
我的两分钱,这看起来很幼稚,但可以帮助新手。
如果您在参与 TabView 的视图中编写没有 VStack 或 HStack(或类似......)的多个项目,您会出现以下奇怪的行为:
参见示例。 (如果您取消注释,它将显示重复的选项卡
//VStack { //}
效果很好。
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
FakeProcessView()
.tabItem {
Image(systemName: "alternatingcurrent")
Text("Process")
}
FakeSettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}
}
}
}
struct FakeProcessView: View {
var body: some View {
VStack {
Text("Hello, FakeProcessView!")
}
.padding()
.background(.blue)
}
}
struct FakeSettingsView: View {
var body: some View {
//VStack {
Text("Hello, FakeSettingsView!")
Text("Hello, AGAIN")
//}
}
}