Skip to content

Instantly share code, notes, and snippets.

@AAlfare
Last active March 19, 2021 09:32
Show Gist options
  • Save AAlfare/1fa397935cb14355662c8ad3198ffa11 to your computer and use it in GitHub Desktop.
Save AAlfare/1fa397935cb14355662c8ad3198ffa11 to your computer and use it in GitHub Desktop.
//
// 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()
}
}
}
@AAlfare
Copy link
Author

AAlfare commented Mar 19, 2021

When I try to select some variations in the variation picker it won't show the checkmark next to the variation name.

@AAlfare
Copy link
Author

AAlfare commented Mar 19, 2021

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