Reputation: 111
In SwiftUI, I have created a simple tabbar based view:
struct ContentView: View {
enum Tab: Int {
case menu, news, viewc, viewd, viewf
}
@State var selectedTab = Tab.menu
func tabbarItem(text: String, image: String) -> some View {
VStack {
Image(systemName: image)
.imageScale(.large)
Text(text)
}
}
var body: some View {
TabView(selection: $selectedTab) {
MenuView().tabItem{
self.tabbarItem(text: "Menu", image: "list.dash")
}.tag(Tab.menu)
NewsView().tabItem{
self.tabbarItem(text: "News", image: "doc")
}.tag(Tab.news)
EmptyView().tabItem{
self.tabbarItem(text: "ViewC", image: "star.circle")
}.tag(Tab.viewc)
EmptyView().tabItem{
self.tabbarItem(text: "ViewD", image: "speaker")
}.tag(Tab.viewd)
EmptyView().tabItem{
self.tabbarItem(text: "ViewF", image: "person")
}.tag(Tab.viewf)
}
}
}
Like the screen shown below:
Questions:
How can I implement what I need in SwiftUI?
Am I using the suitable components to achieve it? Basically, it's a menu and a List, if tab the news, it will just load the latest news, if click from the menu, it will display the filtered news.
Thanks in advance.
Upvotes: 0
Views: 578
Reputation: 2714
Here's the code I created to achieve the layout you are looking for in SwiftUI. Hope this helps.
import SwiftUI
struct ContentView: View {
enum Tab: Int {
case menu, news, viewc, viewd, viewf
}
@State var selectedTab = Tab.menu
@State var showFilter: Bool = false
@State var selectedFilter: String = ""
func tabbarItem(text: String, image: String) -> some View {
VStack {
Image(systemName: image)
.imageScale(.large)
Text(text)
}
}
var body: some View {
TabView(selection: $selectedTab) {
NavigationView {
NewsView(filter: $selectedFilter)
.navigationBarItems(trailing: Button(action: { self.showFilter.toggle() }) {
Text("Filters")
})
.navigationBarTitle("News")
.sheet(isPresented: $showFilter) {
MenuView(selectedFilter: self.$selectedFilter)
}
}
.tabItem{
self.tabbarItem(text: "News", image: "doc")
}
.tag(Tab.news)
ViewC().tabItem{
self.tabbarItem(text: "ViewC", image: "star.circle")
}.tag(Tab.viewc)
EmptyView().tabItem{
self.tabbarItem(text: "ViewD", image: "speaker")
}.tag(Tab.viewd)
EmptyView().tabItem{
self.tabbarItem(text: "ViewF", image: "person")
}.tag(Tab.viewf)
}
}
}
struct MenuView: View {
@Environment(\.presentationMode) var presentationMode
var menus: [String] = ["Menu No.1", "Menu No.2", "Menu No.3"]
@Binding var selectedFilter: String
var body: some View {
List {
ForEach(menus, id: \.self) { menu in
Button(action: {
self.selectedFilter = menu
self.presentationMode.wrappedValue.dismiss()
}) {
Text("\(menu)")
}
}
}
}
}
struct NewsView: View {
@Binding var filter: String
var body: some View {
List {
if filter.count > 0 {
Text("The news is filtered using \(filter)")
}
else {
Text("The latest news is being displayed.")
}
}
}
}
struct ViewC: View {
var body: some View {
Text("Under construction")
}
}
PREVIOUS ANSWER
To achieve the current layout, you need to nest a TabView inside a NavigationView. I created a sample project to test that and below is the code. Though it works, on back button click, I received the below warning:
"Trying to pop to a missing destination at /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-39.4.3/UIKit/UIKitNavigationBridge.swift:390"
Therefore, you might need to rethink the application UI. As the menu tab is just the filters for the News tab, have a TabView that combines both the Menu and the News tab into one. On startup the app will show the latest news. You can then have a separate view within NewsView for the filters.
import SwiftUI
struct ContentView: View {
enum Tab: Int {
case menu, news, viewc, viewd, viewf
}
@State var selectedTab = Tab.menu
func tabbarItem(text: String, image: String) -> some View {
VStack {
Image(systemName: image)
.imageScale(.large)
Text(text)
}
}
var body: some View {
NavigationView {
TabView(selection: $selectedTab) {
MenuView().tabItem{
self.tabbarItem(text: "Menu", image: "list.dash")
}.tag(Tab.menu)
NewsView().tabItem{
self.tabbarItem(text: "News", image: "doc")
}.tag(Tab.news)
EmptyView().tabItem{
self.tabbarItem(text: "ViewC", image: "star.circle")
}.tag(Tab.viewc)
EmptyView().tabItem{
self.tabbarItem(text: "ViewD", image: "speaker")
}.tag(Tab.viewd)
EmptyView().tabItem{
self.tabbarItem(text: "ViewF", image: "person")
}.tag(Tab.viewf)
}
}
}
}
struct MenuView: View {
var menus: [String] = ["Menu No.1", "Menu No.2", "Menu No.3"]
var body: some View {
List {
ForEach(menus, id: \.self) { menu in
NavigationLink(destination: NewsView(filter: "filterString")) {
Text("\(menu)")
}
}
}
}
}
struct NewsView: View {
var filter: String = ""
var body: some View {
List {
if filter.count > 0 {
Text("The news is filtered using \(filter)")
}
else {
Text("The latest news is being displayed.")
}
}
}
}
Upvotes: 1