Send data to next view - SwiftUI - view

So I have been trying to get a simple pass of data working with SwiftUI.
Basically the below script prints out a list of items (in a HStack) I then have each one linked to our Podcast() view.
What I am trying to do is pass through the podcast name to the next view. How do I achieve
this? As all the examples are about int which I am not using am using a String.
import SwiftUI
import RemoteImage
struct ContentView: View {
#State private var showAlert = false
#State var posts: [Program] = []
var body: some View {
NavigationView {
if posts.isEmpty {
Text("Loading")
} else {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .bottom, spacing: 10) {
ForEach(posts) { post in
//return
NavigationLink(destination: Podcasts()){
RemoteImage(type: .url(URL(string:post.icon)!), errorView: { error in
Text(error.localizedDescription)
}, imageView: { image in
image
.resizable()
.renderingMode(.original)
/* .clipShape(Circle())
.shadow(radius: 10)
.overlay(Circle().stroke(Color.red, lineWidth: 5))*/
.aspectRatio(contentMode: .fit)
.frame(width:200, height:200)
}, loadingView: {
Text("Loading ...")
})
}
}
}.frame(height: 200)
}.frame(height: 200)
}
}.onAppear {
Api().getPosts { (posts) in
self.posts = posts
}
}.navigationBarTitle(Text("Home"))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The podcast View
import SwiftUI
struct Podcasts: View {
var body: some View {
NavigationView{
Text("Hello")
}.navigationBarTitle(Text("Podcast"))
}
}
struct Podcasts_Previews: PreviewProvider {
static var previews: some View {
Podcasts()
}
}

Pass post as constructor argument, like
ForEach(posts) { post in
//return
NavigationLink(destination: Podcasts(post: post)){
so now
struct Podcasts: View {
let post: Program
var body: some View {
// !! DON'T ADD SECOND NAVIGATION VIEW IN STACK
// !! - THERE MUST BE ONLY ONE
Text(post.name)
.navigationBarTitle(Text("Podcast"))
}
}

Related

Getting "Out of range error" in a preview SwiftUI

I'm learning SwiftUI and are mapping my app, but I'm having an error with one of the previews.
Hope you can help me figure it out.
Here is my model:
import Foundation
class RecipeModel: ObservableObject {
#Published var recipes = [Recipe]()
init() {
// Parse local included json data
getLocalData()
// Download remote json file and parse data
getRemoteData()
}
Then I have this on Main
import SwiftUI
#main
struct ReallyMexican: App {
var body: some Scene {
WindowGroup {
HomeView()
.environmentObject(RecipeModel())
}
}
}
And this on the view I'm getting the error:
import SwiftUI
struct RecipeSearchView: View {
var recipe:Recipe
#EnvironmentObject var model: RecipeModel
var body: some View {
VStack {
HStack {
Image(recipe.image)
.resizable()
.scaledToFit()
Text(recipe.name)
}
}
.padding()
}
}
struct RecipeSearchView_Previews: PreviewProvider {
static var previews: some View {
let model = RecipeModel()
RecipeSearchView(recipe: model.recipes[0])
}
}
And I use that view here:
import SwiftUI
struct SearchView: View {
#EnvironmentObject var model: RecipeModel
var body: some View {
VStack (alignment: .leading){
Text("Hello")
NavigationView{
List {
ForEach(model.recipes) { item in
RecipeSearchView(recipe: item)
}
}
}
}
.padding()
}
}
struct SearchView_Previews: PreviewProvider {
static var previews: some View {
SearchView()
.environmentObject(RecipeModel())
}
}
The project Build Successfully and I can run it in the simulator.
But on the RecipeSearchView, I'm getting this error:, can't figure out why 🤷‍♂️
"App Crash due to Our of Range Index”
Thanks in advance

How do I create another unique view for each device?

How do I create another view when the user chooses a device ? For each device, the view will not be the same because the information given will not be the same ( custom view )
Thank you to put me on the track.
Canvas image
struct ContentView: View {
var body: some View {
NavigationView {
List(Ios, children: \.sousMenuIos) { item in
HStack {
Image(item.image)
.resizable()
.scaledToFit()
.frame(width: 60, height: 60)
Text(item.name)
.font(.system(.title3, design: .rounded))
.bold()
}
}
.navigationTitle("Jailbreak")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
You need to wrap your list label in a NavigationLink and pass the item to the costum view that you want. here is a working example with dummy data.
struct ListItems: View {
var body: some View {
NavigationView {
List(iOS.previewData) { item in
NavigationLink(destination: CostumView(text: item.name)){
HStack {
Image(systemName: item.image)
.resizable()
.scaledToFit()
.frame(width: 60, height: 60)
Text(item.name)
.font(.system(.title3, design: .rounded))
.bold()
}
}
}
.navigationTitle("Jailbreak")
}
}
}
struct CostumView: View {
var text: String
var body: some View{
Text(text)
}
}
struct ListItems_Previews: PreviewProvider {
static var previews: some View {
ListItems()
}
}
struct iOS: Identifiable {
var id = UUID()
var image: String = ""
var name: String
static let previewData = [
iOS(image: "phone", name: "iPhone 8"),
iOS(image: "phone", name: "iPhone X")
]
}

SwiftUI for macOS - trigger sheet .onDismiss problem

In a multiplatform app I'm showing a sheet to collect a small amount of user input. On iOS, when the sheet is dismissed, the relevant .onDismiss method is called but not on macOS.
I've read that having the .onDismiss in the List can cause problems so I've attached it to the button itself with no improvement. I've also tried passing the isPresented binding through and toggling that within the sheet itself to dismiss, but again with no success.
I am employing a NavigationView but removing that makes no difference. The following simplified example demonstrates my problem. Any ideas? Should I even be using a sheet for this purpose on macOS?
I just want to make clear that I have no problem closing the sheet. The other questions I found were regarding problems closing the sheet - I can do that fine.
import SwiftUI
#main
struct SheetTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
ListView()
}
}
}
The List view.
struct ListView: View {
#State private var isPresented: Bool = false
var body: some View {
VStack {
Text("Patterns").font(.title)
Button(action: {
isPresented = true
}, label: {
Text("Add")
})
.sheet(isPresented: $isPresented, onDismiss: {
doSomethingAfter()
}) {
TestSheetView()
}
List {
Text("Bingo")
Text("Bongo")
Text("Banjo")
}
.onAppear(perform: {
doSomethingBefore()
})
}
}
func doSomethingBefore() {
print("Johnny")
}
func doSomethingAfter() {
print("Cash")
}
}
This is the sheet view.
struct TestSheetView: View {
#Environment(\.presentationMode) var presentationMode
#State private var name = ""
var body: some View {
Form {
TextField("Enter name", text: $name)
.padding()
HStack {
Spacer()
Button("Save") {
presentationMode.wrappedValue.dismiss()
}
Spacer()
}
}
.frame(minWidth: 300, minHeight: 300)
.navigationTitle("Jedward")
}
}
Bad issue.. you are right. OnDismiss is not called. Here is a workaround with Proxybinding
var body: some View {
VStack {
Text("Patterns").font(.title)
Button(action: {
isPresented = true
}, label: {
Text("Add")
})
List {
Text("Bingo")
Text("Bongo")
Text("Banjo")
}
.onAppear(perform: {
doSomethingBefore()
})
}
.sheet(isPresented: Binding<Bool>(
get: {
isPresented
}, set: {
isPresented = $0
if !$0 {
doSomethingAfter()
}
})) {
TestSheetView()
}
}

why is a passed parameter displaying the previous content of an EnvironmentObject

this is a Macos app where the parsclass is setup in a previous view that contains the YardageRowView below. That previous view is responsible for changing the contents of the parsclass. This is working is other views that use a NavigationLink to display the views.
When the parsclass is changed, this view is refreshed, but the previous value is put in the text field on the holeValueTestView.
I cannot comprehend how the value is not being passed into the holeValueTestView correctly
This is a view shown as a .sheet, and if I dismiss it and display it again, everything is fine.
if you create a macOS project called YardageSample and replace the ContentView.swift and YardageSampleApp.swift with the two files below, you can see that the display in red changes and the black numbers do not change until you click Done and redisplay the .sheet
//
// YardageSampleApp.swift
// YardageSample
//
// Created by Brian Quick on 2021-04-12.
//
import SwiftUI
#main
struct YardageSampleApp: App {
#StateObject var parsclass = parsClass()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(parsclass)
}
}
}
//
// ContentView.swift
// YardageSample
//
// Created by Brian Quick on 2021-04-12.
//
import SwiftUI
struct ContentView: View {
#StateObject var parsclass = parsClass()
enum ActiveSheet : String , Identifiable {
case CourseMaintenance
var id: String {
return self.rawValue
}
}
#State var activeSheet : ActiveSheet? = nil
var body: some View {
Button(action: {
self.activeSheet = .CourseMaintenance
}) {
Text("Course Maintenance")
}
.sheet(item: $activeSheet) { sheet in
switch sheet {
case .CourseMaintenance:
CourseMaintenance()
}
}.frame(width: 200, height: 200, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
}
}
class parsClass: ObservableObject {
#Published var pars = [parsRec]()
init() {
self.pars = [parsRec]()
self.pars.append(parsRec())
}
func create(newpars: [parsRec]) {
pars.removeAll()
pars = newpars
}
}
class parsRec: Identifiable, Codable {
var id = UUID()
var Hole = 1
var Yardage = 1
}
struct CourseMaintenance: View {
#EnvironmentObject var parsclass: parsClass
#Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
Button(action: {presentationMode.wrappedValue.dismiss()}, label: {
Text("Done")
})
Button(action: {switchScores(number: 1)}, label: {
Text("Button 1")
})
Button(action: {switchScores(number: 2)}, label: {
Text("Button 2")
})
Button(action: {switchScores(number: 3)}, label: {
Text("Button 3")
})
CourseDetail().environmentObject(parsclass)
}.frame(width: 400, height: 400, alignment: .center)
}
func switchScores(number: Int) {
var newparRecs = [parsRec]()
for i in 0..<17 {
let myrec = parsRec()
myrec.Hole = i
myrec.Yardage = number
newparRecs.append(myrec)
}
parsclass.create(newpars: newparRecs)
}
}
struct CourseDetail: View {
#EnvironmentObject var parsclass: parsClass
var body: some View {
HStack(spacing: 0) {
ForEach(parsclass.pars.indices, id: \.self) { indice in
// this displays the previous value
holeValueTestView(value: String(parsclass.pars[indice].Yardage))
// this displays the correct value after parsclass has changed
Text(String(parsclass.pars[indice].Yardage))
.foregroundColor(.red)
}
}
}
}
struct holeValueTestView: View {
#State var value: String
var body: some View {
//TextField(String(value), text: $value)
Text(value)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
There are a couple of issues going on:
You have multiple instances of parsClass. One is defined in YardageSampleApp and passed into the view hierarchy as a #EnvironmentObject. The second is defined in ContentView as a #StateObject. Make sure you're only using one.
On holeValueTestView, you defined value as a #State variable. That gets set initially when the view is created by its parent and then it maintains its own state. So, when the environmentObject changed, because it was in charge of its own state at this point, it didn't update the value. You can simply remove #State and see the behavior that you want.
struct ContentView: View {
#EnvironmentObject var parsclass : parsClass //<-- Here
enum ActiveSheet : String , Identifiable {
case CourseMaintenance
var id: String {
return self.rawValue
}
}
#State var activeSheet : ActiveSheet? = nil
var body: some View {
Button(action: {
self.activeSheet = .CourseMaintenance
}) {
Text("Course Maintenance")
}
.sheet(item: $activeSheet) { sheet in
switch sheet {
case .CourseMaintenance:
CourseMaintenance()
}
}.frame(width: 200, height: 200, alignment: .center)
}
}
struct holeValueTestView: View {
var value: String //<-- Here
var body: some View {
Text(value)
}
}
As a side note:
In Swift, normally type names are capitalized. If you want to write idiomatic Swift, you would change your parsClass to ParsClass for example.

Why cant I see my for Each loop in the preview view in XCode

I am making a to do list to learn about Core data. I noticed when I add a ForEach loop to read my core data entries, it stops loading the view and the app crashes. If I replace the ForEach loop with a Text("Hi"), it all works. However the app runs perfectly in simulator.
import SwiftUI
struct ContentView: View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(fetchRequest: ToDoItem.getAllToDo()) var toDoItems:FetchedResults<ToDoItem>
#State private var newTodoItem = ""
#State private var selection = 0
var body: some View {
NavigationView {
List {
Section (header: Text("Whats... next")) {
HStack{
TextField("New Item", text: self.$newTodoItem)
Button(action: {
let toDoItem = ToDoItem(context: self.managedObjectContext)
toDoItem.title = self.newTodoItem
toDoItem.createdAt = Date()
do {
try self.managedObjectContext.save()
}catch {
print(error)
}
self.newTodoItem = ""
}){
Image(systemName: "plus.circle.fill")
.foregroundColor(.green)
.imageScale(.large)
}
}
}.font(.headline)
Section(header: Text("To Do's")){
ForEach(self.toDoItems) {todoItem in
HStack{
VStack(alignment: .leading){
Text(todoItem.title!)
.font(.headline)
Text("\(todoItem.createdAt!)")
.font(.caption)
}
}
}.onDelete {indexSet in
let deleteItem = self.toDoItems[indexSet.first!]
self.managedObjectContext.delete(deleteItem)
do {
try self.managedObjectContext.save()
}catch {
print(error)
}
}
}
}
.navigationBarTitle(Text("My List"))
.navigationBarItems(trailing: EditButton())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Any idea what the issue is?

Resources