首页 > 解决方案 > Setting a shared title within a common Header View amongst Views; per Active View

问题描述

Goal: To use a common header View containing a shared title Text().

Scenario: I have multiple Views that share a common tab space within the one container tab View that contains a struct Header that is to be shared.

This is a (many : 1) scenario.

Note: I don't want to use a NavigationView because it screws up landscape mode. A simple small header View is fine. I just need to populate the shared Title space amongst the member Views.
I don't want to merely add duplicate headers (having exactly the same layout) for each member View.

Several ideas: I need the header to respond to the 'change of title' event so I can see the new title.

So I believe I could use 1) @Binder(each member View) --> @State (shared Header View) or
2) @Environment.

I don't know how I could fit #1 into this particular scenario.
So I'm playing with #2: Environment Object.

DesignPattern: Main Header View's title set by multiple Views so the Header View is not aware of the multiple Views:

enter image description here

I'm not getting the EnvironmentObject paradigm to work.

Here's the codes...
MainView:

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

Revision: I've added EnvironmentObject modifiers to the memberViews():

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

One of the member Views that's within the Main Container/Tab View (per above):

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

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

Here's the compile-time error:

enter image description here

Modus Operandi: Set the title w/in header per member View upon .onAppear().

Problem: I'm not getting any title; just the default "Title" value.

Question: Am I on the right track?
If so, what am I missing?
or... is there an alternative?

标签: iosswiftuicombine

解决方案


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

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

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

标题视图的标题是接收者,因此我使用了.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)
    }
}

推荐阅读