Memory Error in SwiftUI that goes way over my head - ios

I'm learning SwiftUI over break for fun over youtube, trying to build a fun little app to learn stuff and i'm attempting to make a custom tab bar at the bottom to control views, and have objects (i made a Person object) that i owuld like to be able to access and modify throughout all my views. From what I can tell, I've achieved that, as it runs perfectly well as I expect it to on the Simulator, but when I try to run on my iPhone I get the error "Thread 1: EXC_BAD_ACCESS (code=257, address=0x481bdfee88082008)"
I'm not familiar with reading memory addresses, i'm just trying to screw around with building apps for fun with my downtime
myTestApp.swift
import SwiftUI
#main
struct myTestApp: App {
var testPerson = Person(name: "Stinky", age: 69)
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(testPerson)
}
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
#State var selectedIndex = 0
let icons = [
"house", "person", "doc", "dice", "gear"
]
let tabNames = [
"Home", "People", "Overview", "Activities", "Settings"
]
var body: some View {
VStack {
ZStack {
switch selectedIndex {
case 0: HomeView()
case 1: PeopleView()
case 2: OverView()
case 3: ActivitiesView()
case 4: SettingsView()
default: HomeView()
}
}
Divider()
.frame(height: 2.0)
HStack {
ForEach(0..<5, id: \.self) { i in
Spacer()
VStack {
Image(systemName: icons[i])
.frame(height: 20.0)
.font(.system(size:23))
.foregroundColor(self.selectedIndex == i ? Color("SelectedColor") : Color("AccentColor"))
Text("\(tabNames[i])")
.font(.system(size:10, weight: .medium, design: .default))
.foregroundColor(self.selectedIndex == i ? Color("SelectedColor") : Color("AccentColor"))
.padding(.top, 1.0)
}
.onTapGesture {
selectedIndex = i
}
.frame(width: 70.0, height: 60.0)
Spacer()
}
}
.frame(height: 41.0)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Views.swift
import SwiftUI
struct HomeView: View {
#EnvironmentObject var testPerson: Person
var body: some View {
NavigationView {
VStack {
ScrollView {
VStack {
Text(testPerson.name + " \(testPerson.age)")
ForEach(0..<5) { i in
Text("The Train Has Arrived!")
.padding(.all, 3.0)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.cornerRadius(20)
.background(Color.gray)
}
Button(action: {
testPerson.age += 1
}, label: {
Text("Age: \(testPerson.age)")
.font(.system(size: 20))
.foregroundColor(.white)
.frame(maxWidth: .infinity)
})
.frame(maxWidth: .infinity, maxHeight: 50)
.foregroundColor(.blue)
.background(.blue)
.cornerRadius(15)
.padding(.horizontal)
}
.navigationTitle("Age: \(testPerson.age)")
}
}
}
struct PeopleView: View {
#EnvironmentObject var testPerson: Person
#State var newPersonCreation = false
var body: some View {
NavigationView {
VStack {
ZStack {
Spacer().fullScreenCover(isPresented: $newPersonCreation, content: {
Button("ass", action: {
self.newPersonCreation.toggle()
})
})
}
Text("Age: \(testPerson.age)")
}
.navigationTitle("People")
.toolbar {
Button(action: {
self.newPersonCreation.toggle()
}, label: {
Image(systemName: "plus")
.foregroundColor(.white)
})
}
}
}
}
struct OverView: View {
var body: some View {
NavigationView {
VStack {
}
.navigationTitle("Overview")
}
}
}
struct ActivitiesView : View {
var body: some View {
NavigationView {
VStack {
}
.navigationTitle("Activities")
}
}
}
struct SettingsView: View {
var body: some View {
NavigationView {
VStack {
}
.navigationTitle("Settings")
}
}
}
struct Previews_Views_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Person.swift
import SwiftUI
class Person: ObservableObject {
#Published var name = "Anonymous"
#Published var age = 1
init(name: String, age: Int) {
self.name = name
self.age = age
}
}

Related

Can't refresh view after changing published variable

I have three files: ContentView (main file), HomeView, ConfigView
I wish I could change one string that is located in HomeView by pressing a button in ConfigView, but I can't do it.
Edit: I realized the problem is because I'm storing objects inside the DadosTimes class. I tried to store a simple string and it worked. How can I make it work even using an object?
ContentView file:
import SwiftUI
extension View {
func inExpandingRectangle() -> some View {
ZStack {
Rectangle()
.fill(Color.clear)
self
}
}
}
//Here i have a declaration for a custom button
class Equipe {
var nome: String
var pontos: Int
var vitorias: Int
init(nome: String) {
self.nome = nome
self.pontos = 0
self.vitorias = 0
}
func addPontos(_qtd: Int){
self.pontos += _qtd
}
}
struct ContentView: View {
#EnvironmentObject var InfoJogo: DadosTimes
var body: some View {
TabView{
HomeView()
.tabItem({
Image(systemName: "house")
Text("Placar")
})
ConfigView()
.tabItem({
Image(systemName: "gear")
Text("Configurações")
})
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
class DadosTimes: ObservableObject{
#Published var time1 = Equipe(nome: "Nós")
#Published var time2 = Equipe(nome: "Eles")
}
HomeView file:
import SwiftUI
struct HomeView: View {
#EnvironmentObject var InfoJogo: DadosTimes
var body: some View {
ZStack{
Color("FundoVerde")
.ignoresSafeArea()
VStack {
HStack{
VStack(spacing: 0){
TextField("", text: $InfoJogo.time1.nome)
.foregroundColor(Color.black)
.font(.system(size: 50))
Text(String(InfoJogo.time1.pontos))
.foregroundColor(Color.black)
.font(.system(size: 85))
Text(String(InfoJogo.time1.vitorias) + " Vitórias")
.foregroundColor(Color(.darkGray))
.font(.system(size: 17))
BotaoPrimario(title: "+", size: 50, action: {
InfoJogo.time1.addPontos(_qtd: 1)
verificaGanhador()
})
}
.inExpandingRectangle()
.fixedSize(horizontal: false, vertical: true)
VStack(spacing: 0){
Text(InfoJogo.time2.nome)
.foregroundColor(Color.black)
.font(.system(size: 50))
Text(String(InfoJogo.time2.pontos))
.foregroundColor(Color.black)
.font(.system(size: 85))
Text(String(InfoJogo.time2.vitorias) + " Vitórias")
.foregroundColor(Color(.darkGray))
.font(.system(size: 17))
BotaoPrimario(title: "+", size: 50, action: {
InfoJogo.time2.addPontos(_qtd: 1)
verificaGanhador()
})
}
.inExpandingRectangle()
.fixedSize(horizontal: false, vertical: true)
}
.padding(.vertical, 50)
.frame(maxWidth: .infinity)
.background(Rectangle()
.foregroundColor(.white)
.cornerRadius(15)
.shadow(radius: 15)
)
}
.padding(.horizontal, 20)
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
ConfigView file
import SwiftUI
struct ConfigView: View {
#EnvironmentObject var InfoJogo: DadosTimes
var body: some View {
ZStack{
Color("FundoVerde")
.ignoresSafeArea()
VStack{
Button("Mudar Nome"){
InfoJogo.time1.nome = "oiii"
}
TextField("", text: $InfoJogo.time1.nome)
}
}
}
}
struct ConfigView_Previews: PreviewProvider {
static var previews: some View {
ConfigView()
}
}
I have already logged in console the InfoJogo.time1.nome variable and it changes in the memory. It's not updating on the screen. What should I do? I've already looked for help everywhere but I couldn't find the solution.
Sorry if it's a basic question, but I've started learning swift yesterday ;)
#Paulw11 provided a perfect description why the change doesn't trigger an update.
What you can do is trigger the update manually with objectWillChange.send().
In your button action do:
BotaoPrimario(title: "+", size: 50, action: {
infoJogo.objectWillChange.send() // here
infoJogo.time2.addPontos(_qtd: 1)
verificaGanhador()
})

Data is not saved or load because of i am call the data from another pages

Model
I have different variables in Travel.
import Foundation
struct Travel: Identifiable, Codable {
var id = UUID()
var name: String
var date = Date()
var location: String
var isFav: Bool
var score: Float
var comment: String
}
View model
I load and save data with UserDefaults. Always its work but in this model not.
import Foundation
class TravelViewModel: ObservableObject {
#Published var travelList = [Travel] ()
#Published var travelled = 0
init(){
load()
}
func load() {
guard let data = UserDefaults.standard.data(forKey: "travelList"),
let savedTravels = try? JSONDecoder().decode([Travel].self, from: data) else { travelList = []; return }
travelList = savedTravels
}
func save() {
do {
let data = try JSONEncoder().encode(travelList)
UserDefaults.standard.set(data, forKey: "travelList")
} catch {
print(error)
}
}
}
Adding Item View
I have addItems func and use this func in addItem button.
import SwiftUI
struct AddTravelView: View {
#StateObject var VM = TravelViewModel()
#State var name = ""
#State var location = ""
#State var isFav = false
#State var score = 0.00
#State var comment = ""
var body: some View {
VStack {
ZStack {
Rectangle()
.fill(.black.opacity(0.2))
.cornerRadius(20)
.frame(width: 350, height: 350)
VStack{
HStack {
Text("Name:")
.font(.system(size: 16 , weight: .medium))
TextField("Type..", text: $name)
}
HStack {
Text("Location:")
.font(.system(size: 16 , weight: .medium))
TextField("Type..", text: $location)
}
HStack {
Text("Score: \(Int(score))")
.font(.system(size: 16 , weight: .medium))
Slider(value: $score, in: 0...10, step: 1)
}
Spacer()
ZStack(alignment: .topLeading) {
Rectangle()
.fill(.white)
.cornerRadius(20)
.frame(height: 200)
VStack(alignment: .leading) {
TextField("Comment...", text: $comment, axis: .vertical)
}.padding()
}
}
.padding()
.frame(width: 300, height: 200)
}
Button {
addTravel()
} label: {
ZStack{
Rectangle()
.fill(.black.opacity(0.2))
.cornerRadius(20)
.frame(width: 350 , height: 100)
Text("ADD TRAVEL")
.font(.system(size: 25, weight: .medium, design: .monospaced))
.foregroundColor(.black)
}.padding()
}
}
}
func addTravel(){
VM.travelList.append(Travel(name: name, location: location, isFav: isFav, score: Float(score), comment: comment))
}
}
struct AddTravelView_Previews: PreviewProvider {
static var previews: some View {
AddTravelView()
}
}
Recent Adds view
In this page i wanna see Items i add before
import SwiftUI
struct RecentTravels: View {
#StateObject var VM = TravelViewModel()
var body: some View {
VStack {
ForEach(VM.travelList) {Travel in
HStack{
Image(systemName: "questionmark")
.frame(width: 50, height: 50)
.padding()
.overlay {
RoundedRectangle(cornerRadius: 5)
.stroke(.black, lineWidth: 2)
}
VStack(alignment: .leading) {
Text(Travel.name)
.font(.subheadline)
.bold()
.lineLimit(1)
Text("\(Travel.date)")
.font(.footnote)
.opacity(0.9)
.lineLimit(1)
}
Spacer()
VStack {
Image(systemName: "heart")
Spacer()
Text("\(Travel.score)")
}
.frame(height: 50)
.font(.system(size: 22))
}
}
}
}
}
struct RecentTravels_Previews: PreviewProvider {
static var previews: some View {
RecentTravels()
}
}
And ContentView
and calling those 2 views in ContentView.
import SwiftUI
struct ContentView: View {
#StateObject var VM = TravelViewModel()
var body: some View {
VStack {
AddTravelView()
RecentTravels()
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
When i write the all code in only ContentView is work but when i call another pages its not work. Usually it was work when i presss add item button and restart app but now its nothing. Its not work even restart the app.
The problem you have is that you have multiple VM, that have no relations to each other. You must not have more than one source of truth
in #StateObject var VM = TravelViewModel().
Keep the one you have in ContentView,
and pass it to the other view like this:
VStack {....}.environmentObject(VM).
In your AddTravelView and RecentTravels ,
add #EnvironmentObject var VM: TravelViewModel instead of #StateObject var VM = TravelViewModel().
Have a look at this link, it gives you some good examples of how to manage data in your app:
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

Q: Adding dismiss button without #Binding to Launch Screen

I am working on an app where the Welcome Screen should be dismissed with a button but I can't figure out how to toggle the welcome screen. I tried to use #Binding and #AppStorage but no success within the existing UserDefaults.
Like an onboarding, the launch screen should only show screen once when the app is first opened.
Thanks for the help!
extension UserDefaults {
var welcomeScreenShown: Bool {
get {
return (UserDefaults.standard.value(forKey: "welcomeScreenShown") as? Bool) ?? false
}
set {
UserDefaults.standard.setValue(newValue, forKey: "welcomeScreenShown")
}
}
}
struct ContentView: View {
var body: some View {
if UserDefaults.standard.welcomeScreenShown {
HomeView()
} else {
WelcomeScreen()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct WelcomeScreen: View {
#AppStorage("welcomeScreenShown")
var welcomeScreenShown: Bool = false
var body: some View {
VStack(alignment: .leading) {
Text("Welcome to")
.font(.system(size: 50, weight: .bold))
.foregroundColor(.black)
.offset(y: -7)
Text("App")
.font(.system(size: 50, weight: .heavy))
.foregroundColor(.black)
.offset(y: -14)
Button(action: {}, label: {
Text("Get Started")
})
.font(.system(size: 18, weight: .bold))
.foregroundColor(.white)
.padding(.horizontal, 25)
.padding(.vertical, 10)
.background(Color.blue)
.clipShape(Capsule())
.animation(.easeInOut(duration: 0.25))
}.frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.white)
.onAppear(perform: { UserDefaults.standard.welcomeScreenShown = true
})
}
}
struct WelcomeScreen_Previews: PreviewProvider {
static var previews: some View {
WelcomeScreen()
}
You can use #AppStorage at the top level and then pass it with a #Binding to the WelcomeScreen:
struct ContentView: View {
#AppStorage("welcomeScreenShown")
var welcomeScreenShown: Bool = false
var body: some View {
if welcomeScreenShown {
HomeView()
} else {
WelcomeScreen(welcomeScreenShown: $welcomeScreenShown)
}
}
}
struct HomeView : View {
var body: some View {
Text("Home")
}
}
struct WelcomeScreen: View {
#Binding var welcomeScreenShown : Bool
var body: some View {
Text("Welcome")
Button(action: {
welcomeScreenShown = true
}) {
Text("Done")
}
}
}
struct WelcomeScreen_Previews: PreviewProvider {
static var previews: some View {
WelcomeScreen(welcomeScreenShown: .constant(false))
}
}
Another option is to use #AppStorage on both screens, but it seems redundant.
With #AppStroage, there doesn't seem to be a need for your first extension.

SwiftUI list showing an array is not updated

In my app in SwiftUI, there is a list showing all items in an array. When I click on one item, its details are shown and can be modified. Those changes are stored in the array, but when I go back to the list view, the changes are only reflected after I made a change to that list array, like adding or moving an item. Can I make this list refresh when it re-appears?
My main view looks like this:
import SwiftUI
struct ContentView: View {
#State var items: [Item]
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: ItemDetailView(items: self.$items, index: self.items.firstIndex(of: item)!)) {
HStack {
VStack(alignment: .leading) {
Text(item.name).font(.title)
if item.serialNumber != nil {
Text(item.serialNumber!)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
Spacer()
Text("\(item.valueInDollars)$").font(.title)
}
}
}
.onDelete(perform: delete)
.onMove(perform: move)
Text("No more items!")
}
.navigationBarTitle(Text("Homepwner"), displayMode: .inline)
.navigationBarItems(leading: EditButton(), trailing: Button(action: addItem) { Text("Add") })
}
}
//... functions
}
The detail view looks like this:
import SwiftUI
struct ItemDetailView: View {
#Binding var items: [Item]
let index: Int
var body: some View {
VStack {
VStack(alignment: .leading) {
HStack {
Text("Name: ")
TextField("Item Name", text: $items[index].name)
}
//... more TextFields
}
.padding(.all, 8.0)
VStack(alignment: .center) {
//... button
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}.navigationBarTitle(items[index].name)
}
}
The class Item is Identifiable and Equatable and only holds the necessary members, the class ItemStore only holds an array of Items.
i just tried this (had to enhance code so it was compilable at all) and it works:
import SwiftUI
struct Item : Equatable, Identifiable, Hashable {
var id = UUID()
var name: String
var serialNumber : String?
var valueInDollars : Int = 5
}
struct ContentView: View {
#State var items: [Item] = [Item(name: "hi"), Item(name: "ho")]
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) { item in
NavigationLink(destination: ItemDetailView(items: self.$items, index: self.items.firstIndex(of: item)!)) {
HStack {
VStack(alignment: .leading) {
Text(item.name).font(.title)
if item.serialNumber != nil {
Text(item.serialNumber!)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
Spacer()
Text("\(item.valueInDollars)$").font(.title)
}
}
}
// .onDelete(perform: delete)
// .onMove(perform: move)
Text("No more items!")
}
.navigationBarTitle(Text("Homepwner"), displayMode: .inline)
// .navigationBarItems(leading: EditButton(), trailing: Button(action: addItem) { Text("Add") })
}
}
//... functions
}
struct ItemDetailView: View {
#Binding var items: [Item]
let index: Int
var body: some View {
VStack {
VStack(alignment: .leading) {
HStack {
Text("Name: ")
TextField("Item Name", text: $items[index].name)
}
//... more TextFields
}
.padding(.all, 8.0)
VStack(alignment: .center) {
//... button
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}.navigationBarTitle(items[index].name)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

NavigationView and NavigationLink on button click in SwiftUI?

I am trying to push from login view to detail view but not able to make it.even navigation bar is not showing in login view. How to push on button click in SwiftUI? How to use NavigationLink on button click?
var body: some View {
NavigationView {
VStack(alignment: .leading) {
Text("Let's get you signed in.")
.bold()
.font(.system(size: 40))
.multilineTextAlignment(.leading)
.frame(width: 300, height: 100, alignment: .topLeading)
.padding(Edge.Set.bottom, 50)
Text("Email address:")
.font(.headline)
TextField("Email", text: $email)
.frame(height:44)
.accentColor(Color.white)
.background(Color(UIColor.darkGray))
.cornerRadius(4.0)
Text("Password:")
.font(.headline)
SecureField("Password", text: $password)
.frame(height:44)
.accentColor(Color.white)
.background(Color(UIColor.darkGray))
.cornerRadius(4.0)
Button(action: {
print("login tapped")
}) {
HStack {
Spacer()
Text("Login").foregroundColor(Color.white).bold()
Spacer()
}
}
.accentColor(Color.black)
.padding()
.background(Color(UIColor.darkGray))
.cornerRadius(4.0)
.padding(Edge.Set.vertical, 20)
}
.padding(.horizontal,30)
}
.navigationBarTitle(Text("Login"))
}
To fix your issue you need to bind and manage tag with NavigationLink, So create one state inside you view as follow, just add above body.
#State var selection: Int? = nil
Then update your button code as follow to add NavigationLink
NavigationLink(destination: Text("Test"), tag: 1, selection: $selection) {
Button(action: {
print("login tapped")
self.selection = 1
}) {
HStack {
Spacer()
Text("Login").foregroundColor(Color.white).bold()
Spacer()
}
}
.accentColor(Color.black)
.padding()
.background(Color(UIColor.darkGray))
.cornerRadius(4.0)
.padding(Edge.Set.vertical, 20)
}
Meaning is, when selection and NavigationLink tag value will match then navigation will be occurs.
I hope this will help you.
iOS 16+
Note: Below is a simplified example of how to present a new view. For a more advanced generic example please see this answer.
In iOS 16 we can access the NavigationStack and NavigationPath.
Usage #1
A new view is activated by a simple NavigationLink:
struct ContentView: View {
var body: some View {
NavigationStack {
NavigationLink(value: "NewView") {
Text("Show NewView")
}
.navigationDestination(for: String.self) { view in
if view == "NewView" {
Text("This is NewView")
}
}
}
}
}
Usage #2
A new view is activated by a standard Button:
struct ContentView: View {
#State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
Button {
path.append("NewView")
} label: {
Text("Show NewView")
}
.navigationDestination(for: String.self) { view in
if view == "NewView" {
Text("This is NewView")
}
}
}
}
}
Usage #3
A new view is activated programmatically:
struct ContentView: View {
#State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
Text("Content View")
.navigationDestination(for: String.self) { view in
if view == "NewView" {
Text("This is NewView")
}
}
}
.onAppear {
path.append("NewView")
}
}
}
iOS 13+
The accepted answer uses NavigationLink(destination:tag:selection:) which is correct.
However, for a simple view with just one NavigationLink you can use a simpler variant: NavigationLink(destination:isActive:)
Usage #1
NavigationLink is activated by a standard Button:
struct ContentView: View {
#State var isLinkActive = false
var body: some View {
NavigationView {
VStack(alignment: .leading) {
...
NavigationLink(destination: Text("OtherView"), isActive: $isLinkActive) {
Button(action: {
self.isLinkActive = true
}) {
Text("Login")
}
}
}
.navigationBarTitle(Text("Login"))
}
}
}
Usage #2
NavigationLink is hidden and activated by a standard Button:
struct ContentView: View {
#State var isLinkActive = false
var body: some View {
NavigationView {
VStack(alignment: .leading) {
...
Button(action: {
self.isLinkActive = true
}) {
Text("Login")
}
}
.navigationBarTitle(Text("Login"))
.background(
NavigationLink(destination: Text("OtherView"), isActive: $isLinkActive) {
EmptyView()
}
.hidden()
)
}
}
}
Usage #3
NavigationLink is hidden and activated programmatically:
struct ContentView: View {
#State var isLinkActive = false
var body: some View {
NavigationView {
VStack(alignment: .leading) {
...
}
.navigationBarTitle(Text("Login"))
.background(
NavigationLink(destination: Text("OtherView"), isActive: $isLinkActive) {
EmptyView()
}
.hidden()
)
}
.onAppear {
self.isLinkActive = true
}
}
}
Here is a GitHub repository with different SwiftUI extensions that makes navigation easier.
Another approach:
SceneDelegate
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: BaseView().environmentObject(ViewRouter()))
self.window = window
window.makeKeyAndVisible()
}
BaseView
import SwiftUI
struct BaseView : View {
#EnvironmentObject var viewRouter: ViewRouter
var body: some View {
VStack {
if viewRouter.currentPage == "view1" {
FirstView()
} else if viewRouter.currentPage == "view2" {
SecondView()
.transition(.scale)
}
}
}
}
#if DEBUG
struct MotherView_Previews : PreviewProvider {
static var previews: some View {
BaseView().environmentObject(ViewRouter())
}
}
#endif
ViewRouter
import Foundation
import Combine
import SwiftUI
class ViewRouter: ObservableObject {
let objectWillChange = PassthroughSubject<ViewRouter,Never>()
var currentPage: String = "view1" {
didSet {
withAnimation() {
objectWillChange.send(self)
}
}
}
}
FirstView
import SwiftUI
struct FirstView : View {
#EnvironmentObject var viewRouter: ViewRouter
var body: some View {
VStack {
Button(action: {self.viewRouter.currentPage = "view2"}) {
NextButtonContent()
}
}
}
}
#if DEBUG
struct FirstView_Previews : PreviewProvider {
static var previews: some View {
FirstView().environmentObject(ViewRouter())
}
}
#endif
struct NextButtonContent : View {
var body: some View {
return Text("Next")
.foregroundColor(.white)
.frame(width: 200, height: 50)
.background(Color.blue)
.cornerRadius(15)
.padding(.top, 50)
}
}
SecondView
import SwiftUI
struct SecondView : View {
#EnvironmentObject var viewRouter: ViewRouter
var body: some View {
VStack {
Spacer(minLength: 50.0)
Button(action: {self.viewRouter.currentPage = "view1"}) {
BackButtonContent()
}
}
}
}
#if DEBUG
struct SecondView_Previews : PreviewProvider {
static var previews: some View {
SecondView().environmentObject(ViewRouter())
}
}
#endif
struct BackButtonContent : View {
var body: some View {
return Text("Back")
.foregroundColor(.white)
.frame(width: 200, height: 50)
.background(Color.blue)
.cornerRadius(15)
.padding(.top, 50)
}
}
Hope this helps!
Simplest and most effective solution is :
NavigationLink(destination:ScoresTableView()) {
Text("Scores")
}.navigationBarHidden(true)
.frame(width: 90, height: 45, alignment: .center)
.foregroundColor(.white)
.background(LinearGradient(gradient: Gradient(colors: [Color.red, Color.blue]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(10)
.contentShape(Rectangle())
.padding(EdgeInsets(top: 16, leading: UIScreen.main.bounds.size.width - 110 , bottom: 16, trailing: 20))
ScoresTableView is the destination view.
In my opinion a cleaner way for iOS 16+ is using a state bool to present the view.
struct ButtonNavigationView: View {
#State private var isShowingSecondView : Bool = false
var body: some View {
NavigationStack {
VStack{
Button(action:{isShowingSecondView = true} ){
Text("Show second view")
}
}.navigationDestination(isPresented: $isShowingSecondView) {
Text("SecondView")
}
}
}
}
I think above answers are nice, but simpler way should be:
NavigationLink {
TargetView()
} label: {
Text("Click to go")
}

Resources