在视图之间的公共标题视图中设置共享标题;每个 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 横幅