I have a ForEach with a dynamic array with an .ondelete. The code works as expected. Now I want to switch to .swipeAction because I want to implement no fullSwipe.
.onDelete works with IndexSet, how can I implement this in a .swipeAction?
How get I rid of this error: "Cannot find 'indexSet' in scope"
List{
ForEach(0..<scan2Clip.saveList.count, id:\.self) { value in
if searchValue == "" {
if scan2Clip.saveList[value] != "" && searchValue == ""{
ShowLineView(value: value)
}
}
else if scan2Clip.saveList[value] != "" && scan2Clip.saveList[value].localizedCaseInsensitiveContains(searchValue){
ShowLineView(value: value)
}
} .swipeActions(edge: .trailing) {
Button(role: .destructive, action: {
self.scan2Clip.saveList.remove(atOffsets: indexSet)
} ) {
Label("Delete", systemImage: "trash")
}
}
// .onDelete{ (indexSet) in
// self.scan2Clip.saveList.remove(atOffsets: indexSet)
// }
}
``
Just give it single index, like
Button(role: .destructive, action: {
self.scan2Clip.saveList.remove(at: value) // << here !!
} ) {
Label("Delete", systemImage: "trash")
}
Related
Because the code for compositing context menu is a little lengthy, I want to code it like this:
var body: some View {
Text("Hello World")
.contextMenu(changingStatusID == nil ? contextMenu() : nil)
}
#ViewBuilder
func contextMenu() -> ContextMenu { //<-- Problem goes here, I don't know how to set the parameters and it's return type
ContextMenu {
Button {
edit()
} label: {
Label("Edit", systemImage: "rectangle.and.pencil.and.ellipsis")
}
Divider()
if #available(macOS 12.0, *) {
Button(role: .destructive) {
do {
try delete()
}
catch {
showAlert = true
}
} label: {
Label("Delete", systemImage: "trash")
}
.alert("Failed to delete.", isPresented: $showAlert) {
Button("OK", role: .cancel) { }
}
} else {
Button {
do {
try delete()
}
catch {
showAlert = true
}
} label: {
Label("Delete ⚠︎", systemImage: "trash")
}
.alert(isPresented: $showAlert) {
Alert(title: Text("Failed"),
message: Text("Unable to delete this task."),
dismissButton: .default(Text("OK")))
}
}
}
}
XCode shows error in the line of function name func contextMenu():
Cannot convert value of type 'ContextMenu<TupleView<(Button<Label<Text, Image>>, Divider, _ConditionalContent<AnyView, some View>)>>' to specified type '<<error type>>'
Reference to generic type 'ContextMenu' requires arguments in <...> Insert '<<#MenuItems: View#>>'
Static method 'buildBlock' requires that 'ContextMenu<TupleView<(Button<Label<Text, Image>>, Divider, _ConditionalContent<AnyView, some View>)>>' conform to 'View'
Here is an approach that works:
struct ContentView: View {
#State private var showContext = true
#State private var showAlert = false
var body: some View {
VStack {
Toggle("show contextMenu", isOn: $showContext)
Text("Hello World")
.contextMenu {
if showContext {
myContextMenu()
}
}
}
.padding()
}
#ViewBuilder
func myContextMenu() -> some View {
// ContextMenu { // not needed
Button {
//edit()
} label: {
Label("Edit", systemImage: "rectangle.and.pencil.and.ellipsis")
}
Divider()
Button(role: .destructive) {
do {
//try delete()
}
catch {
showAlert = true
}
} label: {
Label("Delete", systemImage: "trash")
}
.alert("Failed to delete.", isPresented: $showAlert) {
Button("OK", role: .cancel) { }
}
// }
}
}
I am building a complex View with a number of different TextFields. I've shown a simplified version below- the first code block builds and runs. When I add an identical VStack block to it- the second code block, it fails to run- I get "the compiler is unable to type-check" error. Is the a child limit issue? I wish XCode provided decent error messaging!
Working.....
var body: some View {
ScrollView() {
VStack{ // parts
ForEach(Array(stride(from: 0, to: 461, by: 33)), id: \.self) { index_v in
HStack {
VStack {
Group{
Text("resultsLine1")
Text(PartDisplay[index_v][0])
Text(db.sPart[index_v][1])
if (db.sSelectedMode == "detailMode") {
Text("resultsLine4D")
Text("\(db.sPart[index_v][16]) \(db.sPart[index_v][19]) \(db.sPart[index_v][18])")
Text("results")
Text("\(db.sPart[index_v][22])")
} else {
Text("resultsLine4B")
Text("\(db.sPart[index_v][16]) \(db.sPart[index_v][18])")
}
}
}
.padding(.bottom, 6)
VStack {
Group{
Text("resultsLine4")
Text(PartDisplay[index_v][2])
Text(db.sPart[index_v][3])
Text(db.sPart[index_v][4])
if (db.sSelectedMode == "detailMode") {
Text("resultsLine4D")
Text("\(db.sPart[index_v][1]) \(db.sPart[index_v][3]) \(db.sPart[index_v][8])")
Text("results4")
Text("\(db.sPart[index_v][22])")
} else {
Text("resultsLine4B")
Text("\(db.sPart[index_v][16]) \(db.sPart[index_v][18])")
}
}
}
.padding(.bottom, 6)
}
}
}
}
}
Not working..
var body: some View {
ScrollView() {
VStack{ // parts
ForEach(Array(stride(from: 0, to: 461, by: 33)), id: \.self) { index_v in
HStack {
VStack {
Group{
Text("resultsLine1")
Text(PartDisplay[index_v][0])
Text(db.sPart[index_v][1])
if (db.sSelectedMode == "detailMode") {
Text("resultsLine4D")
Text("\(db.sPart[index_v][16]) \(db.sPart[index_v][19]) \(db.sPart[index_v][18])")
Text("results")
Text("\(db.sPart[index_v][22])")
} else {
Text("resultsLine4B")
Text("\(db.sPart[index_v][16]) \(db.sPart[index_v][18])")
}
}
}
.padding(.bottom, 6)
VStack {
Group{
Text("resultsLine4")
Text(PartDisplay[index_v][2])
Text(db.sPart[index_v][3])
Text(db.sPart[index_v][4])
if (db.sSelectedMode == "detailMode") {
Text("resultsLine4D")
Text("\(db.sPart[index_v][1]) \(db.sPart[index_v][3]) \(db.sPart[index_v][8])")
Text("results4")
Text("\(db.sPart[index_v][22])")
} else {
Text("resultsLine4B")
Text("\(db.sPart[index_v][16]) \(db.sPart[index_v][18])")
}
}
}
.padding(.bottom, 6)
VStack {
Group{
Text("resultsLine1")
Text(PartDisplay[index_v][0])
Text(db.sPart[index_v][1])
if (db.sSelectedMode == "detailMode") {
Text("resultsLine14D")
Text("\(db.sPart[index_v][6]) \(db.sPart[index_v][9]) \(db.sPart[index_v][8])")
Text("results")
Text("\(db.sPart[index_v][2])")
} else {
Text("resultsLine4B")
Text("\(db.sPart[index_v][6]) \(db.sPart[index_v][18])")
}
}
}
.padding(.bottom, 6)
}
}
}
}
}
I would like to be able to popToRoot from my first tabItem (when im on page2 (from first tabItem) and tap twice on first tabItem when im on second tabItem) without having to use the NavigationViewKit package and only the new NavigationStack, is that possible?
My solution works fine but I would like to get rid of the NavigationViewKit package.
Any good advice\code example will be appreciated.
My code :
Home page
import SwiftUI
import NavigationViewKit
struct Home_V: View {
#State var tabSelected: Int = 0
#State private var tappedTwice: Bool = false
// https://designcode.io/swiftui-handbook-tabbar-to-root-view
var handler: Binding<Int> { Binding(
get: { tabSelected },
set: {
if $0 == tabSelected {
// print("tabSelected == \(tabSelected)")
tappedTwice = true
}
tabSelected = $0
}
)}
// https://github.com/fatbobman/NavigationViewKit
#Environment(\.navigationManager) var nvmanager
var body: some View {
TabView(selection: handler) {
NavigationStack {
Page1()
.onChange(of: tappedTwice, perform: { tappedTwice in
guard tappedTwice else { return }
if tabSelected == 0 {
self.tappedTwice = false
nvmanager.wrappedValue.popToRoot(tag:"Page1", animated: true){}
}
})
}
.tabItem {
Label("_HomeTitle", systemImage: "house")
.environment(\.symbolVariants, tabSelected == 0 ? .fill : .none)
}
.tag(0)
.navigationViewStyle(StackNavigationViewStyle())
NavigationStack {
Page2()
}
.tabItem {
Label("_MessagesTitle", systemImage: "envelope")
.environment(\.symbolVariants, tabSelected == 1 ? .fill : .none)
}
.tag(1)
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
Page1
import SwiftUI
import NavigationViewKit
struct Page1: View {
var body: some View {
VStack {
Text("Page 1")
NavigationLink {
Page2()
} label: {
Text("Go to Page 2")
}
}
.navigationViewManager(for: "Page1", afterBackDo: {print("Back to Page1")})
}
}
I got it!
Here is my test code :
import SwiftUI
class NavigationCoordinator: ObservableObject {
#Published var path = NavigationPath()
func popToRoot() {
path.removeLast(path.count)
}
}
struct Test_PopToRoot_NavigationStack: View {
#State private var tabSelected: Int = 0
#State private var tappedTwice: Bool = false
#StateObject var navigationCoordinator = NavigationCoordinator()
#StateObject var navigationCoordinator2 = NavigationCoordinator()
// https://designcode.io/swiftui-handbook-tabbar-to-root-view
var handler: Binding<Int> { Binding(
get: { tabSelected },
set: {
if $0 == tabSelected {
// print("tabSelected == \(tabSelected)")
tappedTwice = true
}
tabSelected = $0
}
)}
var body: some View {
TabView(selection: handler) {
NavigationStack(path: $navigationCoordinator.path) {
VStack {
NavigationLink(value: 1) {
Test_PopToRoot_Tabview1()
.foregroundColor(.black)
.onChange(of: tappedTwice, perform: { tappedTwice in
guard tappedTwice else { return }
if tabSelected == 0 {
self.tappedTwice = false
print("Home tapped twice!!!")
navigationCoordinator.popToRoot()
}
})
}
}
}
.environmentObject(navigationCoordinator)
.tabItem {
Label("_HomeTitle", systemImage: "house")
.environment(\.symbolVariants, tabSelected == 0 ? .fill : .none)
}
.tag(0)
.navigationViewStyle(StackNavigationViewStyle())
NavigationStack(path: $navigationCoordinator2.path) {
VStack {
NavigationLink(value: 1) {
Test_PopToRoot_Tabview2()
.foregroundColor(.black)
.onChange(of: tappedTwice, perform: { tappedTwice in
guard tappedTwice else { return }
if tabSelected == 1 {
self.tappedTwice = false
print("2nd Tab tapped twice!!!")
navigationCoordinator2.popToRoot()
}
})
}
}
}
.environmentObject(navigationCoordinator2)
.tabItem {
Label("_MessagesTitle", systemImage: "envelope")
.environment(\.symbolVariants, tabSelected == 1 ? .fill : .none)
}
.tag(1)
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
struct Test_PopToRoot_Tabview1: View {
var body: some View {
VStack {
NavigationLink(value: 2) {
Text("Go To Page2")
.foregroundColor(.black)
}
}
.navigationDestination(for: Int.self) { i in
Test_PopToRoot_Page2()
}
.navigationTitle(Text("Tabview1"))
}
}
I can't find the issue for this error. I don't think the compiler is giving a meaningful error message.
Why self.book.map {} does not work here?
var body: some View {
List {
ForEach(cards) { card in
if (!self.showMarkedOnly || card.marked) {
ZStack() {
CardView(card: card).frame(maxWidth: 400)
.contextMenu() {
if self.editMode {
Button(action: {}) { HStack { Image(systemName: "square.and.pencil"); Text("Edit") } }
Button(action: {}) { HStack { Image(systemName: "trash"); Text("Delete") } }
// self.book.map { Button(action: { self.onRemoveCard(card, fromBook: $0) }) { HStack { Image(systemName: "folder.badge.minus"); Text("Remove from current Book") } } } // ERROR happens here
if self.book != nil {
Button(action: { self.onRemoveCard(card, fromBook: self.book!) }) { HStack { Image(systemName: "folder.badge.minus"); Text("Remove from current Book") } }
} // <------- this works!!! but I prefer to not use "self.book!"
} else {} }
}
}
}
}
}
This is a case of nesting closures interfering with $0. Try this:
self.book.map { book in Button(action: { self.onRemoveCard(card, fromBook: book) }) { ... }
I don't know if it's possible. I would like that depending on what is selected in the segments the list changes Entity. Similar to what Apple does in its examples with Landmarks but instead of favorites ....
var body: some View {
VStack {
GeometryReader { geometry in
TabView {
NavigationView {
List {
VStack {
Picker(selection: self.$userData.tipoSeleccionado,
label: Text("....?")) {
ForEach(0..<self.Tipo.count) { index in
Text(self.Tipo[index]).tag(index)
}
} .pickerStyle(SegmentedPickerStyle())
}
if self.containedViewType == .tipo1 {
ForEach(self.listadoTipo1, id: \.self) { elemento in
NavigationLink(destination: Tipo1Detail(elementoSeleccionado: elemento)) {
Tipo1Row(tipo: elemento)
} .navigationBarTitle("Busqueda")
.navigationBarHidden(false)
} .onDelete(perform: self.removeTipo1)
}
if self.containedViewType == .tipo2 {
ForEach(self.listadoTipo2, id: \.self) { elemento in
NavigationLink(destination: Tipo2Detail(elementoSeleccionado: elemento)) {
Tipo2Row(tipo: elemento)
} .navigationBarTitle("Busqueda")
.navigationBarHidden(false)
} .onDelete(perform: self.removeTipo2)
}
} // List
}
}
}
}
}