我正在尝试在Swiftui中实现一个视图堆栈,我的@State对象正在被重置,原因我不清楚

原学程将引见我正在测验考试在Swiftui中完成1个望图客栈,我的@State对于象正在被重置,缘由我没有清晰的处置办法,这篇学程是从其余处所瞅到的,而后减了1些海外法式员的疑问与解问,愿望能对于您有所赞助,佳了,上面开端进修吧。

我正在尝试在Swiftui中实现一个视图堆栈,我的@State对象正在被重置,原因我不清楚 教程 第1张

成绩描写

我照样个入门者,正在做1个用客栈推送以及弹出望图的试验。当我从客栈中弹出1个望图时,前1个望图的@State变质曾经重置,我没有晓得为何。

此演示代码在MacOS长进言了尝试。

import SwiftUI

typealias Push = (AnyView) -> ()
typealias Pop = () -> ()

struct PushKey: EnvironmentKey {
 
 static let defaultValue: Push = { _ in }
 
}

struct PopKey: EnvironmentKey {
 
 static let defaultValue: Pop = {() in }
 
}

extension EnvironmentValues {
 
 var push: Push {
  get { self[PushKey.self] }
  set { self[PushKey.self] = newValue }
 }
 
 var pop: Pop {
  get { self[PopKey.self] }
  set { self[PopKey.self] = newValue }
 }
}

struct ContentView: View {
 @State private var stack: [AnyView]

 var body: some View {
  currentView()
.environment(.push, push)
.environment(.pop, pop)
.frame(width: 六00.0, height: 四00.0)
 }
 
 public init() {
  _stack = State(initialValue: [AnyView(AAA())])
 }
 
 private func currentView() -> AnyView {
  if stack.count == 0 {
return AnyView(Text("stack empty"))
  }
  return stack.last!
 }
 
 public func push(_ content: AnyView) {
  stack.append(content)
 }
 
 public func pop() {
  stack.removeLast()
 }
}

struct AAA : View {
 @State private var data = "default text"
 @Environment(.push) var push

 var body: some View {
  VStack {
TextEditor(text: $data)
Button("Push") {
 self.push(AnyView(BBB()))
}
  }
 }
}

struct BBB : View {
 @Environment(.pop) var pop

 var body: some View {
  VStack {
Button("Pop") {
 self.pop()
}
  }
 }
}

假如我在编纂器中键进1些文原,而后面打Push,而后弹出该望图,我愿望文原编纂器保存我的变动,但是它会复原为默许文原。

我错过了甚么?

编纂:

我想这现实上是1个怎样完成NavigationView以及NavigationLink的成绩。这段简略的代码完成了我想要做的工作:

import SwiftUI

struct MyView: View {
 @State var text = "default text"
 var body: some View {
  VStack {
TextEditor(text: $text)
NavigationLink(destination: MyView()) {
 Text("Push")
}
  }
 }
}

struct ContentView: View {
 var body: some View {
  NavigationView {
MyView()
  }
 }
}

在iOS上运转它,如许您便不妨取得NAV客栈。编纂文原,而后按下。假如须要,请再次编纂,而后前往以检查状况已保存。

准绳上,我的代码正在测验考试做异样的工作。

推举谜底

我将分享此测验考试,或者许它将赞助您创立您的版原。

这1切皆初于测验考试创立相似NavigationView以及NavigationLink的实质,但是可以或许追溯到客栈中的随机View

我有1个协定,个中对于象前往View。平日是enumview()援用具备供给准确子项ViewViewViewContentView/MainView的任务方法相似于情节提纲,它只表现current或者path变质中指定的实质。

//To make the View options generic
protocol ViewOptionsProtocol: Equatable {
 associatedtype V = View
 @ViewBuilder func view() -> V
}

这是追踪主望图以及NavigationLink/path的根本导航线由器。这瞅起去与您要履行的操纵相似。

//A generic Navigation Router
class ViewNavigationRouter<T: ViewOptionsProtocol>: ObservableObject{
 
 //MARK: Variables
 var home: T
 //Keep track of your current screen
 @Published private (set) var current: T
 //Keep track of the path
 @Published private (set) var path: [T] = []
 //MARK: init
 init(home: T, current: T){
  self.home = home
  self.current = current
 }
 //MARK: Functions
 //Control how you get to the screen
 ///Navigates to the nextScreen adding to the path/cookie crumb
 func push(nextScreen: T){
  //This is a basic setup just going forward
  path.append(nextScreen)
 }
 ///Goes back one step in the path/cookie crumb
 func pop(){
  //Use the stored path to go back
  _ = path.popLast()
 }
 ///clears the path/cookie crumb and goes to the home screen
 func goHome(){
  path.removeAll()
  current = home
 }
 ///Clears the path/cookie crumb array
 ///sets the current View to the desired screen
 func show(nextScreen: T){
  goHome()
  current = nextScreen
 }
 ///Searches in the path/cookie crumb for the desired View in the latest position
 ///Removes the later Views
 ///sets the nextScreen
 func dismissTo(nextScreen: T){
  while !path.isEmpty && path.last != nextScreen{
pop()
  }
  if path.isEmpty{
show(nextScreen: nextScreen)
  }
 }
 
}

它没有是@Environment,但是它不妨很轻易天成为@EnvrionmentObject,而且一切望图皆必需在enum中,是以望图其实不是完整未知的,但是这是我可以或许绕过AnyView并将望图保存在@ViewBuilder中的独一办法。

我应用相似上面的实质作为主望图中的重要部门body

router.path.last?.view() ?? router.current.view()

这里是您的示例的1个简略完成

import SwiftUI

class MyViewModel:  ViewNavigationRouter<MyViewModel.ViewOptions> {
 //In some view router concepts the data that is /preserved/shared among the views is preserved in the router itself.
 @Published var preservedData: String = "preserved"
 init(){
  super.init(home: .aaa ,current: .aaa)
 }
 
 enum ViewOptions: String, ViewOptionsProtocol, CaseIterable{
  case aaa
  case bbb
  @ViewBuilder func view() -> some View{
ViewOptionsView(option: self)
  }
 }
 
 struct ViewOptionsView: View{
  let option: ViewOptions
  var body: some View{
switch option {
case .aaa:
 AAA()
case .bbb:
 BBB()
}

  }
 }
}
struct MyView: View {
 @StateObject var router: MyViewModel = .init()
 var body: some View {
  NavigationView{
ScrollView {
 router.path.last?.view() ?? router.current.view()
}
.toolbar(content: {
 //Custom back button
 ToolbarItem(placement: .navigationBarLeading, content: {
  if !router.path.isEmpty {
Button(action: {
 router.pop()
}, label: {
 HStack(alignment: .center, spacing: 二, content: {
  Image(systemName: "chevron.backward")
  if router.path.count >= 二{
Text(router.path[router.path.count - 二].rawValue)
  }else{
Text(router.current.rawValue)
  }
 })
 
})
  }
 })
})
.navigationTitle(router.path.last?.rawValue ?? router.current.rawValue)
  }.environmentObject(router)
 }
}


struct MyView_Previews: PreviewProvider {
 static var previews: some View {
  MyView()
 }
}

struct AAA : View {
 //This will reset because the view is cosmetic. the data needs to be preserved somehow via either persistence or in the router for sharing with other views.
 @State private var data = "default text"
 @EnvironmentObject var vm: MyViewModel
 var body: some View {
  VStack {
TextEditor(text: $data)
TextEditor(text: $vm.preservedData)
Button("Push") {
 vm.push(nextScreen: .bbb)
}
  }
 }
}

struct BBB : View {
 @EnvironmentObject var vm: MyViewModel
 
 var body: some View {
  VStack {
Button("Pop") {
 vm.pop()
}
  }
 }
}

佳了闭于我正在测验考试在Swiftui中完成1个望图客栈,我的@State对于象正在被重置,缘由我没有清晰的学程便到这里便停止了,愿望趣模板源码网找到的这篇技巧文章能赞助到年夜野,更多技巧学程不妨在站内搜刮。