How do I return from a child view to a parent view by tapping a row element in a list in SwiftUI? - ios

I need to perform an action similar to "unwind to segue" when the user taps and select a cell row in a list. I'm new to SwiftUI and I would really appreciate if someone can help me.
struct cellView : View {
#State var name = ""
var body: some View {
VStack {
Text(name)
Button(action: { ***unwind to parent view code*** })
}
}
}
I expect there to be an action code to return to parent view after tapping the cell row.

You can do it using Enviroment variable called presenationMode, like this:
struct CellView : View {
#Environment(\.presentationMode) var presentationMode
#State var name = ""
var body: some View {
VStack {
Text(name)
Button("Tap me",
action: { self.presentationMode.wrappedValue.dismiss() })
}
}
}

Related

onAppear not updating instantly in SwiftUI

I have two views: the list, and the detail.
When I tap into the list item and access the detail view, I can configure settings to that model item.
If I have it update (save to CoreData) .onDisappear the List View's .onAppear doesn't trigger with the new saved data.
However, if I add in a "save" button in the Detail View and then manually unwind to the List View the data updates instantly.
Is there a reason for .onDisappear and .onAppear for different screens not triggering in order?
I would have thought that the unwind of the DetailView would trigger the function before the ListView's onAppear would, unless I'm missing something?
I tried to find some info about running them synchronously but with the new async the results haven't been clear.
Note: I'm not displaying the CoreDataManager but it essentially has 4 functions - create(...all the variables to save...), retrieve(), update(), delete()
Example code
ListView
struct ListView: View {
let coreDM = CoreDataManager()
#State var days: [Int] = []
var body: some View {
NavigationView {
List(0..<5) { item in
NavigationLink {
DetailView()
} label: {
Text("List item: \(item)")
}
}
}.onAppear { coreDM.retrieve() }
}
}
DetailView
This version doesn't work
struct DetailView: View {
let coreDM = CoreDataManager()
#State var isOn: Bool = false
var body: some View {
Form {
Toggle("Turn this on", isOn: $isOn)
}
.onDisappear { coreDM.update() }
}
}
This version does work
struct DetailView: View {
let coreDM = CoreDataManager()
#State var isOn: Bool = false
var body: some View {
Form {
Toggle("Turn this on", isOn: $isOn)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
coreDM.update()
} label: {
Text("Save")
}
}
}
}
}
The reason I am confused is that if I add this portion to the DetailView the update does work correctly:
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
...
Button {
coreDM.update()
presentationMode.wrappedValue.dismiss()
} label: {
...
So it seems that I can unwind the view, update the database, and refresh in one move. But if I want to "automate" it by doing it only on the unwind, the triggers don't work correctly.

How can I reproduce Picker's selection binding?

I'm building a custom view and I'm trying to manipulate the active state of its children.
I have this:
struct Menu<Content>: View where Content: View {
#ViewBuilder var content: () -> Content
var body: some View {
content()
}
}
struct ScreenView: View {
var body: some View {
Menu {
Text("Home")
Text("Settings")
Text("Profile")
}
}
}
I would like to be able to pass a binding to the Menu view and based on that, to change the text color if the state is matching the actual text or id of the view. There is a Picker view example that does what I want to achieve. The Picker is managing the look and feel of the selected element. Am I wrong?
struct PickerViewExample: View {
#State var selection: Int
var body: some View {
Picker(selection: $selection, label: Text("Picker"), content: {
Text("1").tag(1)
Text("2").tag(2)
})
}
}
I'd like to know if there is a way to ForEach somehow the content ViewBuilder property in order to manipulate subviews. I like the way Apple solved this using tags. Any alternative is welcome.
I think, this will help
struct ContentView: View {
private var menuItem = ["Home", "Settings", "Profile"]
#State private var selectedMenu = "Home"
var body: some View {
VStack {
Picker("Menu", selection: $selectedMenuIndex, content: {
ForEach(menuItem, id: \.self, content: { title in
Text(title)
})
})
Text("Selected menu: \(selectedMenu)")
}
}
}

Can I navigate ParentView to another View by button in ChildView?, swiftUI

I'm new to SwiftUI and struggling to move MainView to LoginView by clicking Text() in ChildView.
I tried make NavigationView and NavigationLink like this code but it's not working. It's much more complex in real code but I show only simple structure of my code to explain.
struct ContentView: View {
#State private var currViewIdx: Int = 0
var body: some View {
NavigationView {
CurrentView(currViewIdx: $currViewIdx)
}
}
}
struct CurrentView: View {
#Binding var currViewIdx: Int
var body: some View {
if self.currViewIdx == 0 {
HomeView()
.onTapGesture {
self.currViewIdx = 0
}
} else {
//SomeView.onTapGesture(self.currViewIdx = 1)
}
}
}
struct HomeView: View {
var body: some View {
NavigationLink(destination: TestView()) {
Text("Go TestView")
.contentShape(Rectangle())
}
}
}
struct TestView: View {
var body: some View {
Text("Here is Test View")
}
}
I think, the top hierarchy view, ContentView, has NavigationView and it should be changed when I click Text("Go TestView") in HomeView because it's in NavagationLink.
But the ContentView is not changed to TestView although I touch Text("Go TestView"). How to solve this problem? I considered way to add one more State value to change top level view, but it seems not good if I'll make lots of more values
Thank you

SwiftUI transition from modal sheet to regular view with Navigation Link

I'm working with SwiftUI and I have a starting page. When a user presses a button on this page, a modal sheet pops up.
In side the modal sheet, I have some code like this:
NavigationLink(destination: NextView(), tag: 2, selection: $tag) {
EmptyView()
}
and my modal sheet view is wrapped inside of a Navigation View.
When the value of tag becomes 2, the view does indeed go to NextView(), but it's also presented as a modal sheet that the user can swipe down from, and I don't want this.
I'd like to transition from a modal sheet to a regular view.
Is this possible? I've tried hiding the navigation bar, etc. but it doesn't seem to make a difference.
Any help with this matter would be appreciated.
You can do this by creating an environmentObject and bind the navigationLink destination value to the environmentObject's value then change the value of the environmentObject in the modal view.
Here is a code explaining what I mean
import SwiftUI
class NavigationManager: ObservableObject{
#Published private(set) var dest: AnyView? = nil
#Published var isActive: Bool = false
func move(to: AnyView) {
self.dest = to
self.isActive = true
}
}
struct StackOverflow6: View {
#State var showModal: Bool = false
#EnvironmentObject var navigationManager: NavigationManager
var body: some View {
NavigationView {
ZStack {
NavigationLink(destination: self.navigationManager.dest, isActive: self.$navigationManager.isActive) {
EmptyView()
}
Button(action: {
self.showModal.toggle()
}) {
Text("Show Modal")
}
}
}
.sheet(isPresented: self.$showModal) {
secondView(isPresented: self.$showModal).environmentObject(self.navigationManager)
}
}
}
struct StackOverflow6_Previews: PreviewProvider {
static var previews: some View {
StackOverflow6().environmentObject(NavigationManager())
}
}
struct secondView: View {
#EnvironmentObject var navigationManager: NavigationManager
#Binding var isPresented: Bool
#State var dest: AnyView? = nil
var body: some View {
VStack {
Text("Modal view")
Button(action: {
self.isPresented = false
self.dest = AnyView(thirdView())
}) {
Text("Press me to navigate")
}
}
.onDisappear {
// This code can run any where but I placed it in `.onDisappear` so you can see the animation
if let dest = self.dest {
self.navigationManager.move(to: dest)
}
}
}
}
struct thirdView: View {
var body: some View {
Text("3rd")
.navigationBarTitle(Text("3rd View"))
}
}
Hope this helps, if you have any questions regarding this code, please let me know.

SwiftUI - Nested NavigationLink

I have a layout that looks like this:
Layout Drawing
There is a main view which is the Feed that would be my NavigationView and then I have views inside: PostList -> Post -> PostFooter and in the PostFooter A Button that would be my NavigationLink
struct Feed: View {
var body: some View {
NavigationView {
PostList()
}
}
}
struct PostList: View {
var body: some View {
List {
ForEach(....) {
Post()
}
}
}
}
struct Post: View {
var body: some View {
PostHeader()
Image()
PostFooter()
}
}
struct PostFooter: View {
var body: some View {
NavigationLink(destination: Comment()) {
Text("comments")
}
}
}
But When when I tap on the comments, it goes to the Comment View then go back to Feed() then back to Comment() then back to Feed() and have weird behaviour.
Is there a better way to handle such a situation ?
Update
The Navigation is now working but the all Post component is Tapeable instead of just the Text in the PostFooter.
Is there any way to disable tap gesture on the cell and add multiple NavigationLink in a cell that go to different pages ?
How about programmatically active the NavigationLink, for example:
struct PostFooter: View {
#State var commentActive: Bool = false
var body: some View {
VStack{
Button("Comments") {
commentActive = true
}
NavigationLink(destination: Comment(), isActive: $commentActive) {
EmptyView()
}
}
}
}
Another benefit of above is, your NavigationLink destination View can accept #ObservedObject or #Binding for comments editing.

Resources