在视图之间的公共标题视图中设置共享标题;每个 Active View

Posted

技术标签:

【中文标题】在视图之间的公共标题视图中设置共享标题;每个 Active View【英文标题】:Setting a shared title within a common Header View amongst Views; per 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() 上为每个成员视图设置带有标题的标题。

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

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

【问题讨论】:

【参考方案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。这并不是说通知方法不是正确的做法——它提供了一个完全解耦的设计。我不是在评论什么对你来说是正确的——只是偏好键是一种从后代交流/聚合值的方式

以上是关于在视图之间的公共标题视图中设置共享标题;每个 Active View的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AppDelegate 在视图之间共享 iAd 横幅

UITabBarController 视图之间的共享控件

跨多个视图控制器共享背景图像

片段之间的共享数据(父列表视图和子列表视图)

非常基本的objective-c问题——如何让不同的视图共享信息?

如何在整个应用程序中共享CLLocation?