SwiftUI передает по ссылке объект класса другому представлению

Я пытаюсь изучить SwiftUI и собираюсь разработать простое приложение с представлениями вкладок и обменом базовыми данными движения между этими представлениями.

Основная идея - создать объект менеджера движения (как здесь) и использовать значения датчиков во всех представлениях.

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))
}

С помощью этого кода я могу использовать данные датчика в View21, но я не могу получить доступ к данным в представлениях в иерархии выше.

Кроме того, я создал @ObservedObjectв ContentView (как здесь) и пропустил его через все представления. Мое приложение работает в симуляторе, но не на реальном устройстве. Я вижу изменение данных датчика, но не могу переключать представления вкладок. Я пытался использовать@EnvironementObject вместо того @ObservedObject, но поведение такое же.

Буду очень благодарен за любую помощь и советы по моей проблеме. С наилучшими пожеланиями

1 ответ

Решение

Итак, Paulw11 прав в том, что вы, вероятно, хотите внедрить свой ObservableObject в свою среду, а затем в каждом представлении, которое хочет получить доступ к этому экземпляру, вы просто добавляете свойство с оболочкой свойств @EnvironmentObject. Ниже я собрал простейший пример, который я мог придумать, чтобы вы могли понять, как это работает.

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()
        }
    }
    
}

Итак, выше

  • Простой ObservableObject - все, что он делает, это устанавливает два типа Double со случайным значением (обратите внимание, что свойства, которые вы хотите наблюдать, отмечены как @Published)
  • Вид вкладки
  • Простой подвид

Обратите внимание, что оба представления имеют @EnvironmentObject var manager: ManagerPlaceholder. Вы не устанавливаете это свойство напрямую. В этой строке говорится, что вы хотите сослаться на экземпляр ManagerPlaceholder, который находится в Environment. Среда - это своего рода "пул" хранилищ, которым SwiftUI управляет за вас. Вы можете добавить к нему экземпляр объекта, а затем ссылаться на него во вложенных представлениях, которые в нем нуждаются.

Итак, чтобы убедиться, что это в среде, вы добавляете его при создании экземпляра представления (это может быть вкладка или любой суперпредставление). Так, например, в вашем _NAME_App.swift (для цели iOS 14) или SceneDelegate.swift (для цели iOS 13) вы должны создать экземпляр своего представления следующим образом:

ContentView().environmentObject(ManagerPlaceholder())

Если вы запустите приведенный выше код, вы увидите, что при нажатии кнопки "Изменить" он случайным образом устанавливает два свойства, и оба подпредставления будут видеть одни и те же значения при переключении вперед и назад, потому что они оба ссылаются на один и тот же экземпляр.

Не стесняйтесь комментировать, если что-то неясно.

Другие вопросы по тегам