Creating two sidebars in iPadOS application using SwiftUI - ios

I have created one sidebar with NavigationView, which by default appends to the left of the landscape view of application. However I wanted to have another on the right side.
NavigationView {
List {
Label("Pencil", systemImage: "pencil")
Label("Paint", systemImage: "paintbrush.fill")
Label("Erase", systemImage: "quote.opening")
Label("Cutter", systemImage: "scissors")
Label("Eyedropper", systemImage: "eyedropper.halffull")
Label("Draw Line", systemImage: "line.diagonal")
}
.listStyle(SidebarListStyle())
}

Here's a simple working example made with SwiftUI:
struct ContentView: View {
var body: some View {
NavigationView{
TagView()
Text("Default second View")
Text("Default Third View")
}
}
}
struct TagView: View {
let tags = ["Apple", "Google"]
var body: some View {
List{
ForEach(tags, id: \.self) { name in
NavigationLink {
ProductView(tag: name)
} label: {
Text(name)
}
}
}
}
}
struct ProductView: View {
var tag: String
var products: [String] {
if tag == "Apple" {
return ["iPhone", "iPad", "MacBook"]
} else {
return ["resuable stuff"]
}
}
var body: some View {
List{
ForEach(products, id: \.self) { name in
NavigationLink {
DetailsView()
} label: {
Text(name)
}
}
}
}
}
struct DetailsView: View {
var body: some View {
Text("Detailed explanation about product")
}
}

Related

EmptyBody().onAppear not works when sheet present with item

When I present sheet with .sheet(isPresented... onAppear of EmptyView() triggered
but when I use .sheet(item... then onAppear doesn't trigger. I don't understand what mistake I am doing?
item:
enum ActiveSheet: Identifiable {
var id: String { UUID().uuidString }
case customA
case customB
}
Main View:
struct ContentView: View {
#State private var activeSheet: ActiveSheet?
var body: some View {
VStack {
Button(action: { activeSheet = .customA }) {
Text("View A")
}
Button(action: { activeSheet = .customB }) {
Text("View B")
}
}
.buttonStyle(.borderedProminent)
//If I use this .sheet(isPresented... then onAppear triggers, but not with item
.sheet(item: $activeSheet) { item in
switch item {
case .customA:
CustomViewA()
case .customB:
CustomViewB()
}
}
}
}
Empty Views:
struct CustomViewA: View {
var body: some View {
EmptyView()
.onAppear {
print("OnAppear")
}
}
}
struct CustomViewB: View {
var body: some View {
EmptyView()
.onAppear {
print("OnAppear")
}
}
}

Picker conflicts with NavigationLink gesture

I am trying to create the following card view.
With the following code to achieve it.
struct SimpleGame: Identifiable, Hashable {
var id = UUID()
let name: String
}
enum PlayingStatus: String {
case In = "I"
case Out = "O"
case Undecided = "U"
}
struct TestView: View {
let games: [SimpleGame] = [
.init(name: "First"),
.init(name: "Second")
]
#State private var currentStatus: PlayingStatus = .Undecided
var body: some View {
NavigationView {
List(games) { game in
Section {
VStack {
NavigationLink(value: game) {
Text("\(game.name)")
}
Divider()
Picker("Going?", selection: $currentStatus) {
Text("No Response")
.tag(PlayingStatus.Undecided)
Text("Going")
.tag(PlayingStatus.In)
Text("Not going")
.tag(PlayingStatus.Out)
}
.font(.body)
}
}
}
.navigationDestination(for: Game.self) { game in
Text("Detail View")
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Upcoming")
}
}
}
But a tap on element wrapped by NavigationLink is registering as a tap on the Picker. Anyone know of a way around this?
iOS 16/Xcode 14
you could try this:
List {
ForEach(games, id: \.name) { game in
Section {
NavigationLink(value: game) {
Text("\(game.name)")
}
// -- here
VStack {
Divider()
Picker("Going?", selection: $currentStatus) {
Text("No Response").tag(PlayingStatus.Undecided)
Text("Going").tag(PlayingStatus.In)
Text("Not going").tag(PlayingStatus.Out)
}
.font(.body)
}
}
}
}
What often works for me is extending the view.
struct TestView: View {
var body: some View {
List {
ForEach(games, id: \.name) { game in
Section {
NavigationLink(value: game) {
Text("\(game.name)")
}
// -- here
VStack {
Divider()
picker
}
}
}
}
}
}
Extension TestView {
private var picker: some View {
Picker("Going?", selection: $currentStatus) {
Text("No Response").tag(PlayingStatus.Undecided)
Text("Going").tag(PlayingStatus.In)
Text("Not going").tag(PlayingStatus.Out)
}
.font(.body)
}
}

SwiftUI - Section style difference when embedding List within a VStack

It seems that there is a difference in showing a List Section header when you embed the List in a VStack. Does anybody know why this happens?
struct ContentView: View {
#State var toggle: Bool = false
let items: [String] = ["Apple", "Pear", "Banana"]
var body: some View {
NavigationView {
if toggle {
VStack {
list
}
} else {
list
}
}
}
var list: some View {
List {
Section {
ForEach(items, id: \.self) { item in
Text(item)
}
} header: {
Text("Fruit")
}
}
.navigationTitle(toggle ? "VStack" : "No VStack")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
toggle.toggle()
} label: {
Text("Toggle")
}
}
}
}
}

Is there a problem with the way I'm structuring my SwiftUI project that causes .navigationTitle to not appear?

My SwiftUI project refuses to display the navigation title after a certain point. I'm using a navigation structure that I haven't seen implemented on any example projects I've seen, but it makes sense to me and has been seeming to work thus far. My suspicion is that since I'm using a different navigation structure that it is part of the problem. The .navigationBar is there on every page, but the title doesn't display.
SettingsView.swift screen
I've tried many solutions on Stack Overflow and otherwise. I've tried every combination of .navigationBarHidden(false), .navigationBarTitle() and .navigationBarBackButtonHidden(true) on every page listed below, with no change. I've also tried every location I could think of to place these combinations of .navigationBar modifiers.
Recently, I discovered .toolbar, and this changes nothing either. My suspicion is that (as seen in the code snippets below) since the NavigationView is in the first view (WelcomeUI.swift), I can't place the .navigationBarTitle deeper in the code.
Below is my current navigation structure, and bits of code from each file:
WelcomeUI.swift
struct WelcomeUI: View {
var body: some View {
NavigationView {
VStack {
//NavigationLink(destination: SignupUI(), label: {
//Text("Sign Up")
//}
NavigationLink(destination: LoginUI(), label: {
Text("Log In")
}
}
}
}
}
LoginUI.swift
struct LoginUI: View {
var body: some View {
VStack {
NavigationLink(destination: MainUI(), label: { Text("Log In") })
//Button(action: { ... }
}
.navigationBarHidden(false)
}
}
Note: SignupUI.swift is essentially the same as LoginUI.swift
MainUI.swift
struct MainUI: View {
var body: some View {
TabView {
//SpendingView()
//.tabItem {
//Image(...)
//Text("Spending")
//}
//SavingView()
//.tabItem {
//Image(...)
//Text("Saving")
//}
//AddView()
//.tabItem {
//Image(...)
//Text("Add")
//}
//EditView()
//.tabItem {
//Image(...)
//Text("Edit")
//}
SettingsView()
.tabItem {
//Image(...)
Text("Settings")
}
}
.navigationBarBackButtonHidden(true)
}
}
Note: All views in MainUI.swift are structured the same.
SettingsView.swift
struct SettingsView: View {
var body: some View {
ZStack {
Form {
Section(header: Text("Section Header")) {
NavigationLink(destination: WelcomeUI()) {
Text("Setting Option")
}
}
Section {
//Button("Log Out") {
//self.logout()
//}
Text("Log Out")
}
}
.navigationBarTitle("Settings") // This has no effect on code no matter where it is place in SettingsView.swift
}
}
}
I should also note that only the pages after MainUI.swift is affected by this. WelcomeUI.swift and LoginUI.swift work as expected.
Look at the MainUI navigationTitle. I just put a title in every View to find what was going on.
import SwiftUI
struct SubSpendingView: View {
var body: some View {
ScrollView{
Text("SubSpendingView")
}.navigationBarTitle("SubSpending"
//, displayMode: .inline
)
}
}
struct SpendingView: View {
var body: some View {
ScrollView{
Text("SpendingView")
NavigationLink("subSpending", destination: SubSpendingView())
}.padding()
}
}
struct WelcomeUI: View {
var body: some View {
NavigationView {
VStack {
//NavigationLink(destination: SignupUI(), label: {
//Text("Sign Up")
//}
NavigationLink(destination: LoginUI(), label: {
Text("Go to Log In")
})
}.navigationTitle(Text("WelcomeUI"))
}
}
}
struct SettingsView: View {
var body: some View {
VStack{
ZStack {
Form {
Section(header: Text("Section Header")) {
NavigationLink(destination: WelcomeUI()) {
Text("Setting Option")
}
}
Section {
//Button("Log Out") {
//self.logout()
//}
Text("Log Out")
}
}
Button("say-high", action: {print("Hi")})
// This has no effect on code no matter where it is place in SettingsView.swift
}
}//.navigationBarTitle("Settings")
}
}
struct LoginUI: View {
var body: some View {
VStack {
NavigationLink(destination: MainUI(), label: { Text("Log In") })
//Button(action: { ... }
}.navigationTitle(Text("LoginUI"))
.navigationBarHidden(false)
}
}
struct MainUI: View {
#State var selectedTab: Views = .adding
var body: some View {
TabView(selection: $selectedTab) {
SpendingView()
.tabItem {
Image(systemName: "bag.circle")
Text("Spending")
}.tag(Views.spending)
//SavingView()
//.tabItem {
//Image(...)
//Text("Saving")
//}
Text("Adding View")
.tabItem {
Image(systemName: "plus")
Text("Add")
}.tag(Views.adding)
Text("Edit View")
.tabItem {
Image(systemName: "pencil")
Text("Edit")
}.tag(Views.edit)
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}.tag(Views.settings)
}
//This overrides the navigationTitle in the tabs
.navigationBarTitle(Text(selectedTab.rawValue)
//, displayMode: .inline
)
.navigationBarBackButtonHidden(true)
}
}
enum Views: String{
case settings = "Settings"
case spending = "Spending"
case adding = "Add"
case edit = "Edit"
}
struct PaddyNav: View {
var body: some View {
WelcomeUI()
//Wont work
.navigationTitle(Text("PaddyNav"))
}
}
struct PaddyNav_Previews: PreviewProvider {
static var previews: some View {
PaddyNav()
}
}

How to use NavigationView and NavigationLink in SwiftUI?

Piece of code that I made for this question that runs in Playground:
import SwiftUI
struct Player {
let name: String
let surname: String
}
struct DetailView2: View {
var player: Player
var body: some View {
Text("\(player.name) \(player.surname)")
}
}
struct PlaygroundView: View {
// MARK: - Propertiers
#State private var selection = 0
private var players = [
Player(name: "Lionel", surname: "Messi"),
Player(name: "Diogo", surname: "Jota"),
]
// MARK: - View
var body: some View {
TabView(selection: $selection) {
NavigationView {
VStack {
Text("Settings").font(.title)
List(0..<players.count, id: \.self) { index in
NavigationLink(destination: DetailView2(player: players[index])) {
Text("\(index)")
}
}
}
.navigationTitle("Players")
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
}
.background(Color.white)
.tabItem {
Image(systemName: "house.fill")
Text("Players")
}
.tag(0)
VStack {
Text("Settings").font(.title)
List(0..<2, id: \.self) { index in
Text("#\(index)")
}
}
.tabItem {
Image(systemName: "book.fill")
Text("Foo")
}
.tag(1)
}
}
}
struct Playground_Previews: PreviewProvider {
static var previews: some View {
PlaygroundView()
}
}
Current Players bar:
Current Settings bar:
How can I fix the code such that "Players" bar will look like Settings bar (it terms of the styling of the title). It seems like I've got the tab item and navigation working already.
It seems you're looking something like below (prepared & tested with Xcode 12.1 / iOS 14.1)
var body: some View {
TabView(selection: $selection) {
VStack {
Text("Settings").font(.title)
NavigationView {
List(0..<players.count, id: \.self) { index in
NavigationLink(destination: DetailView2(player: players[index])) {
Text("\(index)")
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
}
}
.background(Color.white)
.tabItem {
Image(systemName: "house.fill")
Text("Players")
}
.tag(0)
VStack {
Text("Settings").font(.title)
List(0..<2, id: \.self) { index in
Text("#\(index)")
}
}
.tabItem {
Image(systemName: "book.fill")
Text("Foo")
}
.tag(1)
}
}

Resources