How to access Picker View results from other File?

Hi All ,

I am discovering Xcode , I used to program small devices ( HP48, Psion … ) in the 80-90’s and stopped 20 years ago . Step is huge to iPhone . I don’t want to program big app with big data , just small ones for my everyday life . Swift stay classic , no problem , but adding Views is hard to understand .
My problem : I staid 2 days blocked , looking through Google , I can’t access the View data ( indexView1 or String selected from the Picker ) , View can access all variables from File , but opposite is not possible . I extracted very few lines from my program to show my issue , I would like to import IndexView1 to the file and re-export it as display in the View .

What am I missing ? :hugs:

Probably very easy for you , but a huge issue for me .

Thanks to read me , apologize for my English ( as a French ) :pray:


@Christophe

Hi Christophe,

Welcome to the Code Crew community.

So that those of us who may be of assistance to you are able to do some testing, can you post your code as text rather than a screenshot.

Copy and paste your code in a reply and format it by selecting the code you have just pasted in and then tap the menu icon above that looks like this </> This will format the code nicely and you will note that for multiple lines of code you will see three back-ticks above the code like this ``` and 3 back-ticks below the code like this ```. So if you were to type in those 3 back-ticks on a new line yourself and then paste your code immediately below it and then on a new line type another 3 back-ticks you will achieve the same thing.

I have tried to re-create your example but I have included the variables that you declared in the other file directly in the ContentView.

struct ContentView: View {
    @State var index = 15
    let diaphs = [1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.5, 2.8, 3.2, 3.6, 4.0, 4.5, 5.0, 5.6, 6.4, 7.1, 8.0, 9.0, 10.0, 11.0, 13.0, 14.0, 16.0, 18.0, 20.0, 22.0, 26.0, 29.0, 32.0]
    var displayForTest = "Display for test"

    var body: some View {
        VStack {
            Spacer()
            Picker("Diaphragme", selection: $index) {
                ForEach(0...diaphs.count - 1, id: \.self) { index in
                    Text("f/" + String(format: "%.1f", diaphs[index]))
                }
            }
            .frame(width: 50)
            .pickerStyle(.wheel)
            .compositingGroup()
            .clipped()

            Text("Index: \(index)")
                .padding()

            Spacer()
            Text(displayForTest)
        }
    }
}

Would I be correct in assuming that this is related to Photography?

Thank you Chris for your quick reply and to provide the code in text format :wink:

The aim is how can I , from the File , access the results of the Picker View ( eventually aperture , speed and sensitivity ).
File is the place I would like to put all lignes of calculation I already have made on Swift Playgrounds

Yes , the app is supposed to help photography ( calculate index of illumination , then help changing shooting parameters as well as ND filters ) .

Regards / Christophe

For test :slightly_smiling_face:

//
//  ContentView.swift
//  New
//
//  Created by Christophe NOBER on 17/02/2022.
//

import SwiftUI

struct ContentView: View {
    
    @State var indexView1 = 15
    
    var body: some View {
        VStack {
            Spacer()
            
    Picker("Diaphragme", selection: $indexView1)
        {
            ForEach(0..<Diaphs.count)
            {
                index in Text( "f/" + String(format:"%.1f", Diaphs[index] ))
            }
        
        }
        .frame(width: 50)
        .pickerStyle(WheelPickerStyle())
        .compositingGroup()
        .clipped()
        Text("Index:\(indexView1)")
            
            Spacer()
            Text( "displayForTest" )
            Spacer()
            
        }
        
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
//

// File.swift

// New

//

// Created by Christophe NOBER on 17/02/2022.

//

**import** Foundation

**import** SwiftUI

**let** Diaphs = [1.0, 1.2,1.4,1.6,1.8,2.0,2.2,2.5,2.8,3.2,3.6,4.0,4.5,5.0,5.6,6.4,7.1, 8.0,9.0,10.0,11.0,13.0,14.0,16.0,18.0,20.0,22.0,26.0,29.0,32.0]

**var** displayForTest = " Display for test "

// print ( Diaphs )

// print ( indexView1 )

You really don’t. SwiftUI Views aren’t intended to be used like that. They are descriptions of how something should look onscreen and are created and destroyed as needed by the system. They don’t hang around for you to reach in and grab values from.

You should instead be working with model and/or view model objects to store the data that you want to access from elsewhere. Only use the View to describe how your UI looks. So in this particular case, use the View to present a Picker so the user can change the selected data but use a model and/or view model to actually store that selection.

Thank you Roosterboy for your replay .

I understand I have to change the way I organise exchange data/view .

I understand views can’t treat any data as well as calculations but only display them , only models or view models can .

I’ll try :hugs:

Hope I won’t waste as much time as I did .

Regards

Christophe

Sorry , I am back :smirk:

I still don’t understand why ContentView can’t see the func CalculsIL ligne 39 in the File :smirk:

//
//  ContentView.swift
//  New
//
//  Created by Christophe NOBER on 17/02/2022.
//

import SwiftUI

struct ContentView: View {
    
    @State var indexView1 = 15
    @State var indexView2 = 20
    @State var indexView3 = 3
    
    var body: some View {
        VStack {
            LazyHStack {
            
            Spacer()
            
            PickerUIView(index: $indexView1, List: Diaphs, Titre: "Aperture" )
                    .frame(width: 70.0, height: 300.0)
                .clipped()
            
            
            Spacer()
            
            PickerUIView(index: $indexView2, List: Vitesses, Titre: "Speed" )
                    .frame(width: 70.0, height: 300.0).clipped()
            
            Spacer()
            
            PickerUIView(index: $indexView3, List: ISOs, Titre: "Iso" ).frame(width: 70.0, height: 300.0).clipped()
            
            Spacer()
            }
            
            Text( "IL :\(CalculsIL(ListD: <#T##Diaphs#>, Int: <#T##indexView1#>, ListV: <#T##Vitesses#>, Int: <#T##indexView2#>, ListI: <#T##ISOs#>, Int: <#T##indexView3#>))")
            
            Text (" Ici résultat soon ")
            
            Spacer()
            
        }
            
  
    
}
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

and in the File func CalculsIL ligne 65 can’t see all arguments ( the indexes with are in the Content View and variables with are declared in the same File between lignes 10and 15 :smirk: //

//  File.swift
//  new
//
//  Created by Christophe NOBER on 18/02/2022.
//

import Foundation

let Diaphs: [String] = ["0.95","1.0", "1.2","1.4","1.6","1.8","2.0","2.2","2.5","2.8","3.2","3.6","4.0","4.5","5.0","5.6","6.4","7.1","8.0","9.0","10.0","11.0","13.0","14.0","16.0","18.0","20.0","22.0","26.0","29.0","32.0"]

let Vitesses: [String] = ["1/32000","1/16000","1/12800", "1/10000","1/8000","1/6400","1/5000","1/4000","1/3200","1/2500","1/2000","1/1600","1/1250","1/1000", "1/800","1/640","1/500","1/400","1/320", "1/250","1/200","1/160","1/125","1/100", "1/80","1/60","1/50","1/40", "1/30","1/25","1/20","1/15","1/13","1/10" ,"1/8","1/6","1/5","1/4","1/3","1/2.5","1/2","1/1.6", "1/1.3","1","1.3","1.5","2","2.5","3","4","5","6.5","8","10","13","15","20","25","30","40","50","60"]

let ISOs: [String] = [ "12" ,"25", "50", "64", "100", "160","200","250","300","320","400","500","640","800","1000","1250","1600","2000","2500","3200","4000","5000","6400","8000","10000","12800","25600" ]



class Program: ObservableObject {

    
func ExtractDouble (Value: String ) -> Double
{
    // print (Value)
    let components = Value.components(separatedBy: "/")
    if components.count == 2 {
        let x = Double(components[0])! / Double(components[1])!
        return (x)
    }
    
    else {
        let x = Double(components[0])!
        return (x)
    }
}


func NearestValue ( Value : Double , Liste : [String]) -> String {
    

    var BestCoef = 9999.9
    var BestDisp = "None"
    
    for Item in Liste {
        // print ( "BestCoef \(BestCoef)")
        let z: Double  = ExtractDouble (Value: Item)
        // print ( abs( 1-Value/z))
        if BestCoef > abs( 1-Value/z)
        {
            BestCoef = abs( 1-Value/z)
            // print ( "BestCoef2 \(BestCoef)")
            BestDisp = Item
            //print (" Nearest: \(Item)")
        }
    }
    
    if BestCoef >= 1.3 {
        
        BestDisp = BestDisp + "-*" }
    
    else   { BestDisp = BestDisp + "-" }
    
    return BestDisp
}

    func CalculsIL ( ListD: Diaphs , indexD : indexView1 , ListV: Vitesses , indexD: indexView2 , ListI: ISOs , indexI: indexView3 ) -> Double {
        
        

}

}

Still didn’t put any code in CalculsIL as parameters are not “in the scope”

what am I going through ?

Pleeeeeeease help :pray:

and the 3rd file :

//
//  SwiftUIView.swift
//  new
//
//  Created by Christophe NOBER on 18/02/2022.
//

import SwiftUI

struct PickerUIView: View {
    
    @Binding var index:Int
    var List = Diaphs
    var Titre: String
    var sortiePicker = "Init"
    
    var body: some View {
        
        VStack {
            
          Spacer()
            
            Text(Titre)
            
    Picker(Titre, selection: $index)
        {
           
            ForEach(0..<List.count)
            {
                index in Text( List[index] )
            }
        
        }
        .pickerStyle(WheelPickerStyle())
        .compositingGroup()
        .clipped()
       
        }

        
    }
}

struct PickerUIView_Previews: PreviewProvider {
    static var previews: some View {
        PickerUIView(index: Binding.constant(15), List: Diaphs, Titre: "Aperture")
    }
}

ContentView can’t see CalculsIL because that function doesn’t exist in File. What does exist is a class called Program that has a method called CalculsIL.

Do this:

  1. add Program to your ContentView as an @StateObject so you can access it:
@StateObject var program = Program()
  1. modify Program.CalculsIL to this:
func CalculsIL(indexD: Int, indexV: Int, indexI: Int) -> Double {
    //get ListD, ListV and ListI from your global variables
    let listD = Diaphs
    let listV = Vitesses
    let listI = ISOs
    //and perform your calculations
}

or, if you want to be able to pass in alternate arrays for the ListD, ListV, and ListI parameters, you could use this:

func CalculsIL(listD: [String] = Diaphs, indexD: Int,
               listV: [String] = Vitesses, indexV: Int,
               listI: [String] = ISOs, indexI: Int) -> Double {
    //perform your calculations
}
  1. access the CalculsIL function like this:
Text("IL :\(program.CalculsIL(indexD: indexView1, indexV: indexView2, indexI: indexView3))")

That will make your current code work, although I would probably recommend rethinking how you have everything set up. Using global variables like Diaphs, Vitesses and ISOs isn’t exactly a best practice. I would think about making them properties of your Program class instead.

Thank you rooster boy :wink:

engine is running , there is still a lot to do but that’s a step beyond :pray:

Hi all ,

How to avoid initialization while changing Views ?

Each “View” needs struct Base() instance to access all my calculations , issue : all variables are initialized and some variables are needed both sides .

I managed to pass one variable ( IL ) while calling new view :

The app works fine , but I not convicted it is a good way/habit , what would I do if Views need a lot of commun variables ?

The best would be a way to avoid initialization of a struct while instancing this struct ( except the very first time ) .

Thank you for your advise :pray:t2:

Christophe

If multiple Views need access to the same data structure, you have 2 options:

  1. Pass the data structure as a parameter to each View that needs it, or
  2. Inject the data structure into the environment using the .environmentObject() modifier.

For option 1, you can use a struct or a class but for option 2 you would have to use a class that conforms to the ObservableObject protocol.

:pray:t2: RoosterBoy

Hi again ,

my first app works ( photo ) , but I’m still confused with properties … the way to catch , the way to store and to retrieve from any class/struct :smirk:

Example : I use TextField to get some inputs in SwiftUIViewInput ( hour/minute hhmm formatted ).
Works fine , I want to store it in d.hdr ( heure début repos in French ) with is supposed to be located in the Data class


Issue : when I want to retrieve from the ContentView the value supposed to be stored in hdr / Data class , I still retrieve the defaults values .

I do my best not to bother but I’m blocked and I still have missed something about to access/change properties from anywhere .

Thank you :relieved:

Two things:

  1. If your Data object is being created in SwiftUIViewInput, you should be creating it as an @StateObject rather than an @ObservedObject. Doing the latter means it will be recreated every time the View is created, which in SwiftUI can be many times and is not under your control.

  2. Don’t call your class Data. There is already a Foundation type called Data and giving your class the same name has the potential to cause conflicts and bugs that can be hard to track down. Plus, it’s a rather vague name and you should really be giving your stuff names that better describe what they are and what they do.

Thank you for your help Patrick .

I did all what you said , but … properties generated in SwiftUIInput ( hdr/hfr ) I tried to store via d.hdr and d.hfr are still defaults values ( 0 // 11 ) while read from the ContentView :thinking:

:exploding_head:

Disregard , I did it using @Binding var between the 2 views .

Anyway , all advice are welcome :hugs:

More advice, then.

init() {
    self.hdr = hdr
    self.hfr = hfr
}

This does nothing and is not necessary. Since the init takes no parameters hdr and hfr, that code essentially does this:

init() {
    self.hdr = self.hdr
    self.hfr = self.hfr
}

And, since you already assign default values to hdr and hfr, you don’t need an init anyway.


When posting code to these forums, place three backticks ``` on the line before your code and three backticks ``` on the line after your code so that it will be formatted properly. You can also highlight an entire code block and click the </> button on the toolbar to wrap the block for you.

This makes it far easier to read and also makes it easier for other posters to copy/paste the code in order to test solutions and such.