Files
Hendrik Hogertz 527acd2967 Raise deployment target to iOS 18 and modernize SwiftUI APIs
Adopt modern SwiftUI patterns now that the minimum target is iOS 18:
NavigationStack, .toolbar, .tint, new Tab API with sidebarAdaptable
style, and remove iOS 17 availability checks. Add Liquid Glass effect
support for iOS 26 in TimerView and fix an optional interpolation
warning in AppState.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-14 23:14:57 +01:00

158 lines
4.2 KiB
Swift

//
// RecipeSectionStructureViews.swift
// Nextcloud Cookbook iOS Client
//
// Created by Vincent Meilinger on 01.03.24.
//
import Foundation
import SwiftUI
// MARK: - RecipeView Generic Editable View Elements
struct RecipeListSection: View {
@Binding var list: [String]
var body: some View {
VStack(alignment: .leading) {
ForEach(list, id: \.self) { item in
HStack(alignment: .top) {
Text("\u{2022}")
Text("\(item)")
.multilineTextAlignment(.leading)
}
.padding(4)
}
}
}
}
struct SecondaryLabel: View {
let text: LocalizedStringKey
var body: some View {
Text(text)
.foregroundColor(.secondary)
.font(.headline)
.padding(.vertical, 5)
}
}
struct EditableText: View {
@Binding var text: String
@Binding var editMode: Bool
@State var titleKey: LocalizedStringKey = ""
@State var lineLimit: ClosedRange<Int> = 0...1
@State var axis: Axis = .horizontal
var body: some View {
if editMode {
TextField(titleKey, text: $text, axis: axis)
.textFieldStyle(.roundedBorder)
.lineLimit(lineLimit)
} else {
Text(text)
}
}
}
struct EditableListView: View {
@Binding var isPresented: Bool
@Binding var items: [String]
@State var title: LocalizedStringKey
@State var emptyListText: LocalizedStringKey
@State var titleKey: LocalizedStringKey = ""
@State var lineLimit: ClosedRange<Int> = 0...50
@State var axis: Axis = .vertical
var body: some View {
NavigationStack {
ZStack {
List {
if items.isEmpty {
Text(emptyListText)
} else {
ForEach(items.indices, id: \.self) { ix in
TextField(titleKey, text: $items[ix], axis: axis)
.lineLimit(lineLimit)
.padding(5)
}
.onDelete(perform: deleteItem)
.onMove(perform: moveItem)
.scrollDismissesKeyboard(.immediately)
}
}
VStack {
Spacer()
Button {
addItem()
} label: {
Image(systemName: "plus")
.foregroundStyle(.white)
.bold()
.padding()
.background(Circle().fill(Color.nextcloudBlue))
}
.padding()
}
}
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button(action: { isPresented = false }) {
Text("Done")
}
}
}
.environment(\.editMode, .constant(.active))
}
}
private func addItem() {
withAnimation {
items.append("")
}
}
private func deleteItem(at offsets: IndexSet) {
withAnimation {
items.remove(atOffsets: offsets)
}
}
private func moveItem(from source: IndexSet, to destination: Int) {
withAnimation {
items.move(fromOffsets: source, toOffset: destination)
}
}
}
// MARK: - Previews
struct EditableListView_Previews: PreviewProvider {
// Sample keywords for preview
@State static var sampleList: [String] = [
/*"3 Eggs",
"1 kg Potatos",
"3 g Sugar",
"1 ml Milk",
"Salt, Pepper"*/
]
static var previews: some View {
Color.white
.sheet(isPresented: .constant(true), content: {
EditableListView(isPresented: .constant(true), items: $sampleList, title: "Ingredient", emptyListText: "Add cooking steps for fellow chefs to follow.")
})
}
}