Why would an init cause a NavigationLink to fail in Swiftui? - swift

I have two pieces of test code to check that things worked out. Basically I wanted to navigate from one view to a specific tag on a TabView page. Everything worked out until I put an init in the destination file. Is there a way to do this so adding an init won't break things? Here are the test files:
import SwiftUI
struct ContentView: View {
#State var selectedView = ""
var body: some View {
NavigationView {
NavigationLink("go", destination: PageTwo(selectedView: "three"))
.foregroundColor(Color.black)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and
import SwiftUI
struct PageTwo: View {
#State var selectedView = ""
// Adding this breaks the link between the files -> init(){}
var body: some View {
TabView(selection: $selectedView) {
Button("Show Second View") {
selectedView = "two"
}
.padding()
.tabItem {
Label("First", systemImage: "1.circle")
}
.tag("one")
Button("Show First View") {
selectedView = "one"
}
.padding()
.tabItem {
Label("Second", systemImage: "2.circle")
}
.tag("two")
Button("Show First View") {
selectedView = "one"
}
.padding()
.tabItem {
Label("Third", systemImage: "3.circle")
}
.tag("three")
}
}
}
struct PageTwo_Previews: PreviewProvider {
static var previews: some View {
PageTwo()
}
}
Thanks for any help.

Related

Why is the navigation bar so tall

Im running Xcode 14 beta 6 and this page is a link from my home page and the nav bar is stacking like there is two.
If you don't understand here's some images
and here's my code:
import SwiftUI
struct ProductsLink: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var btnBack : some View { Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image(systemName: "arrow.backward.circle")
.aspectRatio(contentMode: .fit)
.foregroundColor(Color("LaunchText"))
}
}
}
var body: some View {
Products(text: "All Products")
.navigationBarItems(leading: btnBack)
.navigationBarBackButtonHidden()
}
}
struct ProductsLink_Previews: PreviewProvider {
static var previews: some View {
ProductsLink()
}
}
contentview:
import SwiftUI
struct ContentView: View {
#State private var selection = 1
init() {
UITabBar.appearance().backgroundColor = .systemBackground
}
var body: some View {
TabView(selection: $selection {
Home()
.tabItem { Label("Home", systemImage: "house.fill") }.tag(1)
About()
.tabItem { Label("About", systemImage: "person.crop.circle") }.tag(2)
Products()
.tabItem { Label("Products", systemImage: "bag.fill") }.tag(3)
Gallery()
.tabItem { Label("Gallery", systemImage: "photo.on.rectangle") }.tag(4)
Contact()
.tabItem { Label("Contact", systemImage: "phone.bubble.left.fill") }.tag(5)
}
.accentColor(Color("Green Power Logo Colour"))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
products:
struct Products: View {
var text = "Products"
var body: some View {
NavigationView {
ScrollView {
AllProductCards
NavigationLink(destination: ContactForm()) {
HStack {
Text("Intrested. Contact Form \(Image(systemName: "arrow.forward.circle"))")
.foregroundColor(.white)
} .frame(width: 250 ,height: 50)
.background(Color("Dark Green"))
.cornerRadius(7)
}
Footer()
}
.navigationTitle(text)
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct Products_Previews: PreviewProvider {
static var previews: some View {
Products()
}
}
First one thing, in the ContentView, you are referencing the Products view. If look into the Products view, there is an AllProductCards reference to (I guess) another view. I don't know what's in the view. My assumption would be, it is a List/VStack with NavigationLinks to the ProductsLinks. The ProductsLinks itself are referencing to Products. Is that correct, or what is AllProductsLinks doing? If my assumption is correct, you are looping the products. Since the NavigationView() is part of the Products, that causes a nested NavigationView() into another NavigationView()
I'd think about the looping situation and change that.
For now and here it is "fixed" if you pull out the NavigationView(). You would have to moved the NavigationView() from the Products view into the ContentView like below. So even if the Products view is looped, you have just one NavigationView().
Again, I implemented the AllProductCards according to my assumption.
The updated ContentView (Products nested into NavigationView()):
import SwiftUI
struct ContentView: View {
#State private var selection = 1
init() {
UITabBar.appearance().backgroundColor = .systemBackground
}
var body: some View {
TabView(selection: $selection {
Home()
.tabItem { Label("Home", systemImage: "house.fill") }.tag(1)
About()
.tabItem { Label("About", systemImage: "person.crop.circle") }.tag(2)
NavigationView {
Products()
}
.tabItem { Label("Products", systemImage: "bag.fill") }.tag(3)
Gallery()
.tabItem { Label("Gallery", systemImage: "photo.on.rectangle") }.tag(4)
Contact()
.tabItem { Label("Contact", systemImage: "phone.bubble.left.fill") }.tag(5)
}
.accentColor(Color("Green Power Logo Colour"))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The updated Products view (removed NavigationView()):
struct Products: View {
var text = "Products"
var body: some View {
ScrollView {
AllProductCards()
NavigationLink(destination: ContactForm()) {
HStack {
Text("Intrested. Contact Form \(Image(systemName: "arrow.forward.circle"))")
.foregroundColor(.white)
} .frame(width: 250 ,height: 50)
.background(Color("Dark Green"))
.cornerRadius(7)
}
Footer()
}
.navigationTitle(text)
}
}

Weird list view when I add navigationBarItem

to make it short: I want to have the same view of the list like in the first image i shared. But when I add a navigation bar item the list looks strange to me. It this a bug of the new version of Swift/XCode or needs something to be changed?
Code:
import SwiftUI
import CoreData
struct ContentView: View {
var body: some View {
NavigationView {
List{
Text("test1")
Text("test2")
Text("test3")
}
.navigationTitle("Test")
// .navigationBarItems(leading:
// Text("Test")
// )
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Try using .navigationViewStyle as below:
struct ContentView: View {
#State private var isFullScreen = false
var body: some View {
NavigationView {
List{
Text("One")
Text("Two")
}
.navigationTitle("Testt")
.navigationBarItems(leading: Text("Add"))
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
Hey! Give This A Try!
var body: some View {
NavigationView {
List{
Text("One")
Text("Two")
}
.navigationTitle("Testt, displayMode: .inline)
.navigationBarItems(leading: Text("Add"))
}
}
}

Pop to root view using Tab Bar in SwiftUI

Is there any way to pop to root view by tapping the Tab Bar like most iOS apps, in SwiftUI?
Here's an example of the expected behavior.
I've tried to programmatically pop views using simultaneousGesture as follow:
import SwiftUI
struct TabbedView: View {
#State var selection = 0
#Environment(\.presentationMode) var presentationMode
var body: some View {
TabView(selection: $selection) {
RootView()
.tabItem {
Image(systemName: "house")
.simultaneousGesture(
TapGesture().onEnded {
self.presentationMode.wrappedValue.dismiss()
print("View popped")
}
)
}.tag(0)
Text("")
.tabItem {
Image(systemName: "line.horizontal.3")
}.tag(1)
}
}
}
struct RootView: View {
var body: some View {
NavigationView {
NavigationLink(destination: SecondView()) {
Text("Go to second view")
}
}
}
}
struct SecondView: View {
var body: some View {
Text("Tapping the house icon should pop back to root view")
}
}
But seems like those gestures were ignored.
Any suggestions or solutions are greatly appreciated
We can use tab bar selection binding to get the selected index. On this binding we can check if the tab is already selected then pop to root for navigation on selection.
struct ContentView: View {
#State var showingDetail = false
#State var selectedIndex:Int = 0
var selectionBinding: Binding<Int> { Binding(
get: {
self.selectedIndex
},
set: {
if $0 == self.selectedIndex && $0 == 0 && showingDetail {
print("Pop to root view for first tab!!")
showingDetail = false
}
self.selectedIndex = $0
}
)}
var body: some View {
TabView(selection:selectionBinding) {
NavigationView {
VStack {
Text("First View")
NavigationLink(destination: DetailView(), isActive: $showingDetail) {
Text("Go to detail")
}
}
}
.tabItem { Text("First") }.tag(0)
Text("Second View")
.tabItem { Text("Second") }.tag(1)
}
}
}
struct DetailView: View {
var body: some View {
Text("Detail")
}
}
I messed around with this for a while and this works great. I combined answers from all over and added some stuff of my own. I'm a beginner at Swift so feel free to make improvements.
Here's a demo.
This view has the NavigationView.
import SwiftUI
struct AuthenticatedView: View {
#StateObject var tabState = TabState()
var body: some View {
TabView(selection: $tabState.selectedTab) {
NavigationView {
NavigationLink(destination: TestView(titleNum: 0), isActive: $tabState.showTabRoots[0]) {
Text("GOTO TestView #1")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(.stack)
.onAppear(perform: {
tabState.lastSelectedTab = TabState.Tab.first
}).tabItem {
Label("First", systemImage: "list.dash")
}.tag(TabState.Tab.first)
NavigationView {
NavigationLink(destination: TestView(titleNum: 0), isActive: $tabState.showTabRoots[1]) {
Text("GOTO TestView #2")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}.navigationTitle("")
.navigationBarTitleDisplayMode(.inline).navigationBarTitle(Text(""), displayMode: .inline)
}
.navigationViewStyle(.stack)
.onAppear(perform: {
tabState.lastSelectedTab = TabState.Tab.second
}).tabItem {
Label("Second", systemImage: "square.and.pencil")
}.tag(TabState.Tab.second)
}
.onReceive(tabState.$selectedTab) { selection in
if selection == tabState.lastSelectedTab {
tabState.showTabRoots[selection.rawValue] = false
}
}
}
}
struct AuthenticatedView_Previews: PreviewProvider {
static var previews: some View {
AuthenticatedView()
}
}
class TabState: ObservableObject {
enum Tab: Int, CaseIterable {
case first = 0
case second = 1
}
#Published var selectedTab: Tab = .first
#Published var lastSelectedTab: Tab = .first
#Published var showTabRoots = Tab.allCases.map { _ in
false
}
}
This is my child view
import SwiftUI
struct TestView: View {
let titleNum: Int
let title: String
init(titleNum: Int) {
self.titleNum = titleNum
self.title = "TestView #\(titleNum)"
}
var body: some View {
VStack {
Text(title)
NavigationLink(destination: TestView(titleNum: titleNum + 1)) {
Text("Goto View #\(titleNum + 1)")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
NavigationLink(destination: TestView(titleNum: titleNum + 100)) {
Text("Goto View #\(titleNum + 100)")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView(titleNum: 0)
}
}
You can achieve this by having the TabView within a NavigationView like so:
struct ContentView: View {
#State var selection = 0
var body: some View {
NavigationView {
TabView(selection: $selection) {
FirstTabView()
.tabItem {
Label("Home", systemImage: "house")
}
.tag(0)
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct FirstTabView: View {
var body: some View {
NavigationLink("SecondView Link", destination: SecondView())
}
}
struct SecondView: View {
var body: some View {
Text("Second View")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ContentView()
}
}
}

SwiftUI can't create navigationview

I'm following through some swiftUI tutorial and I couldn't figure out what this meant.
This was the very first step, and I haven't done anything other than adding the NavigationView. How do I resolve this?
That's just container, you need some content inside, If you are just trying something out try following:
var body: some View {
NavigationView {
Text("Testing")
}
}
You cannot have an empty NavigationView. Add something inside.
struct ContentView: View {
var body: some View {
NavigationView {
Text("Text here")
}
}
}
FIRST: Add new SwiftUI-View (Name: MapView.swift without code-changing )
SECOND: Add new SwiftUI-View (Name: SecondView.swift)
THEN Change ContentView and SecondView:
a) ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
MapView() // on upper Screen will appear: "Hello World"
NavigationView {
NavigationLink(destination: SecondView()) {
VStack {
Text("click here")
.fontWeight(.heavy)
.font(.largeTitle)
.padding()
Text("go to screen II")
}
}
} //End of NavigationView
}// End of VStack
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
VStack {
ContentView()
}
}
}
b) SecondView.swift
import SwiftUI
struct SecondView: View {
var body: some View {
VStack {
Text("Screen II")
.fontWeight(.heavy)
.foregroundColor(.blue)
.font(.largeTitle)
.padding()
Text("you changed it")
Text("GRATULATION!")
}
}
}
struct SecondView_Previews: PreviewProvider {
static var previews: some View {
SecondView()
}
}
c) MapView.swift
import SwiftUI
struct MapView: View {
var body: some View {
Text(/*#START_MENU_TOKEN#*/"Hello, World!"/*#END_MENU_TOKEN#*/)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
}
}

How to navigate to a new view from navigationBar button click in SwiftUI

Learning to SwiftUI. Trying to navigate to a new view from navigation bar buttton clicked.
The sample code below:
var body: some View {
NavigationView {
List(0...< 5) { item in
NavigationLink(destination: EventDetails()){
EventView()
}
}
.navigationBarTitle("Events")
.navigationBarItems(trailing:
NavigationLink(destination: CreateEvent()){
Text("Create Event")
}
)
}
}
Three steps got this working for me : first add an #State Bool to track the showing of the new view :
#State var showNewView = false
Add the navigationBarItem, with an action that sets the above property :
.navigationBarItems(trailing:
Button(action: {
self.showNewView = true
}) {
Text("Go To Destination")
}
)
Finally add a navigation link somewhere in your view code (this relies on also having a NavigationView somewhere in the view stack)
NavigationLink(
destination: MyDestinationView(),
isActive: $showNewView
) {
EmptyView()
}.isDetailLink(false)
Put the NavigationLink into the label of a button.
.navigationBarItems(
trailing: Button(action: {}, label: {
NavigationLink(destination: NewView()) {
Text("")
}
}))
This works for me:
.navigationBarItems(trailing: HStack { AddButton(destination: EntityAddView()) ; EditButton() } )
Where:
struct AddButton<Destination : View>: View {
var destination: Destination
var body: some View {
NavigationLink(destination: self.destination) { Image(systemName: "plus") }
}
}
It is an iOS13 bug at the moment: https://forums.developer.apple.com/thread/124757
The "sort-of" workaround can be found here: https://stackoverflow.com/a/57837007/4514671
Here is my solution:
MasterView -
import SwiftUI
struct MasterView: View {
#State private var navigationSelectionTag: Int? = 0
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DestinationView(), tag: 1, selection: self.$navigationSelectionTag) {
EmptyView()
}
Spacer()
}
.navigationBarTitle("Master")
.navigationBarItems(trailing: Button(action: {
self.navigationSelectionTag = 1
}, label: {
Image(systemName: "person.fill")
}))
}
}
}
struct MasterView_Previews: PreviewProvider {
static var previews: some View {
MasterView()
}
}
And the DetailsView -
import SwiftUI
struct DetailsView: View {
var body: some View {
Text("Hello, Details!")
}
}
struct DetailsView_Previews: PreviewProvider {
static var previews: some View {
DetailsView()
}
}