NavigationLink in contextMenu - ios

I've been experimenting with NavigationLink in a contextMenu, and have run into this issue:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("foo")
.contextMenu {
NavigationLink(destination: Text("foo context destination")) { //works
Text("foo context")
}
}
.padding(.all)
NavigationLink(destination: Text("bar destination")) { //works
Text("bar")
.contextMenu {
NavigationLink(destination: Text("bar context destination")) { //does not work
Text("bar context")
}
}
}
.padding(.all)
} //VStack
} //NavigationView
} //body
} //ContentView
As shown in the code, NavigationLink within a contextMenu seems to work for 'foo context' but not for 'bar context'. The difference is that 'foo' is wrapped in a NavigationLink, but 'bar' is not. I would appreciate any suggestions for solving the issue with 'bar context' navigation.
Edit: To clarify, I would like to find a way to navigate to "bar destination" by tapping "bar", OR navigate to "bar context destination" by tapping "bar context" in the contextMenu. The problem seems to be that when "bar" is wrapped in NavigationLink, then NavigationLink in the contextMenu attached to "bar" is not working.
Thanks!

I have made changes to your code so that both "foo" and "bar" works.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("foo")
.contextMenu {
NavigationLink(destination: Text("foo context destination")) { //works
Text("foo context")
}
}
.padding(.all)
Text("bar")
.contextMenu {
NavigationLink(destination: Text("bar context destination")) { //does not work
Text("bar context")
}
}
.padding(.all)
} //VStack
} //NavigationView
} //body
} //ContentView

Related

Minimize or bring back sheet in SwiftUI

I have a Voip calling app using CallKit and when call received, it will open a view call IncomingView in a sheet in my SwiftUI app. So far so good. But i want to minimize the sheet and can navigate to other pages and preferably shows a green bar at the navigation (similar to WhatApp) that indicates the call is going on and when i tap there, it should bring back my "IncomingView".
here is my code:
struct MainView: View {
let acceptPublishser = NotificationCenter.default
.publisher(for: Notification.Name.DidCallAccepted)
let endPublisher = NotificationCenter.default
.publisher(for: Notification.Name.DidCallEnd)
var body: some View {
NavigationView {
TabView {
TabListView()
.tabItem {
Label("Home", systemImage: "house.fill")
}
ContactListView()
.tabItem {
Label("Contacts", systemImage: "person.crop.circle")
}
ProfileView()
.tabItem {
Label("Profile", systemImage: "person.crop.circle")
}
}
.padding(0)
.onAppear(){
self.showModal = MyCallDelegate.shared.isIncomingCall
}
.sheet(isPresented: $showModal){
IncomingCallView() // -> Present IncomingCallview() as sheet
}
.accentColor(Color(.green))
}.onReceive(self.acceptPublishser, perform: { output in
showModal = true
})
.onReceive(self.endPublisher, perform: { output in
showModal = false
})
}
}
struct IncomingCallView: View {
var body: some View {
VStack{
Spacer()
Text("callerId").foregroundColor(.white)
Text("Timer").foregroundColor(.white)
}
}

TabView selection resets to first tab on sheet presentation

Update:
Thanks to Harshil & Sumit for pointing me out that I was simply too dumb to realize that I was using id() instead of tag(). If there is anything you can learn from this question, it is this:
When you work alone on a project, you tend to go blind. You don't see your own mistakes. Do code reviews. Ask friends and colleagues to look over it. It's a great idea. ;)
Original Question:
In my SwiftUI project I am using a TabView with a $selection Binding for being able to switch tabs programmatically.
The problem is: When I present a sheet on, for example, the second view contained in the TabView the selection is reset to the first tab.
For me this seems like a SwiftUI bug - but is there a workaround?
Below you find a working example that demonstrates the behaviour. (Tested with Xcode 12.4)
How to test: Go to the second tab, tap the button "Two" and you will see that you get back to the first tab. As soon as you remove the selection property from the TabView this does not happen any more.
Cheers
Orlando 🍻
enum TabPosition: Hashable {
case one
case two
case three
}
struct RootView: View {
#State private var selection: TabPosition = .one
var body: some View {
TabView(selection: $selection) {
One()
.tabItem { Label("One", systemImage: "1.circle") }
.id(TabPosition.one)
Two()
.tabItem { Label("Two", systemImage: "2.circle") }
.id(TabPosition.two)
Three()
.tabItem { Label("Three", systemImage: "3.circle") }
.id(TabPosition.three)
}
}
}
struct One: View {
var body: some View {
Text("One").padding()
}
}
struct Two: View {
#State var isPresented = false
var body: some View {
Button("Two") { isPresented.toggle() }
.sheet(isPresented: $isPresented, content: {
Three()
})
}
}
struct Three: View {
var body: some View {
Text("Three").padding()
}
}
Use .tag() like this:
struct ContentView: View {
#State private var selection = 1
var body: some View {
TabView(selection: $selection) {
One()
.tabItem { Label("One", systemImage: "1.circle") }
.tag(1)
Two()
.tabItem { Label("Two", systemImage: "2.circle") }
.tag(2)
Three()
.tabItem { Label("Three", systemImage: "3.circle") }
.tag(3)
}
}
}
Insted of id .id(TabPosition.one) assign tag like this .tag(TabPosition.one)

SwiftUI List NavigationView onDelete alert confirmation has ugly animation

The problem is that whenever you put the list in navigationView, the animation of the delete cancelation of the list row is not so nice. Am I doing something wrong in my body property?
var body: some View {
NavigationView {
VStack {
List {
ForEach(self.contacts){ contact in
ContactRow(contact: contact)
}.onDelete { self.setDeletIndex(at: $0) }
}
.alert(isPresented: $showConfirm) {
Alert(title: Text("Delete"), message: Text("Sure?"),
primaryButton: .cancel(),
secondaryButton: .destructive(Text("Delete")) {
self.delete()
})
}
.listStyle(PlainListStyle())
.navigationTitle("Contacts")
.navigationBarItems(trailing: HStack {
Button("Add", action: self.addItem)
})
}
}
}

SwiftUI NavigationLink: how to call a function before showing destination view

I would like to call a function, after clicking on an item, and before displaying the destination view.
The code below doesn't seem to work: myFunction is called, but the destination view is not shown.
It looks like the onTapGesture overwrites the NavigationLink destination.
NavigationView {
List(restaurants) { restaurant in
NavigationLink(destination: RestaurantView(restaurant: restaurant)) {
RestaurantRow(restaurant: restaurant)
}.onTapGesture { myModel.myFunction(restaurant) }
}
}
How can I have both, when clicking on a list item?
function is called
destination view is shown
Try to add NavigationLink into a Button, like:
Button(action: {
//Call here
myModel.myFunction(restaurant)
}, label: {
NavigationLink(destination: RestaurantView(restaurant: restaurant)) {
RestaurantRow(restaurant: restaurant)
}
})
EDIT pasted test code, try directly
struct TestNavigationView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Detail").onAppear() {
test()
}) {
Text("Click")
}
}
}
}
func test() {
print("Hell0")
}
}
another approach: (might not work if List there)
struct TestNavigationView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Detail")) {
Text("Click")
}.simultaneousGesture(TapGesture().onEnded{
test()
})
}
}
}
func test() {
print("Hell0")
}
}

App freezes when I change EnvironmentObject value

I want to login my user when they click a button. When they click the button, it calls self.auth.login() which changes loggedIn to true.
When this change happens, ContentView is meant to reload with HomeView() as the body View. However it just crashes the app.
Here's my code:
ContentView.swift
struct ContentView: View {
#EnvironmentObject var settings: UserSettings
#EnvironmentObject var auth: UserAuth
var body: some View {
if (!auth.loggedIn){
return AnyView(LoginView())
} else {
return AnyView(HomeView())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(UserAuth())
}
}
class UserAuth: ObservableObject {
#Published var loggedIn: Bool = false
func login(){
self.loggedIn = true
}
}
struct LoginView: View {
#EnvironmentObject var auth: UserAuth
var body: some View {
ZStack {
Color.orange
.edgesIgnoringSafeArea(.all)
VStack {
Image("launcher_logo").resizable()
.scaledToFit()
.frame(height: 100)
.padding(.top, 100)
Spacer()
Button(action: {
self.auth.login()
print("loggedIn: ", self.auth.loggedIn) // prints "loggedIn: true" then doesn't print again when pressed
}) {
Text(String(auth.loggedIn))
}
...
Any idea what the problem is?
EDIT:
If I change my ContentView View to just LoginView() then it doesn't freeze. The app works. But I wan ContentView to be dynamic based on whether the user is loggedIn or not.
EDIT2: HomeView is causing it to crash:
struct HomeView: View {
// #EnvironmentObject var auth: UserAuth
var body: some View {
TabView {
HomeView()
.tabItem {
Image(systemName: "1.square.fill")
Text(String("4"))
}.onTapGesture {
// self.auth.login()
}
HomeView()
.tabItem {
Image(systemName: "3.square.fill")
}
Text("The Last Tab")
.tabItem {
Image(systemName: "3.square.fill")
Text("Profile")
}
}
.font(.headline)
}
func goOnline(){
print("went online")
}
}
console log:
[Firebase/Crashlytics][I-CLS000000] Failed to download settings Error Domain=FIRCLSNetworkError Code=-5 "(null)" UserInfo={status_code=404, type=2, request_id=, content_type=text/html; charset=utf-8}
2020-06-04 19:53:04.408869+1000 App[6718:4035694] Connection 5: received failure notification
2020-06-04 19:53:04.409001+1000 App[6718:4035694] Connection 5: failed to connect 3:-9816, reason -1
2020-06-04 19:53:04.409111+1000 App[6718:4035694] Connection 5: encountered error(3:-9816)
Your HomeView calls itself, causing an infinite loop.

Resources