SwiftUI Deep Link Navigation bouncing - swift

I've built a deep linking schema with SwiftUI and have it mostly working. The only issue that I'm suffering from is that when I'm in the same tab and in a navigation stack, a deep link that routes to a different view in the same tab causes this weird bouncing effect where it navigates to the main tab, back to the deep linked view then back out.
in the Main tab view I have this code
.onOpenURL { url in
handleDeepLink(url: url)
}
this goes to the tab and if the deep link is valid it triggers a hidden navigation link
NavigationLink(destination: CompanyPageView(symbol: deepLinkedCompany), isActive: $isDeepLinkingIn, label: { EmptyView() }).hidden()
this works in most cases but for some reason if I'm already in the tab and inside another navigation link in page, opening a deep link does the bounce thing.
How my navState is :
class NavState: ObservableObject {
#Published var firstLevel: String? = nil
var secondLevel: String? = nil
var thirdLevel: String? = nil
}
Subview
#EnvironmentObject var navState: NavState
#Binding var deepLinkedCompany: String
VStack {
//random view stuff
NavigationLink(destination: CompanyPageView(symbol: deepLinkedCompany), tag: "hoboken" , selection: $navState.firstLevel, label: { EmptyView() }).hidden()
}
CompanyPageView
#EnvironmentObject var navState: NavState
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
//view stuff
.onOpenURL{url in
self.mode.wrappedValue.dismiss()
}

Related

Making custom TabView recognize tab item Nav bar when keyboard is active

Problem: Custom TabView does not recognise NavBar so its being pushed out of view when keyboard is active.
This is container code that wraps content and TabBarItems in Zstack. As content is made this way it contains all NavBar informations and that NavBar is being treated like any other view, its being pushed upwards when keyboard is active.
struct CustomTabBarContainerView<Content:View> : View {
#Binding var selection: TabBarItem
#State private var tabs: [TabBarItem] = []
let content: Content
init(selection: Binding<TabBarItem>, #ViewBuilder content: () -> Content){
self._selection = selection
self.content = content()
}
var body: some View {
containerVersion2
}
}
private var containerVersion2 : some View{
ZStack(alignment: .bottom){
content
.zIndex(0)
.ignoresSafeArea()
VStack(spacing:15){
CustomTabBarView(tabs: tabs, selection: $selection, localSelection: selection).zIndex(0)
}
}
}
This is AppTabBar that is being used with custom implementation:
struct AppTabBarView: View {
#State private var selection: String = "home"
#State private var tabSelection: TabBarItem = .chat
var body: some View {
//instead of apple default TabView we are going to use our custom that looks nearly the same
CustomTabBarContainerView(selection: $tabSelection){
ChatsView()//is wrapped with NavigationView as it contains chats to enter called ChatView()
.tabBarItem(tab: .chat, selection: $tabSelection)//code for this is reddudant but this is also CUSTOM not default
LogBackgroundView()
.tabBarItem(tab: .upcoming, selection: $tabSelection)
...
}
One possible solution is to wrap CustomTabBarContainerView with NavigationView here in AppTabBar struct and delete ChatsView NavigationView, that way NavBar works perfectly but I think it ugly solution for producion code.
I've tried adding .ignoresSafeArea(.keyboard, edges: .bottom) to ChatView that is being navigated to but only efect i got was that only Hstack where is TextEdit and sendButton stays in place(not being pushed by keyboard) but scrollView where is conversation and NavBar above it is still being pushed.
struct ChatView: View {
var body: some View {
ZStack{
VStack{
Spacer()
messagesView
.padding(.vertical)
}
VStack(spacing:0){
chatBottomBar
}
}
.background(chatBackground,ignoresSafeAreaEdges: [.bottom])
.ignoresSafeArea(.keyboard, edges: .bottom)
.navigationBarTitleDisplayMode(.inline)
This is customTabBar resource I used as its concept is to be as similar as possible to apple TabView Making custom TabView

How to get a Value in a Class before the View begins? | SwiftUI

I know wired question, but I dont know how to name the Problem, I am very new to Swift and I am trying to get the #ObservedObject messagesManager(HERE) changed to the ChatId both is coming within a navigation Link, so I have both informations, but I can't merge them before the View starts, and I dont want to have a button to update it, when the View loads, it should change to the new Value. Here the code:
import SwiftUI
struct ChatView: View {
#State var infos : SessionServiceImpl
#State var ChatId : String
#ObservedObject var messagesManager = messagingManager(chatID: "")
var body: some View {
VStack {
VStack {
TitleRow(infos: infos)
ScrollViewReader { proxy in
ScrollView{
ForEach(messagesManager.messages, id: \.id) { message in
MessageBubbleView(infos: infos, message: message)
}
}
When the chatID gets changed it will change the Result in the ForEach. I hope u understand my Problem, because currently it just uses the empty Placeholder.

Navigate back after saving in Swift UI

I have a simple detail view where users can input data. After pressing the save button, I would like the app to navigate back to the previous list view. The detail view is opened through a NavigationLink. I assume the action for the button needs to be adjusted in some way. Any advice is appreciated.
In order to do this you need to grab an Environment variable in your detail view:
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
and then call it's dismiss method in your button's action like this:
self.presentationMode.wrappedValue.dismiss()
A more detailed example:
struct DetailView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
#State var someData = ""
var body: some View {
TextField("Placeholder", text: $someData)
Button(action: {
saveData()
self.presentationMode.wrappedValue.dismiss()
}) {
Text("Save data and go back")
}
}
}

Programatical navigation on NavigationView deeper than two views

Is there a way I can do programatical navigation with more than two views using NavigationView?
Like this: View 1 -> View 2 -> View 3
This is the sample code for what I'm trying to do:
class Coordinator: ObservableObject {
#Published var selectedTag: String?
}
struct ContentView: View {
#EnvironmentObject var coordinator: Coordinator
let things = ["first", "second", "third"]
var body: some View {
NavigationView {
List(things, id: \.self) { thing in
NavigationLink(destination: SecondView(thing: thing),
tag: thing,
selection: self.$coordinator.selectedTag) {
Text(thing)
}
}
}
}
}
struct SecondView: View {
#EnvironmentObject var coordinator: Coordinator
let thing: String
let things = ["fourth", "fifth", "sixth"]
var body: some View {
List(things, id: \.self) { thing2 in
NavigationLink(destination: ThirdView(thing: self.thing, thing2: thing2),
tag: thing2,
selection: self.$coordinator.selectedTag) {
Text(thing2)
}
}
}
}
struct ThirdView: View {
let thing: String
let thing2: String
var body: some View {
Text("\(thing) \(thing2)")
}
}
I hoped that I could select a specific tag and deep navigate to the ThirdView but even the simple navigation won't work. If I select a link on the SecondView it will navigate forward and then back, instead of just navigating forward as expected.
I also tried using 2 variables, one to represent the tag of each screen but it also doesn't work.
Is there a way to make this work? Am I doing something wrong?
Currently, your code is acting like there's another flow of navigation from SecondView to ThirdView, which I assume you're not intending to. If that's the intended case, you should also wrap SecondView's body into a NavigationView, too.
Caveat: You'll have 2 navigation bars.
Without tag and selection (in SecondView or also in ContentView), your code works as intended. Is there a specific reason for using tag and selection here? I couldn't find a proper solution that both uses them and have only one NavigationView.

SwiftUI - Presenting a View on top of the current View from AppDelegate

I am working on a project that at some point it receives a notification. When that happens, I need to show a View. I am not able to catch notification from any View so I am looking for a way to change to control it from outside of View structs. After the View's purpose is done, I need to dismiss it where the app left off. Think like the native behaviour when there is an active call.
I thought I could use sheet however I could not find any way to trigger it for every View that could be active when the notifications come. Or maybe trying to extend native View class would work but again, no luck finding a tutorial.
Any help will be appreciated.
Just update your model based on notification. There is not necessary to define .sheet (modal view) everywhere in your view hierarchy. Doing it in root view should be enough.
To demonstrate that (copy - paste - run) I create small project where I mimic notification with SwiftUI Toggle.
import SwiftUI
class Model: ObservableObject {
#Published var show = false
}
struct SubView: View {
#EnvironmentObject var model: Model
var tag: Int
var body: some View {
VStack {
NavigationLink(destination: SubView(tag: tag + 1).environmentObject(model)) {
Text("subview \(tag)")
}
if tag == 2 {
Toggle(isOn: $model.show) {
Text("toggle")
}.padding()
}
}.navigationBarTitle("subview \(tag)")
}
}
struct ContentView: View {
#ObservedObject var model = Model()
var body: some View {
NavigationView {
SubView(tag: 0).environmentObject(model)
}.sheet(isPresented: $model.show) {
Text("sheet")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
with the result