Delete Func Deletes All Items in List - SwiftUI - swiftui

When I select one of the items inside my list from my lists it only deletes the selected item.
But when I list all the lists and their reminders inside the AllView it deletes all of the reminders inside the list.
How can I overcome that problem?
To tell my problem clearly I have two videos that show both cases.
First Case
Second case
it is my delete button inside ReminderCell view
struct ReminderCell: View {
#Environment(\.managedObjectContext) private var viewContext
var reminder: CDReminder
#State var isSelected: Bool
Button(action: {
self.isSelected = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1){
deleteReminder(at: Int(reminder.index))
}
and again inside the ReminderCell I have deleteReminder func
func deleteReminder(at offsets: Int) {
viewContext.delete(reminder)
PersistenceController.shared.saveContext()
}
Inside the AllView I am calling listDetailCell as
struct AllView: View {
#State var title = ""
#State var note = ""
#State var releaseDate = Date()
#ObservedObject var list : CDListModel
#State var selectedList = CDListModel()
#Environment(\.presentationMode) var mode
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest var lists: FetchedResults<CDListModel>
init(){
list = CDListModel()
let request: NSFetchRequest<CDListModel> = CDListModel.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \CDListModel.name, ascending: true)]
request.entity = CDListModel.entity()
_lists = FetchRequest(fetchRequest: request)
}
var body: some View {
List{
ForEach(lists, id: \.self) { list in
ListDetailCell(list: list)
}
}
}
My ListDetailCell
struct ListDetailCell: View {
#State var title = ""
#ObservedObject var list : CDListModel
#State var selectedList: CDListModel!
#State var isAddReminderTapped = false
#Environment(\.managedObjectContext) private var viewContext
var body: some View {
VStack(alignment: .leading){
Text(list.text ?? "")
ForEach((list.reminders?.allObjects as! [CDReminder]).indices , id: \.self) { reminderIndex in
ReminderCell(reminder: (list.reminders?.allObjects[reminderIndex]) as! CDReminder, isSelected: false, selectedList: $selectedList, onComplete: {})
}
}
}
}

Your delete function is wrong.
Here you are passing an offsets: Int. But you are never using that offsets inside the function. You are just deleting the whole reminder.
func deleteReminder(at offsets: Int) {
viewContext.delete(reminder)
PersistenceController.shared.saveContext()
}

Somehow using ForEach inside the List was causing this problem in AllView.
When I change the body of the AllView like below my problem disappeared.
NavigationView {
ScrollView {
VStack{
HStack{
Text("Tumu")
.font(.system(size: 40, weight: .bold, design: .rounded))
.foregroundColor(.gray)
Spacer()
}
.padding(.leading)
ForEach(lists, id: \.self) { list in
ListDetailCell(list: list)
}

Related

SwiftUI: NavigationView focus on last selected NavigationLink

maybe a very simple problem:
I use a navigation with a long list of entries. If the user returns from the navigationLink the list starts on the first item. How can I set the focus on the last selected navigationLink so the user don't need to scroll from the beginning again.
My app is for blind people so the scrolling from above isn't an easy thing.
´´´
struct CategoryDetailView: View {
#EnvironmentObject var blindzeln: BLINDzeln
#AppStorage ("version") var version: Int = 0
#State var shouldRefresh: Bool = false
#State private var searchText = ""
let categoryTitle: String
let catID: Int
var body: some View {
VStack{
List {
ForEach(blindzeln.results.filter { searchText.isEmpty || ($0.title.localizedCaseInsensitiveContains(searchText) || $0.textBody.localizedCaseInsensitiveContains(searchText)) }, id: \.entryID){ item in
NavigationLink(destination: ItemDetailViewStandard(item: item, isFavorite: false, catID: catID)) {DisplayEntryView(item: item, catID: catID)}.listRowSeparatorTint(.primary).listRowSeparator(.hidden)
}
}
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "") {}
.navigationTitle(categoryTitle)
.navigationBarTitleDisplayMode(.inline)
.listStyle(.inset)
}
.task(){
await blindzeln.decodeCategoryData(showCategory: categoryTitle)
}
.onAppear(){
blindzeln.resetData()
}
}
}
´´´
you could try this approach, using the List with selection, such
as in this example code. It does not scroll back to the beginning of the list
after selecting a destination.
struct ContentView: View {
#State private var selections = Set<Thing>()
#State var things: [Thing] = []
var body: some View {
NavigationStack {
List(things, selection: $selections){ thing in
NavigationLink(destination: Text("destination-\(thing.val)")) {
Text("item-\(thing.val)")
}
}
}
.onAppear {
(0..<111).forEach{things.append(Thing(val: $0))}
}
}
}
EDIT-1:
Since there are so many elements missing from you code, I can only guess
and suggest something like this:
struct CategoryDetailView: View {
#EnvironmentObject var blindzeln: BLINDzeln
#AppStorage ("version") var version: Int = 0
#State var shouldRefresh: Bool = false
#State private var searchText = ""
#State private var selections = Set<Thing>() // <-- same type as item in the List
let categoryTitle: String
let catID: Int
var body: some View {
VStack {
// -- here
List(blindzeln.results.filter { searchText.isEmpty || ($0.title.localizedCaseInsensitiveContains(searchText) || $0.textBody.localizedCaseInsensitiveContains(searchText)) },
id: \.entryID,
selection: $selections){ item in
NavigationLink(destination: ItemDetailViewStandard(item: item, isFavorite: false, catID: catID)) {
DisplayEntryView(item: item, catID: catID)
}
.listRowSeparatorTint(.primary).listRowSeparator(.hidden)
}
}
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "") {}
.navigationTitle(categoryTitle)
.navigationBarTitleDisplayMode(.inline)
.listStyle(.inset)
.task{
await blindzeln.decodeCategoryData(showCategory: categoryTitle)
}
.onAppear{
blindzeln.resetData()
}
}
}

In SwiftUI how do I refer to a view to perform a property on it?

I have a SearchBar that I've added a dismiss property to. dismiss is used by the cancel button, but also might be used by the parent view when displaying a sheet. How do I define the SearchBar in the parent view to be able to reference the dismiss property?
The relevant parts of the SearchBar look like this:
struct SearchBar: View {
#Binding var text: String
#Binding var isSearching: Bool
let prompt: String
var body: some View {
...
}
var dismiss: Void {
// dismiss the keyboard
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
isSearching = false
text = ""
}
}
I envisioned the parent SearchView to look something like this:
struct SearchView: View {
#State private var isShowingDataView = false
#State private var searchText = ""
#State private var isSearching = false
let prompt = "Search"
#State private var searchBar: View
var body: some View {
VStack(alignment: .leading) {
searchBar = SearchBar(text: $searchText, isSearching: $isSearching, prompt: prompt)
...
Button(action: {
showData(data: data)
}) {
HStack {
...
}
}
}
}
func showData(data: Data) {
dataShowing = data
if isSearching {
searchBar.dismiss
}
isShowingDataView = true
}
}
With the above I get the error:
"Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements"
on the searchBar definition line, and
"Type '()' cannot conform to 'View'"
on the VStack line.
dismiss should be a method on your Searchview, and you should pass a closure to SearchBar for it to call when its cancel button is tapped.
struct SearchBar: View {
#Binding var text: String
let prompt: String
let isSearching: Bool
let cancel: () -> Void
var body: some View {
HStack {
TextField(prompt, text: $text)
Button("Cancel", action: cancel)
}
.disabled(!isSearching)
}
}
struct SearchView: View {
#State private var isShowingDataView = false
#State private var searchText = ""
#State private var isSearching = false
let prompt = "Search"
var body: some View {
VStack(alignment: .leading) {
SearchBar(
text: $searchText,
prompt: prompt,
isSearching: $isSearching,
cancel: self.cancelSearch
)
Button(action: {
showData(data: data)
}) {
HStack {
...
}
}
}
}
private func showData(data: Data) {
dataShowing = data
cancelSearch()
isShowingDataView = true
}
private func cancelSearch() {
guard isSearching else { return }
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
isSearching = false
text = ""
}
}

Can't transfer variable from one watchos view to another view, Using swiftui

I am trying to get data from one view to another.
I can not figure out how to get values from the fourth view array into the Third view.
I am not using storyboards. I tried using #EnvironmentObject but can not make it work. New to coding. In xcode I am using watchos without app.
I tried to strip out most of the code and leave just the important stuff that can be tested. I used NavigationLink(destination: )to transfer between views.
enter code here
class viewFromEveryWhere: ObservableObject {
#Published var testname2: String = "testTTname"
}
struct secondView: View {
var body: some View {
Text("second view")
List(1..<7) {
Text("\($0)")
}
}
}
struct thirdView: View {
#EnvironmentObject var testname2: viewFromEveryWhere
#EnvironmentObject var testSixTestArray: viewFromEveryWhere
#State var sixTestArray:[String] = ["ww","GS","DW","CD","TS","JW",]
var body: some View {
List(sixTestArray, id:\.self) {
Text($0)
}
}
}
struct fourthView: View {
#StateObject var testname2 = viewFromEveryWhere()
#State private var name: String = ""
#State var testSixTestArray:[String] = []
func collectName () {
print("collectName triggered")
if testSixTestArray.count < 5 {
// testSixTestArray.append(name)
print(name)
print(testSixTestArray)
}
// .enviromentObject(testSixTestArray)
}
var body: some View {
VStack(alignment: . leading) {
Text("Type a name")
TextField("Enter your name", text: $name)
Text("Click to add, \(name)!")
// Button("click this if \(name) is correct") {}
Button(action:{
print("Button Tapped")
collectName()
print(testSixTestArray.count)
name = ""
}) {
Text("Add \(name) to list")
}
// .buttonStyle(GrowingButton1())
}
Text("forth view")
// testSixTestArray.append(name)
.environmentObject(testname2)
}
}
/*func presentTextInputControllerWithSuggestions(forLanguage suggestionsHandler:
((String)-> [Any]?)?,
allowedInputMode inputMode:
WKTextInputMode,
completion: #escaping ([Any]?) -> Void) {}
*/
struct ContentView: View {
#State var sixNameArray:[String] = ["","","","","","",]
#State var messageTextBox: String = "Start"
#State var button1: String = "Button 1"
#State var button2: String = "Button 2"
#State var button3: String = "Button 3"
var body: some View {
NavigationView {
VStack{
Text(messageTextBox)
.frame(width: 120, height: 15, alignment: .center)
.truncationMode(.tail)
.padding()
NavigationLink(destination: secondView(),
label:{
Text(button1)
})
.navigationBarTitle("Main Page")
NavigationLink(destination: thirdView(),
label:{
Text(button2)
})
NavigationLink(destination: fourthView(),
label:{
Text(button3)
})
}
}
}
}
enter code here

Wrong List Item Selected in NavigationLink

I'm trying to build out a simple navigation where you can click on items in a link and pop back to the root controller from a sheet view. As you can see from the video below, when I tap on an item in the list, the wrong item is loaded (there's an offset between the row I click and the one that gets highlighted and loaded).
I also get the error SwiftUI encountered an issue when pushing aNavigationLink. Please file a bug.
Here's all my code:
import SwiftUI
struct ContentView: View {
#State var rootIsActive:Bool = false
var body: some View {
NavigationView{
AllProjectView(rootIsActive: self.rootIsActive)
}
.navigationBarTitle("Root")
.navigationViewStyle(StackNavigationViewStyle())
.environment(\.rootPresentationMode, self.$rootIsActive)
}
}
struct AllProjectView: View {
#State var rootIsActive:Bool = false
#State var projects: [String] = ["1", "2", "3"]
var body: some View{
List{
ForEach(projects.indices, id: \.self){ idx in
ProjectItem(name: self.$projects[idx], rootIsActive: self.$rootIsActive)
}
}.navigationBarTitle("All Projects")
}
}
struct ProjectItem: View{
#Binding var name: String
#Binding var rootIsActive: Bool
init(name: Binding<String>, rootIsActive: Binding<Bool>){
self._name = name
self._rootIsActive = rootIsActive
}
var body: some View{
NavigationLink(
destination: ProjectView(name: self.name),
isActive: self.$rootIsActive){
Text(name)
}
.isDetailLink(false)
.padding()
}
}
struct ProjectView: View {
var name: String
#State var isShowingSheet: Bool = false
#Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
#Environment(\.rootPresentationMode) private var rootPresentationMode: Binding<RootPresentationMode>
var body: some View{
VStack{
Text(name)
Button("Show Sheet"){
self.isShowingSheet = true
}
}
.sheet(isPresented: $isShowingSheet){
Button("return to root"){
self.isShowingSheet = false
print("pop view")
self.presentationMode.wrappedValue.dismiss()
print("pop root")
self.rootPresentationMode.wrappedValue.dismiss()
}
}
.navigationBarTitle("Project View")
}
}
// from https://stackoverflow.com/a/61926030/1720985
struct RootPresentationModeKey: EnvironmentKey {
static let defaultValue: Binding<RootPresentationMode> = .constant(RootPresentationMode())
}
extension EnvironmentValues {
var rootPresentationMode: Binding<RootPresentationMode> {
get { return self[RootPresentationModeKey.self] }
set { self[RootPresentationModeKey.self] = newValue }
}
}
typealias RootPresentationMode = Bool
extension RootPresentationMode {
public mutating func dismiss() {
self.toggle()
}
}
You only have one isRootActive variable that you're using. And, it's getting repeated for each item on the list. So, as soon as any item on the list is tapped, the isActive property for each NavigationLink turns to true.
Beyond that, your isRootActive isn't actually doing anything right now, since your "Return to root" button already does this:
self.isShowingSheet = false
self.presentationMode.wrappedValue.dismiss()
At that point, there's nothing more to dismiss -- it's already back at the root view.
My removing all of the root and isActive stuff, you get this:
struct ContentView: View {
var body: some View {
NavigationView{
AllProjectView()
}
.navigationBarTitle("Root")
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct AllProjectView: View {
#State var projects: [String] = ["1", "2", "3"]
var body: some View{
List{
ForEach(projects.indices, id: \.self){ idx in
ProjectItem(name: self.$projects[idx])
}
}.navigationBarTitle("All Projects")
}
}
struct ProjectItem: View{
#Binding var name: String
var body: some View{
NavigationLink(
destination: ProjectView(name: self.name)
){
Text(name)
}
.isDetailLink(false)
.padding()
}
}
struct ProjectView: View {
var name: String
#State var isShowingSheet: Bool = false
#Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
var body: some View{
VStack{
Text(name)
Button("Show Sheet"){
self.isShowingSheet = true
}
}
.sheet(isPresented: $isShowingSheet){
Button("return to root"){
self.isShowingSheet = false
print("pop view")
self.presentationMode.wrappedValue.dismiss()
}
}
.navigationBarTitle("Project View")
}
}
If you had an additional view in the stack, you would need a way to keep track of if the root were active. I've used a custom binding here that converts an optional String representing the project's name to a Bool value that gets passed down the view hierarchy:
struct ContentView: View {
var body: some View {
NavigationView{
AllProjectView()
}
.navigationBarTitle("Root")
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct AllProjectView: View {
#State var projects: [String] = ["1", "2", "3"]
#State var activeProject : String?
func activeBindingForProject(name : String) -> Binding<Bool> {
.init {
name == activeProject
} set: { newValue in
activeProject = newValue ? name : nil
}
}
var body: some View{
List{
ForEach(projects.indices, id: \.self){ idx in
InterimProjectView(name: self.$projects[idx],
isActive: activeBindingForProject(name: self.projects[idx]))
}
}.navigationBarTitle("All Projects")
}
}
struct InterimProjectView: View {
#Binding var name : String
#Binding var isActive : Bool
var body : some View {
NavigationLink(destination: ProjectItem(name: $name, isActive: $isActive),
isActive: $isActive) {
Text("Next : \(isActive ? "true" : "false")")
}
}
}
struct ProjectItem: View {
#Binding var name: String
#Binding var isActive: Bool
var body: some View{
NavigationLink(
destination: ProjectView(name: self.name, isActive: $isActive)
){
Text(name)
}
.isDetailLink(false)
.padding()
}
}
struct ProjectView: View {
var name: String
#Binding var isActive : Bool
#State var isShowingSheet: Bool = false
var body: some View{
VStack{
Text(name)
Button("Show Sheet"){
self.isShowingSheet = true
}
}
.sheet(isPresented: $isShowingSheet){
Button("return to root"){
self.isShowingSheet = false
print("pop root")
self.isActive.toggle()
}
}
.navigationBarTitle("Project View")
}
}

NavigationView inside NavigationView SwiftUI

How can I remove navigationBar inside navigationView.When I use navigate view inside another navigationview it show me another navigationbar and put space from navigationBar.How can I solve this problem . I tried to use navigationBarHidden or NavigationBarTitle (displaymode : .inline) but it didn't work.Its works when I use for one NavigationView but inside another navigationView its not working.
struct ShowCaseView : View {
var productList = ShowCaseViewModel()
#State var cancellable = Set<AnyCancellable>()
#State var productListData : ShowCaseDataResponse?
#State var isAnimating : Bool = true
#State var showCaseData : [ShowCaseData] = []
#State var isOpened : Bool = true
var body: some View {
ZStack{
NavigationView{
ScrollView {
VStack {
if productListData?.success == true {
ForEach(showCaseData , id:\.id) { data in
if data.isHeaderVisible == true {
Text(data.name ?? "")
.font(.system(size: 18))
.bold()
}
ListTypeShow(data: data)
}
}
}
}
}.navigationBarTitle("",displayMode: .inline)
ActivityIndicator(isAnimating: $isAnimating)
}
.onAppear {
if isOpened == true {
getStoreIndex()
}
}
}
If parent view of ShowCaseView already has NavigationView then you don't need another one in ShowCaseView, ie.
struct ShowCaseView : View {
var productList = ShowCaseViewModel()
#State var cancellable = Set<AnyCancellable>()
#State var productListData : ShowCaseDataResponse?
#State var isAnimating : Bool = true
#State var showCaseData : [ShowCaseData] = []
#State var isOpened : Bool = true
var body: some View {
ZStack{
// NavigationView{ // << remove this one !!