Skip to content

Instantly share code, notes, and snippets.

@chase-seibert
Last active January 20, 2024 23:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chase-seibert/6f135543290572befb5213a863bc666a to your computer and use it in GitHub Desktop.
Save chase-seibert/6f135543290572befb5213a863bc666a to your computer and use it in GitHub Desktop.
100DaysofSwiftUI.swift
@main
struct Test1App: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
//
// ContentView.swift
// Test2
//
// Created by cseibert on 12/18/23.
//
import SwiftUI
struct ContentView: View {
@State private var checkAmount = 0.0
@State private var numberOfPeople = 2
@State private var tipPercentage = 20
@FocusState private var amountIsFocused: Bool
//let tipPercentages = [10, 15, 20, 25, 0]
let tipPercentages = 0..<101
var grandTotal: Double {
let tipSelection = Double(tipPercentage)
let tipValue = checkAmount / 100 * tipSelection
return checkAmount + tipValue
}
var totalPerPerson: Double {
let peopleCount = Double(numberOfPeople)
let amountPerPerson = grandTotal / peopleCount
return amountPerPerson
}
var body: some View {
NavigationStack {
Form {
Section {
TextField("Amount", value: $checkAmount, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
.focused($amountIsFocused)
}
Section("How much tip do you want to leave?") {
Picker("Tip percentage", selection: $tipPercentage) {
ForEach(tipPercentages, id: \.self) {
Text($0, format: .percent)
}
}
}
Section {
Picker("Number of people", selection: $numberOfPeople) {
ForEach(2..<100) {
Text("\($0) people").tag($0)
}
}
}
Section("Amount per person") {
Text(totalPerPerson, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
}
Section("Total check") {
Text(grandTotal, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
.foregroundStyle(grandTotal < 100 ? .red : .black)
}
}
.navigationTitle("WeSplit")
.toolbar {
if amountIsFocused {
Button("Done") {
amountIsFocused = false
}
}
}
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// Test3
//
// Created by cseibert on 12/21/23.
//
import SwiftUI
struct ContentView: View {
@State private var checkAmount = ""
@State private var numberOfPeople = 2
@State private var tipPercentage = 20
let tipPercentages = [10, 15, 20, 25, 0]
var body: some View {
Form {
Section {
TextField("Amount", value: $checkAmount, format: .currency(code: "USD"))
}
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// GuessTheFlag
//
// Created by cseibert on 12/27/23.
//
import SwiftUI
struct ContentView: View {
@State private var countries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Nigeria", "Poland", "Spain", "UK", "Ukraine", "US"].shuffled()
@State private var correctAnswer = Int.random(in: 0...2)
@State private var showingScore = false
@State private var scoreTitle = ""
@State private var scoreValue = 0
@State private var whichFlag = 0
@State private var rotationAmounts = [0.0, 0.0, 0.0]
@State private var opacityAmounts = [1.0, 1.0, 1.0]
struct FlagImage: View {
var name: String
var body: some View {
Image(name)
.clipShape(.capsule)
.shadow(radius: 5)
}
}
var body: some View {
ZStack {
RadialGradient(stops: [
.init(color: Color(red: 0.1, green: 0.2, blue: 0.45), location: 0.3),
.init(color: Color(red: 0.76, green: 0.15, blue: 0.26), location: 0.3),
], center: .top, startRadius: 200, endRadius: 400)
.ignoresSafeArea()
VStack {
Spacer()
Text("Guess the Flag")
.font(.largeTitle.bold())
.foregroundStyle(.white)
VStack(spacing: 15) {
VStack {
Text("Tap the flag of")
.foregroundStyle(.secondary)
.font(.subheadline.weight(.heavy))
Text(countries[correctAnswer])
.font(.largeTitle.weight(.semibold))
}
ForEach(0..<3) { number in
Button {
whichFlag = number
withAnimation(.spring(duration: 0.5, bounce: 0.5)) {
rotationAmounts[number] += 360
}
opacityAmounts[(number + 1) % 3] = 0.2
opacityAmounts[(number + 2) % 3] = 0.2
flagTapped(number)
} label: {
FlagImage(name: countries[number])
}
.rotation3DEffect(.degrees(rotationAmounts[number]), axis: (x: 0, y: 1, z: 0))
.opacity(opacityAmounts[number])
}
}
.frame(maxWidth: .infinity)
.padding(.vertical, 20)
.background(.regularMaterial)
.clipShape(.rect(cornerRadius: 20))
.alert(scoreTitle, isPresented: $showingScore) {
Button("Continue", action: askQuestion)
} message: {
Text("Your score is \(scoreValue)")
}
Spacer()
Spacer()
Text("Score: \(scoreValue)")
.foregroundStyle(.white)
.font(.title.bold())
Spacer()
}
.padding()
}
}
func flagTapped(_ number: Int) {
if number == correctAnswer {
scoreTitle = "Correct"
scoreValue += 1
} else {
let incorrectAnswer = countries[number]
scoreTitle = "Wrong, that is the flag for \(incorrectAnswer)"
}
showingScore = true
}
func askQuestion() {
countries.shuffle()
correctAnswer = Int.random(in: 0...2)
rotationAmounts = [0.0, 0.0, 0.0]
opacityAmounts = [1.0, 1.0, 1.0]
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// RockPaperScissors
//
// Created by cseibert on 12/29/23.
//
import SwiftUI
struct ContentView: View {
enum RPSChoice: String, CaseIterable {
case rock = "mountain.2"
case paper = "newspaper"
case scissors = "scissors"
}
@State var goalIsToWin = true
@State var score = 0
@State var numQuestions = 0
@State var computerSelection = RPSChoice.allCases.randomElement()!
@State var playerSelection: RPSChoice? = nil
@State var playerWon = false
@State var showResult = false
func reset() {
showResult = false
playerWon = false
playerSelection = nil
goalIsToWin = !goalIsToWin
computerSelection = RPSChoice.allCases.randomElement()!
}
func makeSelection(selection: RPSChoice) {
playerSelection = selection
if (computerSelection == RPSChoice.rock) {
if (playerSelection == RPSChoice.paper) {
playerWon = true
}
}
if (computerSelection == RPSChoice.paper) {
if (playerSelection == RPSChoice.scissors) {
playerWon = true
}
}
if (computerSelection == RPSChoice.scissors) {
if (playerSelection == RPSChoice.rock) {
playerWon = true
}
}
if (playerWon && goalIsToWin) {
score += 1
}
numQuestions += 1
showResult = true
}
var body: some View {
VStack {
Text("Rock, Paper, Scissors")
.font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/)
Spacer()
Text("Opponent has played")
.font(.title2)
Image(systemName: computerSelection.rawValue)
.font(.largeTitle)
.foregroundStyle(.tint)
Spacer()
if (goalIsToWin) {
Text("Your goal is to WIN")
.font(.title2)
Image(systemName: "checkmark.circle")
.font(.largeTitle)
.foregroundStyle(Color.green)
} else {
Text("Your goal is to LOOSE")
Image(systemName: "exclamationmark.triangle")
.font(.largeTitle)
.foregroundStyle(Color.red)
}
Spacer()
Text("Make your choice")
.font(.title2)
HStack {
ForEach(RPSChoice.allCases, id: \.rawValue) { choice in
Button {
makeSelection(selection: choice)
} label: {
Image(systemName: choice.rawValue)
.font(.largeTitle)
.tint(.white)
}
.imageScale(.large)
.foregroundStyle(.tint)
.frame(width: 75, height: 35)
.padding()
.shadow(radius: 5)
.background(.blue)
.clipShape(.capsule)
Spacer()
}
}
}
.padding()
.background(
Rectangle().fill(Color(white: 0.9, opacity: 0.7).gradient).ignoresSafeArea()
)
.alert(playerWon ? "You WON" : "You LOST", isPresented: $showResult) {
Button("Continue", action: reset)
} message: {
Text("Your score is \(score)")
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// WordScramble
//
// Created by cseibert on 12/31/23.
//
import SwiftUI
struct ContentView: View {
let MIN_LENGTH = 3
@State private var usedWords = [String]()
@State private var rootWord = ""
@State private var newWord = ""
@State private var score = 0
@State private var errorTitle = ""
@State private var errorMessage = ""
@State private var showingError = false
func startGame() {
usedWords = [String]()
newWord = ""
score = 0
if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt") {
if let startWords = try? String(contentsOf: startWordsURL) {
let allWords = startWords.components(separatedBy: "\n")
rootWord = allWords.randomElement() ?? "silkworm"
return
}
}
fatalError("Could not load start.txt from bundle.")
}
func addNewWord() {
let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
guard answer.count > 0 else { return }
guard isOriginal(word: answer) else {
wordError(title: "Word used already", message: "Be more original")
return
}
guard isPossible(word: answer) else {
wordError(title: "Word not possible", message: "You can't spell that word from '\(rootWord)'!")
return
}
guard isReal(word: answer) else {
wordError(title: "Word not recognized", message: "You can't just make them up, you know!")
return
}
guard isLongEnough(word: answer) else {
wordError(title: "Word is too short", message: "You can't use words that are less than \(MIN_LENGTH) characters long")
return
}
withAnimation {
usedWords.insert(answer, at: 0)
}
score += answer.count
newWord = ""
}
func isOriginal(word: String) -> Bool {
!usedWords.contains(word)
}
func isPossible(word: String) -> Bool {
var tempWord = rootWord
for letter in word {
if let pos = tempWord.firstIndex(of: letter) {
tempWord.remove(at: pos)
} else {
return false
}
}
return true
}
func isReal(word: String) -> Bool {
let checker = UITextChecker()
let range = NSRange(location: 0, length: word.utf16.count)
let misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")
return misspelledRange.location == NSNotFound
}
func isLongEnough(word: String) -> Bool {
return word.count >= MIN_LENGTH
}
func wordError(title: String, message: String) {
errorTitle = title
errorMessage = message
showingError = true
}
var body: some View {
NavigationStack {
VStack {
HStack {
Text("\"\(rootWord)\"")
if (score > 0) {
Text("\(score) points")
.foregroundColor(.green)
}
}
.font(.title)
List {
Section("Words") {
TextField("Enter your word", text: $newWord)
.textInputAutocapitalization(.never)
.onSubmit(addNewWord)
}
Section {
ForEach(usedWords, id: \.self) { word in
HStack {
Image(systemName: "\(word.count).circle")
Text(word)
}
}
}
}
.scrollContentBackground(.hidden)
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.teal.opacity(0.33).gradient)
.navigationTitle("Word Maker")
.toolbar {
if (!usedWords.isEmpty) {
Button("Reset") {
startGame()
}
}
}
}
.onAppear(perform: startGame)
.alert(errorTitle, isPresented: $showingError) { } message: {
Text(errorMessage)
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// BetterRest
//
// Created by cseibert on 12/30/23.
//
import CoreML
import SwiftUI
struct ContentView: View {
static var defaultWakeTime: Date {
var components = DateComponents()
components.hour = 7
components.minute = 0
return Calendar.current.date(from: components) ?? .now
}
@State private var wakeUp = defaultWakeTime
@State private var sleepAmount = 8.0
@State private var coffeeAmount = 4
@State private var alertTitle = ""
@State private var alertMessage = ""
@State private var showingAlert = false
var calculateBedtime: String {
do {
let config = MLModelConfiguration()
let model = try SleepCalculator(configuration: config)
let components = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
let hour = (components.hour ?? 0) * 60 * 60
let minute = (components.minute ?? 0) * 60
let prediction = try model.prediction(wake: Int64(hour + minute), estimatedSleep: sleepAmount, coffee: Int64(coffeeAmount))
let sleepTime = wakeUp - prediction.actualSleep
alertTitle = "Your ideal bedtime is…"
return sleepTime.formatted(date: .omitted, time: .shortened)
} catch {
alertTitle = "Error"
return "Sorry, there was a problem calculating your bedtime."
}
}
var body: some View {
NavigationStack {
VStack {
Form {
Section("When do you want to wake up?") {
DatePicker("Please enter a time", selection: $wakeUp, displayedComponents: .hourAndMinute)
.labelsHidden()
}
Section("Desired amount of sleep") {
Stepper("\(sleepAmount.formatted()) hours", value: $sleepAmount, in: 4...12, step: 0.25)
}
Section {
Picker("Daily coffee intake", selection: $coffeeAmount) {
ForEach(1...20, id: \.self) {
Text("^[\($0) cup](inflect: true)").tag($0)
}
}
}
}
VStack {
Text(calculateBedtime).font(.title)
Text("Daily Bed Time").font(.headline)
}
.frame(alignment: .center)
}
.navigationTitle("BetterRest")
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// Animations
//
// Created by cseibert on 1/1/24.
//
import SwiftUI
struct ContentView: View {
@State private var animationAmount = 0.0
@State private var enabled = false
var body: some View {
Button("Tap Me") {
withAnimation(.spring(duration: 1, bounce: 0.5)) {
animationAmount += 360
}
}
.padding(50)
.background(.red)
.foregroundStyle(.white)
.clipShape(.circle)
.rotation3DEffect(.degrees(animationAmount), axis: (x: 1, y: 0, z: 0))
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// Multiplication
//
// Created by cseibert on 1/2/24.
//
import SwiftUI
// TODO
// Ask one question at a time
// Scale up/down based on whether they are getting correct answers
// Enter answers w/ giant buttons where it tells you how many digits are required
// Animation on correct answers
struct ContentView: View {
@State private var difficultly = 2 // 2...12
@State private var numQuestions = 0
@State private var firstNumber = 0
@State private var secondNumber = 0
@State private var answer = 0
@State private var answerCharacters = [nil, nil, nil]
@State private var answerCorrect = false
@State private var numberButtonSelected = 0
@State private var partialAnswer = 0
@State private var gameOver = false
@State private var backgroundColor = Color.white.gradient
@State private var animationAmount = 0.0
@State private var numPad = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[0, ],
]
func resetGame() {
numQuestions = 0
resetQuestion()
}
func resetQuestion() {
gameOver = false
if answerCorrect {
difficultly = min(difficultly + 1, 12)
}
firstNumber = Int.random(in: 2...difficultly)
secondNumber = Int.random(in: 2...difficultly)
answer = firstNumber * secondNumber
answerCharacters = [nil, nil, nil]
answerCorrect = false
numberButtonSelected = 0
partialAnswer = 0
backgroundColor = Color.white.gradient
}
func enterNumber(number: Int) {
partialAnswer = partialAnswer * 10 + number
if partialAnswer == answer {
answerCorrect = true
gameOver = true
} else if String(partialAnswer).count >= String(answer).count {
answerCorrect = false
gameOver = true
}
}
private var answerLength: Int {
return String(answer).count
}
private var partialAnswerWithPlaceholder: String {
if partialAnswer == 0 {
return "_"
}
return String(partialAnswer)
// TODO: return "2 _ _" for a three-digit answer with one digit entered, etc
}
var body: some View {
NavigationStack {
VStack {
Spacer()
Section {
Text("What is the answer?")
.font(.headline)
.foregroundStyle(Color.gray)
Text("\(firstNumber) x \(secondNumber)")
.font(.largeTitle)
.fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
.foregroundStyle(Color.black)
}
Spacer()
Section {
Text("It has ^[\(answerLength) number](inflect: true) in it")
.font(.headline)
.foregroundStyle(Color.gray)
HStack {
Text(partialAnswerWithPlaceholder)
}
.font(.largeTitle)
}
Spacer()
VStack(spacing: 20) {
ForEach(numPad, id: \.self) { row in
HStack(spacing: 40) {
ForEach(row, id: \.self) { number in
Button("\(number)") {
enterNumber(number: number)
if gameOver {
withAnimation(.spring(duration: 1.5, bounce: 0.25)) {
if answerCorrect {
backgroundColor = Color.green.gradient
} else {
backgroundColor = Color.red.gradient
}
animationAmount += 360
} completion: {
resetGame()
}
}
}
.font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/)
.padding()
.frame(width:80, height: 60)
.background(.white)
.clipShape(.capsule)
.shadow(radius: 5)
}
}
}
}
.rotation3DEffect(.degrees(animationAmount), axis: (x: 0, y: 1, z: 0))
}
.navigationTitle("Multiplication")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(backgroundColor)
}
.onAppear(perform: resetGame)
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// iExpense
//
// Created by cseibert on 1/5/24.
//
import SwiftUI
import Observation
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
SecondView(name: "@twostraws")
}
}
}
struct SecondView: View {
let name: String
@Environment(\.dismiss) var dismiss
var body: some View {
Button("Dismiss") {
dismiss()
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// iExpense
//
// Created by cseibert on 1/5/24.
//
import SwiftUI
struct ContentView: View {
@State private var numbers = [Int]()
@State private var currentNumber = 1
func removeRows(at offsets: IndexSet) {
numbers.remove(atOffsets: offsets)
}
var body: some View {
NavigationStack {
VStack {
List {
ForEach(numbers, id: \.self) {
Text("Row \($0)")
}
.onDelete(perform: removeRows)
}
Button("Add Number") {
numbers.append(currentNumber)
currentNumber += 1
}
}
.toolbar {
EditButton()
}
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// iExpense
//
// Created by cseibert on 1/5/24.
//
import SwiftUI
struct ContentView: View {
// for full control use this
@State private var tapCount2 = UserDefaults.standard.integer(forKey: "Tap2")
// for simple situations use this
@AppStorage("tapCount") private var tapCount = 0
// or persist custom types like this (Codable)
struct User: Codable {
let firstName: String
let lastName: String
}
@State private var user = User(firstName: "Taylor", lastName: "Swift")
var body: some View {
Button("Tap count: \(tapCount)") {
tapCount += 1
// don't need this with the AppStorage verison!
UserDefaults.standard.set(tapCount, forKey: "Tap")
// persist the Codable version
Button("Save User") {
let encoder = JSONEncoder()
if let data = try? encoder.encode(user) {
UserDefaults.standard.set(data, forKey: "UserData")
}
}
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// iExpense
//
// Created by cseibert on 1/5/24.
//
import SwiftUI
struct ExpenseItem: Identifiable, Codable {
var id = UUID()
let name: String
let type: String
let amount: Double
}
@Observable
class Expenses {
var items = [ExpenseItem]() {
didSet {
if let encoded = try? JSONEncoder().encode(items) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
init() {
if let savedItems = UserDefaults.standard.data(forKey: "Items") {
if let decodedItems = try? JSONDecoder().decode([ExpenseItem].self, from: savedItems) {
items = decodedItems
return
}
}
items = []
}
}
struct ContentView: View {
@State private var expenses = Expenses()
@State private var showingAddExpense = false
func removeItems(at offsets: IndexSet) {
expenses.items.remove(atOffsets: offsets)
}
func expenseColor(amount: Double) -> Color {
if amount >= 100.0 {
return Color.red
}
if amount >= 10.0 {
return Color.green
}
return Color.black
}
var body: some View {
NavigationStack {
List {
ForEach(expenses.items) { item in
HStack {
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text(item.type)
}
Spacer()
Text(item.amount, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
.foregroundStyle(expenseColor(amount: item.amount))
}
}
.onDelete(perform: removeItems)
}
.navigationTitle("iExpense")
.toolbar {
Button("Add Expense", systemImage: "plus") {
showingAddExpense = true
}
}
.sheet(isPresented: $showingAddExpense) {
AddView(expenses: expenses)
}
}
}
}
#Preview {
ContentView()
}
//
// AddView.swift
// iExpense
//
// Created by cseibert on 1/15/24.
//
import SwiftUI
struct AddView: View {
@State private var name = ""
@State private var type = "Personal"
@State private var amount = 0.0
let types = ["Business", "Personal"]
var expenses: Expenses
@Environment(\.dismiss) var dismiss
var body: some View {
NavigationStack {
Form {
TextField("Name", text: $name)
Picker("Type", selection: $type) {
ForEach(types, id: \.self) {
Text($0)
}
}
TextField("Amount", value: $amount, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
.keyboardType(.decimalPad)
}
.navigationTitle("Add new expense")
.toolbar {
Button("Save") {
let item = ExpenseItem(name: name, type: type, amount: amount)
expenses.items.append(item)
dismiss()
}
}
}
}
}
#Preview {
AddView(expenses: Expenses())
}
//
// ContentView.swift
// Moonshot
//
// Created by cseibert on 1/20/24.
//
import SwiftUI
struct ContentView: View {
var body: some View {
Image(.chaseSelfPortrait0605)
.resizable()
.scaledToFit()
.containerRelativeFrame(.horizontal) { size, axis in
size * 0.8
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// Moonshot
//
// Created by cseibert on 1/20/24.
//
import SwiftUI
struct CustomText: View {
let text: String
var body: some View {
Text(text)
}
init(_ text: String) {
print("Creating a new CustomText")
self.text = text
}
}
struct ContentView: View {
var body: some View {
ScrollView {
LazyVStack(spacing: 10) {
ForEach(0..<100) {
CustomText("Item \($0)")
.font(.title)
}
}
.frame(maxWidth: .infinity)
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// Moonshot
//
// Created by cseibert on 1/20/24.
//
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationStack {
List(0..<100) { row in
NavigationLink("Row \(row)") {
Text("Detail \(row)")
}
}
.navigationTitle("SwiftUI")
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// Moonshot
//
// Created by cseibert on 1/20/24.
//
import SwiftUI
struct User: Codable {
let name: String
let address: Address
}
struct Address: Codable {
let street: String
let city: String
}
struct ContentView: View {
var body: some View {
Button("Decode JSON") {
let input = """
{
"name": "Taylor Swift",
"address": {
"street": "555, Taylor Swift Avenue",
"city": "Nashville"
}
}
"""
let data = Data(input.utf8)
let decoder = JSONDecoder()
if let user = try? decoder.decode(User.self, from: data) {
print(user.address.street)
}
}
}
}
#Preview {
ContentView()
}
//
// ContentView.swift
// Moonshot
//
// Created by cseibert on 1/20/24.
//
import SwiftUI
struct ContentView: View {
let layout = [
GridItem(.adaptive(minimum: 80, maximum: 120)),
]
var body: some View {
ScrollView {
LazyVGrid(columns: layout) {
ForEach(0..<1000) {
Text("Item \($0)")
}
}
}
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment