简体   繁体   中英

SwiftUI pass by reference a class object to another view

I'm trying to learn SwiftUI and I'm going to develop a simple app with tab views and sharing core motion data between those views.

The main idea is to create a motion manager object (like here ) and use the sensor values in all views.

ContentView.swift :

import SwiftUI

struct ContentView: View {

@State private var selection = 1

@State private var viewNames : [String] = ["View1", "View2"]

var body: some View {
    
    TabView(selection: $selection){
        
        View1(viewName: $viewNames[0]).tag(0)
        
        View2(viewName: $viewNames[1]).tag(1)
                    
        }
    }
}

View1.swift :

import SwiftUI

struct View1 : View {

@Binding var viewName : String

var body: some View {
    
    Text("First View")
    .font(.title)
    .foregroundColor(Color.gray)
    .tabItem {
        VStack {
            Image(systemName: "star")
            Text(viewName)
        }
     }
  }
}

View2.swift :

struct View2 : View {

@Binding var viewName : String


var body: some View {
    
    VStack {
        Text("Second View")
            .font(.title)
            .foregroundColor(Color.green)
            .padding(.top)
        
        View21(motionManager: MotionManager())

        
        
    }.tabItem {
        VStack {
            Image(systemName:"heart")
            Text(viewName)
        }
    }
    
  }

}

View21.swift

struct View21 : View {

@ObservedObject var motionManager : MotionManager

@State private var showDetails = false


var body: some View{
  Text(String(format: "%.2f", motionManager.x))
}

With these code I can use the sensor data in View21 , but I can't access the data in the views in the hierarchy above.

Furthermore I created @ObservedObject in the ContentView (like here ) and passed it through all views. My app work in the simulator, but it doesn't on the real device. I can see sensor data changing, but I can't switch the tab views. I've tried to use @EnvironementObject instead of @ObservedObject , but the behavior is the same.

I'll be very thankful for any help and tipps to my issue. Best wishes

Okay, so Paulw11 is right that the you probably want to inject your ObservableObject into your environment, then in each view that wants to access that instance, you just add a property with the @EnvironmentObject property wrapper. Below I've thrown together the simplest example I could think of, so that you can get the idea of how it works.

import SwiftUI
import Combine

class ManagerPlaceholder: ObservableObject {
    
    @Published var propertyOne: Double = 1.0
    @Published var propertyTwo: Double = 2.0
    
    func action() {
        propertyOne = Double.random(in: 0.0..<100.00)
        propertyTwo = Double.random(in: 0.0..<100.00)
    }
    
}


struct ContentView: View {
    
    @EnvironmentObject var manager: ManagerPlaceholder
    
    var body: some View {
        TabView {
            Subview()
                .tabItem { Label("First", systemImage: "hexagon.fill") }
                .tag(1)
            Subview()
                .tabItem { Label("Second", systemImage: "circle.fill") }
                .tag(2)
            
        }
    }
    
}

struct Subview: View {
    
    @EnvironmentObject var manager: ManagerPlaceholder
    
    var body: some View {
        VStack {
            Text("Prop One: \(manager.propertyOne)").padding()
            Text("Prop Two: \(manager.propertyTwo)").padding()
            Button("Change", action: manager.action).padding()
        }
    }
    
}

So above is

  • A simple ObservableObject - All it does is set two Doubles with a random value (notice the properties you want to observe are marked as @Published)
  • A tab view
  • A simple sub-view

Notice that both of the views have a @EnvironmentObject var manager: ManagerPlaceholder . You don't set that property directly. That line says that you want to reference a ManagerPlaceholder instance that's in the Environment. The environment is a sort of "pool" of storage that SwiftUI manages for you. You can add an instance of an object to it, then reference it in sub-views that need it.

So to make sure that's in the environment you add it when instantiating a view (could be the tab view, or any super-view). So for example in your _NAME_App.swift (for an iOS 14 target) or SceneDelegate.swift (for an iOS 13 target), you'd instantiate your view like this:

ContentView().environmentObject(ManagerPlaceholder())

If you run the code above you'll see when you hit the Change button it randomly sets the two properties, and both subviews will see the exact same values when you switch back and forth, because they're both referencing the same instance.

Feel free to comment if anything is unclear.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM