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())
Если вы запустите приведенный выше код, вы увидите, что при нажатии кнопки "Изменить" он случайным образом устанавливает два свойства, и оба подпредставления будут видеть одни и те же значения при переключении вперед и назад, потому что они оба ссылаются на один и тот же экземпляр.
Не стесняйтесь комментировать, если что-то неясно.