GBA002/Delta/Settings/Experimental Features/FeatureDetailView.swift

118 lines
3.2 KiB
Swift

//
// FeatureDetailView.swift
// Delta
//
// Created by Riley Testut on 4/10/23.
// Copyright © 2023 Riley Testut. All rights reserved.
//
import SwiftUI
import DeltaFeatures
struct FeatureDetailView<Feature: AnyFeature>: View
{
@ObservedObject
var feature: Feature
var body: some View {
Form {
Section {
Toggle(isOn: $feature.isEnabled.animation()) {
Text(feature.name)
.bold()
}
} footer: {
if let description = feature.description
{
Text(description)
}
}
if feature.isEnabled
{
ForEach(feature.allOptions, id: \.key) { option in
if let optionView = optionView(option)
{
Section {
optionView
} footer: {
if let description = option.description
{
Text(description)
}
}
}
}
}
}
}
// Cannot open existential if return type uses concrete type T in non-covariant position (e.g. Box<T>).
// So instead we erase return type to AnyView.
private func optionView<T: AnyOption>(_ option: T) -> AnyView?
{
guard let view = OptionRow(option: option) else { return nil }
return AnyView(view)
}
}
private struct OptionRow<Option: AnyOption, DetailView: View>: View where DetailView == Option.DetailView
{
var name: LocalizedStringKey
var value: any LocalizedOptionValue
var detailView: DetailView
@State
private var displayInline: Bool = false
init?(option: Option)
{
// Only show if option has a name, localizable value, and detailView.
guard
let name = option.name,
let value = option.value as? any LocalizedOptionValue,
let detailView = option.detailView()
else { return nil }
self.name = name
self.value = value
self.detailView = detailView
}
var body: some View {
VStack {
if displayInline
{
// Display entire view inline.
detailView
}
else
{
let wrappedDetailView = Form {
detailView
}
NavigationLink(destination: wrappedDetailView) {
HStack {
Text(name)
Spacer()
value.localizedDescription
.foregroundColor(.secondary)
}
}
.overlay(
// Hack to ensure displayInline preference is in View hierarchy.
detailView
.hidden()
.frame(width: 0, height: 0)
)
}
}
.onPreferenceChange(DisplayInlineKey.self) { displayInline in
self.displayInline = displayInline
}
}
}