NavigationLink within view presented as an overlay. SwiftUI - swiftui

I am presenting a view as an overlay but when I add a NavigationLink with a destination to that overlay view, the text is greyed out and tapping it does nothing. How do I proceed?
struct ContentView: View {
var body: some View {
NavigationView { }
.overlay(
VStack {
NavigationLink(destination: Text("Go to some view")) {
Text("NavigationLink in overlay")
}
Button {
print("button tapped")
} label: {
Text("Button in overlay")
}
}
)
}
}

Your NavigationLink is not inside the NavigationView! Try this:
struct ContentView: View {
var body: some View {
NavigationView {
Color.clear
.overlay(
VStack {
NavigationLink(destination: Text("Go to some view")) {
Text("NavigationLink in overlay")
}
Button {
print("button tapped")
} label: {
Text("Button in overlay")
}
}
)
}
}
}

Related

how to hide Tabview when using ToolbarItem, preserving ToolbarItem by disappearing

I have two views embedded in a TabView and a third view activated by a ToolbarItem in a navigationStack.
problem 1)
When I tap on plus button I navigate to my addView, but I still can see the tabs at the bottom.
problem 2)
after many test I found that if put the tabView code in MainView Inside a NavigationStack, I solve problem 1) but each time I dismiss from a detailView from a row in ContentView, the navigation Item disappears.
the main view for the tabview
struct MainView: View {
var body: some View {
TabView {
ContentView()
.tabItem {
Label("List", systemImage: "list.dash")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape.fill")
}
}
}
}
the ContentView (a list of lessons, navigationDestination goes to a detail view)
struct ContentView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest (sortDescriptors: [
SortDescriptor(\.lessonNuber, order: .reverse)
], predicate: nil) var lessons: FetchedResults<Lesson>
#State var showAddView = false
var body: some View {
NavigationStack {
VStack {
List {
ForEach(lessons, id: \.self) { lesson in
NavigationLink {
DetailView(lesson: lesson)
} label: {
HStack {
Text("\(lesson.lessonNuber)")
.font(.title)
Text( "\(lesson.un_notion)")
.font(.body)
}
}
}
}
// .background(
// NavigationLink(destination: AddView(), isActive: $showAddView) {
// AddView()
// }
// )
.navigationDestination(isPresented: $showAddView) {
AddView()
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
showAddView = true
} label: {
Label("Add Lesson", systemImage: "plus")
}
}
}
}
.padding()
}
}
}

SwiftUI - WatchOS - NavigationView - Layout issues

I am writing a simple watchOS app using SwiftUI but I am having issues to proper layout it.
This is the code:
struct ContentView: View {
var body: some View {
VStack(spacing: 0) {
HeaderView()
NavigationView {
ScrollView {
NavigationLink(destination: View1()) {
Text("View 1")
}
NavigationLink(destination: View2()) {
Text("View 2")
}
NavigationLink(destination: View2()) {
Text("View 3")
}
NavigationLink(destination: View2()) {
Text("View 4")
}
}
}
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct HeaderView: View {
var body: some View {
VStack() {
Text("Header")
}
.frame(maxWidth: .infinity)
.background(Color.red)
}
}
struct View2: View {
var body: some View {
VStack {
Text("View 2 Content")
}
.navigationTitle("View 2")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("+") {
}
}
}
}
}
Issues:
I cannot rid of this unused space
No matter what the "+" button shows up below the toolbar. I would like to have it in the shown position

SwiftUI - How To Set Child Screen Title

I have a SwiftUI Home screen:
import SwiftUI
struct HomeView: View {
#State private var navigateToSettingsView : Bool = false
var body: some View {
NavigationView {
if navigateToSettingsView {
NavigationLink(destination: UserSettingsView(), isActive: $navigateToSettingsView) {
EmptyView()
}
.navigationTitle("Home") // This overrides the word Back with Home on the child back button
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("User Settings").font(.headline)
}
}
}
}
else {
NavigationLink(destination: Text("Hello, I am HomeView child screen")) {
homeScreen
}
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: {
navigateToSettingsView = true
}) {
Image(systemName: "gearshape")
}
}
ToolbarItemGroup(placement: .navigationBarLeading) {
Text("App Name")
}
}
}
}
}
}
extension HomeView {
private var homeScreen: some View {
VStack {
Text("Hello, I am HomeView")
}
.frame(maxWidth: .infinity)
}
}
The UserSettingsView is just basic right now:
import SwiftUI
struct UserSettingsView: View {
var body: some View {
VStack {
Text("I am a UserSettingsView")
}
}
}
What I am struggling with is setting the title of the child screen when the user clicks on the Gear icon. The ToolbarItem seems to be ignored. How do you set the title of the child screen so that it has a title of User Settings?
I believe you can achieve what you want using a cleaner approach.
The example below replaces the Button with another NavigationLink. It maintains the Home text instead of Back, but it also shows a title in the user settings screen. The only catch is that the title is in the center of the top area, not in the leading position.
Note that the #State variable does not exist anymore.
struct Example: View {
var body: some View {
NavigationView {
NavigationLink(destination: Text("Hello, I am HomeView child screen")) {
Text("I'm a home screen")
}
.navigationTitle("Home")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
NavigationLink(destination: UserSettingsView().navigationTitle("User settings")) {
Image(systemName: "gearshape")
}
}
ToolbarItemGroup(placement: .navigationBarLeading) {
Text("App Name")
}
}
}
}
}

SwiftUI Elements move down between views

I have two views
import SwiftUI
import CoreData
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Image("qr-code")
.resizable()
.scaledToFit()
.position(x: 100, y: 100)
.offset(x: 100)
Text("Thank you")
.position(x: 200)
}.toolbar{
ToolbarItemGroup(placement: .bottomBar) {
NavigationLink(destination: ContentView().navigationBarBackButtonHidden(true)) {
Text("Show QR")
}
Spacer()
NavigationLink(destination: CustomizeView().navigationBarBackButtonHidden(true)) {
Text("Customize")
}
}
}
}
}
}
struct CustomizeView: View {
var body: some View {
List {
Section(header: Text("Important tasks")) {
Text("Task data goes here")
Text("Task data goes here")
}
}.toolbar{
ToolbarItemGroup(placement: .bottomBar) {
NavigationLink(destination: ContentView().navigationBarBackButtonHidden(true)) {
Text("Show QR")
}
Spacer()
NavigationLink(destination: CustomizeView().navigationBarBackButtonHidden(true)) {
Text("Customize")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
CustomizeView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
}
When I click on Customize and the click on Show, I see that the picture moves down. Is it expected behavior? How can I make sure that all elements are in the same positions regardless of how much I clicked on nvaigation buttons?
You should only have one NavigationView in your view hierarchy.
Right now, there are NavigationViews in ContentView and in CustomizeView any time you navigate to either with a NavigationLink, it will add an additional navigation bar to the view, pushing down your content.
To fix this, your root view could just be the NavigationView and then you links could navigation to views that do not contain additional NavigationViews.
struct ContentView: View {
var body: some View {
NavigationView {
BasicView()
}
}
}
struct BasicView : View {
var body: some View {
VStack {
Image(systemName: "pencil")
.resizable()
.scaledToFit()
.position(x: 100, y: 100)
.offset(x: 100)
Text("Thank you")
.position(x: 200)
}.toolbar{
ToolbarItemGroup(placement: .bottomBar) {
NavigationLink(destination: BasicView().navigationBarBackButtonHidden(true)) {
Text("Show QR")
}
Spacer()
NavigationLink(destination: CustomizeView().navigationBarBackButtonHidden(true)) {
Text("Customize")
}
}
}
}
}
struct CustomizeView: View {
var body: some View {
List {
Section(header: Text("Important tasks")) {
Text("Task data goes here")
Text("Task data goes here")
}
}.toolbar{
ToolbarItemGroup(placement: .bottomBar) {
NavigationLink(destination: BasicView().navigationBarBackButtonHidden(true)) {
Text("Show QR")
}
Spacer()
NavigationLink(destination: CustomizeView().navigationBarBackButtonHidden(true)) {
Text("Customize")
}
}
}
}
}
The problem is, I think you have called another ContentView from a ContentView. That's why one more navigation bar is added and shifted
You can create another view called QRShowView and place the qr image there and you have already CustomizeView view . Add two buttons in ContentView like show or customized.
Then call the respective view when is clicked.

SwiftUI ContextMenu navigation to another view

I am trying to get a context menu to navigate to another view using the following code
var body: some View
{
VStack
{
Text(self.event.name).font(.body)
...
Spacer()
NavigationLink(destination: EditView(event: self.event))
{
Image(systemName: "pencil")
}
}
.navigationBarTitle(Text(appName))
.contextMenu
{
NavigationLink(destination: EditView(event: self.event))
{
Image(systemName: "pencil")
}
}
}
The NavigationLink within the VStack works as expected and navigates to the edit view but I want to use a contextMenu. Although the context menu displays the image, when I tap on it it doesn't navigate to the edit view, instead it just cancels the context menu.
I am doing this within a watch app but don't think that should make a difference, is there anything special I have to do with context menu navigation?
I would use the isActive variant of NavigationLink that you can trigger by setting a state variable. Apple documents this here
This variant of NavigationLink is well fit for dynamic/programatic navigation.
Your .contextMenu sets the state variable to true and that activates the NavigationLink. Because you don't want the link to be visible, set the label view to EmptyView
Here's an example, not identical to your post but hopefully makes it clear.
struct ContentView: View {
#State private var showEditView = false
var body: some View {
NavigationView {
VStack {
Text("Long Press Me")
.contextMenu {
Button(action: {
self.showEditView = true
}, label: {
HStack {
Text("Edit")
Image(systemName: "pencil")
}
})
}
NavigationLink(destination: Text("Edit Mode View Here"), isActive: $showEditView) {
EmptyView()
}
}
.navigationBarTitle("Context Menu")
}
}
}
In Xcode 11.4 it's now possible to do this with sensible NavigationLink buttons. Yay! 🎉
.contextMenu {
NavigationLink(destination: VisitEditView(visit: visit)) {
Text("Edit visit")
Image(systemName: "square.and.pencil")
}
NavigationLink(destination: SegmentsEditView(timelineItem: visit)) {
Text("Edit individual segments")
Image(systemName: "ellipsis")
}
}
This works on Xcode 11.6
struct ContentView: View {
#State var isActiveFromContextMenu = false
var body: some View {
NavigationView{
VStack{
NavigationLink(destination : detailTwo(), isActive: $isActiveFromContextMenu ){
EmptyView()
}
List{
NavigationLink(destination: detail() ){
row(isActiveFromContextMenu: $isActiveFromContextMenu)
}
NavigationLink(destination: detail() ){
row(isActiveFromContextMenu: $isActiveFromContextMenu)
}
NavigationLink(destination: detail() ){
row(isActiveFromContextMenu: $isActiveFromContextMenu)
}
}
}
}
}
}
struct detail: View {
var body: some View{
Text("Detail view")
}
}
struct detailTwo: View {
var body: some View{
Text("DetailTwo view")
}
}
struct row: View {
#Binding var isActiveFromContextMenu : Bool
var body: some View {
HStack{
Text("item")
}.contextMenu{
Button(action: {
self.isActiveFromContextMenu = true
})
{
Text("navigate to")
}
}
}
}
I found success in masking the NavigationLink in the background and switching the context with a Button as the shortest yet simplest alternative.
struct ContentView: View {
#State private var isShowing = false
var body: some View {
NavigationView {
Text("Hello")
.background(NavigationLink("", destination: Text("World!"), isActive: $isShowing))
.contextMenu {
Button {
isShowing = true
} label: {
Label("Switch to New View", systemImage: "chevron.forward")
}
}
}
}
}