【问题标题】:Setting a shared title within a common Header View amongst Views; per Active View在视图之间的公共标题视图中设置共享标题;每个 Active View
【发布时间】:2021-01-20 20:39:16
【问题描述】:

目标:使用包含共享标题Text()的通用标题视图。

场景:我有多个视图共享一个公共选项卡空间在一个容器选项卡视图中,其中包含要共享的结构标题。

????这是一个(很多:1)场景。

注意:我不想使用 NavigationView,因为它会破坏横向模式。一个简单的小标题视图就可以了。我只需要在成员视图中填充共享标题空间。
我不想仅仅为每个成员视图添加重复的标题(具有完全相同的布局)。

几个想法:我需要标题来响应“更改标题”事件,这样我才能看到新标题。

所以我相信我可以使用 1) @Binder(每个成员 View) --> @State (shared Header View) 或
2) @Environment。

我不知道如何将#1 融入这个特定场景。
所以我在玩#2:环境对象。

DesignPattern: Main Header View 的标题由多个 View 设置,因此 Header View 不知道多个 View:

我没有让 EnvironmentObject 范式工作。

这是代码...
主视图:

import SwiftUI

// Need to finish this.

class NYTEnvironment {
    var title = "Title"
    var msg = "Mother had a feeling..."
}

class NYTSettings: ObservableObject {
    @Published var environment: NYTEnvironment
    init() {
        self.environment = NYTEnvironment()
    }
}

struct NYTView: View {
    var nytSettings  = NYTSettings()
     
    @State var selectionDataSegmentIndex = 0

    var bindingDataSourceSegment: Binding<Int> {
        .init(get: {
            selectionDataSegmentIndex
        }, set: {
            selectionDataSegmentIndex = $0
        })
    }

    var body: some View {
        let county = 0; let state = 1; let states = 2

        VStack {
            NYTHeaderView()
            SegmentAndDataPickerVStack(spacing: 10) {
                if let segments = Source.NYT.dataSegments {
                    Picker("NYT Picker", selection: bindingDataSourceSegment) {
                        ForEach(segments.indices, id: \.self) { (index: Int) in
                            Text(segments[index])
                        }
                    }.pickerStyle(SegmentedPickerStyle())
                }
            }
            if selectionDataSegmentIndex == county {
                NYTCountyView()
            } else if selectionDataSegmentIndex == state {
                NYTStateView()
            } else if selectionDataSegmentIndex == states {
                NYTStatesView()
            }
            Spacer()
        }.environmentObject(nytSettings)
    }

    struct TrailingItem: View {
        var body: some View {
            Button(action: {
                print("Info")
            }, label: {
                Image(systemName: "info.circle")
            })
        }
    }
}

// ====================================================================================

struct NYTHeaderView: View {
    @EnvironmentObject var nytSettings: NYTSettings
    var body: some View {
        ZStack {
            Color.yellow
            Text(nytSettings.environment.title)
        }.frame(height: Header.navigationBarHeight)
    }
}

修订:我在 memberViews() 中添加了 EnvironmentObject 修饰符:

if selectionDataSegmentIndex == county {
                NYTCountyView().environmentObject(NYTSettings())
            } else if selectionDataSegmentIndex == state {
                NYTStateView().environmentObject(NYTSettings())
            } else if selectionDataSegmentIndex == states {
                NYTStatesView().environmentObject(NYTSettings())
            }
            ...

主容器/选项卡视图中的成员视图之一(如上所述):

struct NYTCountyView: View {
    @ObservedObject var dataSource = NYTCountyModel()
    @EnvironmentObject var nytSettings: NYTSettings
    ...
    ...

 }.onAppear {
       nytSettings.environment.title = "Selected Counties"
                            
       if dataSource.revisedCountyElementListAndDuration == nil {
              dataSource.getData()
          }
       }
       Spacer()
       ...
}

这是编译时错误:

作案手法:在 .onAppear() 上为每个成员视图设置带有标题的标题。

问题:我没有得到任何头衔;只是默认的“标题”值。

问题:我走对了吗?
如果是这样,我错过了什么?
或者...有其他选择吗?

【问题讨论】:

    标签: ios swiftui combine


    【解决方案1】:

    整个问题归结为“多:1”范式。
    我通过休息和散步得到了这个启示。

    这就是众所周知的“方孔中的圆钉”场景。

    我需要的是一个 轻度耦合 关系,其中不需要标题值的来源。
    因此使用 Notification 范例。

    标题视图的标题是接收者,因此我使用了 .onReceive 修饰符:

    struct NYTHeaderView: View {
        @State private var title: String = ""
        var body: some View {
            ZStack {
                Color.yellow
                Text(title).onReceive(NotificationCenter.default.publisher(for: .headerTitle)) {note in
                    title = note.object as? String ?? "New York Times"
                }
    
            }.frame(height: Header.navigationBarHeight)
        }
    }
    

    【讨论】:

      【解决方案2】:

      这听起来像是 SwiftUI 首选项旨在解决的问题。偏好是从孩子那里收集和减少的值,以供某些远古祖先使用。一个值得注意的例子是 NavigationView 如何获得它的标题 - 标题设置在孩子身上,而不是 NavigationView 本身:

      NavigationView {
          Text("I am a simple view")
              .navigationTitle("Title")
      }
      

      因此,在您的情况下,您有某种标题(为简洁起见简化为 String),每个子视图都可能想要设置。所以你会像这样定义一个TitlePreferenceKey

      struct TitlePreferenceKey: PreferenceKey {
          static var defaultValue: String = ""
          
          static func reduce(value: inout String, nextValue: () -> String) {
              value = nextValue()
          }
      }
      

      这里,reduce 函数只是应用它从后代中看到的最后一个值,但由于您只选择了一个子视图,它应该可以工作。

      然后,要使用它,你会有这样的东西:

      struct NYTView: View {
         @State var title = ""
         @State var selection = 0
      
         var body: some View {
            VStack {
               Text(title)
               Picker("", selection: $selection) {
                  Text("SegmentA").tag(0)
                  Text("SegmentB").tag(1)
               }
      
               switch selection {
               case 0: NYTCountyView()
               case 1: NYTStateView()
                         .preference(key: TitlePreferenceKey.self, value: "State view")
               default: EmptyView()
               }
            }
            .onPreferenceChange(TitlePreferenceKey.self) {
               self.title = $0
            }
         }
      
      struct NYTCountyView: View {
         @State var selectedCounty = "..."
      
         var body: some View {
             VStack {
                 //...
             }
             .preference(key: TitlePreferenceKey.self, value: selectedCounty)
         }
      }
      

      因此,可以由父级设置首选项,如NYTStateView 的示例,或由具有动态值的子级设置,如NYTCountyView 的示例

      【讨论】:

      • PreferenceKey 方法显然只适用于 NavigationView。我没有使用 NavigationView,因为它会干扰我的应用程序中的纵向-> 横向模式。我想要一个完全不同的横向与纵向视图。因此,带有标题的更简单的静态视图。在这种情况下,Preference 方法不起作用;但是通知范式确实如此。话虽如此,我很感激你给我的东西,并会注意到它是一个以导航为中心的范例。
      • @FrederickC.Lee... 嗯.. PreferenceKey 不仅适用于NavigationView,在我的示例中没有NavigationView。 PreferenceKey 是一种让多个视图以一对多的方式与其距离祖先进行通信的方式
      • 我没有让它工作;我所有的引用都指向 NavigationView;我会更多地玩它。我忘记了标题视图本身。我没有从首选项键加载标题值。学习它。
      • 是的...我按照您的示例进行操作,但无法让它最终发挥作用。静态视图中的标题不会改变。但是,通知 --> 接收者范例有效。
      • 很难知道究竟什么对你不起作用,但请记住 .onPreferenceChange 必须在祖先上完成 - 而不是在兄弟视图上完成,@FrederickC.Lee。这并不是说通知方法不是正确的做法——它提供了一个完全解耦的设计。我不是在评论什么对你来说是正确的——只是偏好键是一种从后代交流/聚合值的方式
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-21
      • 1970-01-01
      • 2012-09-27
      • 1970-01-01
      • 1970-01-01
      • 2014-05-05
      • 1970-01-01
      相关资源
      最近更新 更多