Last active
March 19, 2021 09:32
-
-
Save AAlfare/1fa397935cb14355662c8ad3198ffa11 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// Product | |
// | |
// Created by Andreas Alfarè on 12.03.21. | |
// | |
import SwiftUI | |
struct IdentifiableIndices<Base: RandomAccessCollection> | |
where Base.Element: Identifiable { | |
typealias Index = Base.Index | |
struct Element: Identifiable { | |
let id: Base.Element.ID | |
let rawValue: Index | |
} | |
fileprivate var base: Base | |
} | |
extension IdentifiableIndices: RandomAccessCollection { | |
var startIndex: Index { base.startIndex } | |
var endIndex: Index { base.endIndex } | |
subscript(position: Index) -> Element { | |
Element(id: base[position].id, rawValue: position) | |
} | |
func index(before index: Index) -> Index { | |
base.index(before: index) | |
} | |
func index(after index: Index) -> Index { | |
base.index(after: index) | |
} | |
} | |
extension RandomAccessCollection where Element: Identifiable { | |
var identifiableIndices: IdentifiableIndices<Self> { | |
IdentifiableIndices(base: self) | |
} | |
} | |
extension ForEach where ID == Data.Element.ID, | |
Data.Element: Identifiable, | |
Content: View { | |
init<T>( | |
_ indices: Data, | |
@ViewBuilder content: @escaping (Data.Index) -> Content | |
) where Data == IdentifiableIndices<T> { | |
self.init(indices) { index in | |
content(index.rawValue) | |
} | |
} | |
} | |
extension ForEach where ID == Data.Element.ID, | |
Data.Element: Identifiable, | |
Content: View { | |
init<T>( | |
_ data: Binding<T>, | |
@ViewBuilder content: @escaping (T.Index, Binding<T.Element>) -> Content | |
) where Data == IdentifiableIndices<T>, T: MutableCollection { | |
self.init(data.wrappedValue.identifiableIndices) { index in | |
content( | |
index.rawValue, | |
Binding( | |
get: { data.wrappedValue[index.rawValue] }, | |
set: { data.wrappedValue[index.rawValue] = $0 } | |
) | |
) | |
} | |
} | |
} | |
struct Product { | |
var name: String | |
var variants: [Variant] | |
var options: [Variation] | |
} | |
struct Variant: Identifiable, Hashable { | |
var stock: Int | |
var variation: [Variation] | |
var id = UUID().uuidString | |
func hash(into hasher: inout Hasher) { | |
hasher.combine(id) | |
} | |
} | |
struct Variation: Hashable { | |
var name: String | |
var option: String | |
} | |
struct EditorView: View { | |
var product: Product = Product( | |
name: "T-Shirt", | |
variants: [ | |
Variant( | |
stock: 0, | |
variation: [ | |
Variation(name: "Size", option: "S") | |
] | |
) | |
], | |
options: [ | |
Variation(name: "Size", option: "S"), | |
Variation(name: "Size", option: "M"), | |
Variation(name: "Size", option: "L"), | |
] | |
) | |
@State var variants: [Variant] | |
init() { | |
_variants = State(initialValue: product.variants) | |
} | |
var body: some View { | |
Form { | |
Text(product.name) | |
ForEach($variants) { index, variant in | |
Section { | |
NavigationLink( | |
destination: VariationPicker(options: product.options, selection: variant.variation), | |
label: { | |
ForEach(variants[index].variation, id: \.self) { variation in | |
Text("\(variation.name): \(variation.option)") | |
} | |
}) | |
Stepper(value: $variants[index].stock) { | |
Text("Stock: \(variants[index].stock)") | |
} | |
Button("Delete", action: { | |
variants.remove(at: index) | |
}) | |
} | |
} | |
Button("Add", action: { | |
variants.append( | |
Variant(stock: 0, variation: []) | |
) | |
}) | |
} | |
} | |
} | |
struct VariationPicker: View { | |
var options: [Variation] | |
@Binding var selection: [Variation] | |
var body: some View { | |
List(options, id: \.self) { variation in | |
HStack { | |
Text(variation.name) | |
.foregroundColor(.secondary) | |
Spacer() | |
Text(variation.option) | |
if self.selection.contains(variation) { | |
Image(systemName: "checkmark.circle") | |
.foregroundColor(.accentColor) | |
.imageScale(.large) | |
} else { | |
Image(systemName: "circle") | |
.foregroundColor(Color(.quaternaryLabel)) | |
.imageScale(.large) | |
} | |
} | |
.contentShape(Rectangle()) | |
.onTapGesture { | |
if let index = self.selection.firstIndex(of: variation) { | |
self.selection.remove(at: index) | |
} else { | |
self.selection.append(variation) | |
} | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
NavigationView { | |
EditorView() | |
} | |
} | |
} |
It works if I present the picker in a sheet though...
.sheet(isPresented: $showPicker, content: { VariationPicker(options: viewModel.product.options, selection: $viewModel.product.variants[index].variation) })
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
When I try to select some variations in the variation picker it won't show the checkmark next to the variation name.