在 SwiftUI 中使用 TabView 时第一个选项卡栏按钮出现两次

问题描述 投票:0回答:7

从屏幕截图中可以看到,“语言”选项卡出现了两次。

我有以下 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 的显示指示器。导航栏中还有一个编辑按钮,点击时如下所示:

swift swiftui tabbar tabview
7个回答
2
投票

将您的视图包装在将内部视图分组为一个的任何视图中。

ZStack{ //HStack, ScrollView, NavigationStack,etc.
     LanguageView()
}

addLanguageAlert 没有主页。你必须把它放在某个地方。


0
投票

您对

.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)
    }
}

0
投票

如果更改顺序,是否会获得两个 LanguageView() 选项卡?

我遇到了这个问题。我的 SwatchListView() 包含 ScrollView() 和 Spacer()。当我将它们都包含在 VStack 中时,问题就消失了。我怀疑在我的例子中 Spacer() 可能已被赋予第二个选项卡。


0
投票

这是因为

Spacer()

在重复的页面中,查找不需要的

Spacer()
并将其删除。通常,错误的
Spacer()
将位于视图的顶部或底部。删除和检查时记得预览

DJ bon26


0
投票

好吧,我也遇到了同样的问题,基于这个答案,您需要做的是将导航视图(复制的选项卡项 - 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()
    }
}

0
投票

真正的答案是: 每个

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

0
投票

我的两分钱,这看起来很幼稚,但可以帮助新手。

如果您在参与 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")
        //}
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.