Hi,
I am making a list app which supports multiple lists, where each list can have multiple items. (typical reminders app)
I believe I have written the code correctly for adding a new list, but I am not sure how to pass the list to the 'tasks' view - because when I add a new task to the list, it does not update the view. i.e. Although the item is actually getting added, it is not seen as the view is not refreshed. If I leave the view and come back, the added item is seen.
Do I use a @Binding or @State? How?
(if the below link is hidden with '*', it is tiny***)
Screen Recording - https://developer.apple.com/forums/content/attachment/8987f011-7c2d-4910-8532-ab245e9fc99d
Code for the view which shows all lists:
struct TaskListView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: TaskList().entity, sortDescriptors: [NSSortDescriptor(keyPath: \TaskList.name, ascending: true)])
private var lists: FetchedResults<TaskList>
var body: some View {
List {
ForEach(lists, id: \.self) { list in
NavigationLink(destination: TaskItemsView(list: list)) {
Text(list.wrappedName)
}
}
}
.navigationBarItems(trailing: Button("Add") {
withAnimation {
addList()
}
})
.navigationTitle("Lists")
}
private func addList() {
let newList = TaskList(context: viewContext)
newList.name = "New List"
newList.dateCreated = Date()
let newTask = TaskItem(context: viewContext)
newTask.name = "New List Task"
newList.addToItems(newTask)
try? viewContext.save()
}
}
Code for view that shows items in a list:
struct TaskItemsView: View {
@Environment(\.managedObjectContext) var viewContext
var list: TaskList
var body: some View {
List {
ForEach(list.itemsArray, id: \.self) { item in
Text(item.wrappedName)
}
}
.navigationTitle(list.wrappedName)
.navigationBarItems(trailing: Button("Add") {
withAnimation {
addTask()
}
})
}
private func addTask() {
let newTask = TaskItem(context: viewContext)
newTask.name = "New Task in list \(list.wrappedName)"
list.addToItems(newTask)
try? viewContext.save()
}
}
Post
Replies
Boosts
Views
Activity
Hi,
I'm creating a To-do list app with SwiftUI. There's a main 'List' view with all the lists users create. When you tap on a list, it takes you to the Detail View with the items in the list. The problem is, when I make a change to the list in the Detail View, it doesn't change in the main list.
The changes are indeed getting saved, as if I restart the app, the changes are persistent.
I've looked at similar issues but none of the solutions seem to be working.
swift
struct HomeView: View {
@Environment(\.managedObjectContext) var viewContext
@FetchRequest(entity: ItemGroup().entity, sortDescriptors: [NSSortDescriptor(keyPath: \ItemGroup.createdAt, ascending: false)])
var groups: FetchedResultsItemGroup
var body: some View {
NavigationView {
List {
Section
{
ForEach(groups, id: \.self) { group in
NavigationLink(destination: DetailView(group: .constant(group)), label: {
GroupRow(group: group)
})
// ...
swift
struct GroupRow: View {
@ObservedObject var group: ItemGroup
var body: some View {
VStack(alignment: .leading) {
ProgressView(value: group.progress)
Text(group.wrappedName)
Text("\(group.ongoingItems.count) Ongoing")
}
}
}
swift
struct DetailView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Item().entity, sortDescriptors: [NSSortDescriptor(keyPath: \Item.createdAt, ascending: false)])
private var items: FetchedResultsItem
@Binding var group: ItemGroup
var body: some View {
...
Please suggest what might be the solution! Thanks!
Hi,
I am making a list app with SwiftUI. In the view which shows the list's items, each item has an info button which allows the user to edit the item. However, when I pass the item to that view, it does not pass the right item.
For example, I have a list of items:
Item 5 [Info Button]
Item 4 [Info Button]
Item 3 [Info Button]
Item 2. [Info Button]
Item 1. [Info Button]
If I tap on the Info Button for any one of the items, it will only show Item 5 in the Detail Edit View.
Here's the code:
swift
struct ItemsView: View {
@Environment(\.managedObjectContext) private var viewContext
@ObservedObject var group: ItemGroup
@State private var showingDetailView = false
var body: some View {
ScrollView {
VStack(alignment: .leading) {
ForEach(group.ongoingItems) { item in
ItemRow(for: item)
.padding()
}
}
func ItemRow(for item: Item) - some View {
HStack(spacing: 10) {
...
Text(item.wrappedName)
.fontWeight(.light)
Spacer()
Button(action: {
showingDetailView.toggle()
}, label: {
Image(systemName: "info.circle").imageScale(.large)
})
.sheet(isPresented: $showingDetailView, content: {
NavigationView {
ItemDetailView(selectedItem: item)
}
})
}
}
swift
struct ItemDetailView: View {
@Environment(\.managedObjectContext) var viewContext
@ObservedObject var selectedItem: Item
...
Hi,
Like in the stock reminders app, when you create a reminder on one device with a notification set, that notification will be sent to all devices. I am trying to build such a list app with SwiftUI, Core Data, and CloudKit.
Right now, I am using local notifications. How do I have the local notifications sync across devices using CloudKit?
Thanks.
Hi,
I am using matched geometry effect to animate small image views as the row expands when the user taps on it. I am getting the error AttributeGraph precondition failure: invalid value type for attribute: 3580720 (saw ViewTransform, expected Phase).
The error occurs randomly, either when switching into and out of different ItemsViews or when expanding different rows. The error is highlighted in the <AppName>App.swift file at the @main line at the top.
Here's the code. Please do help me I've been frustrated at this issue. I've seen similar issues on forums but none of the solutions have worked. I believe the issue is due to the matchedGeometryEffect itself, as if I remove those lines, the app doesn't crash.
// ItemsView.swift
...
var body: some View {
ScrollView {
VStack(alignment: .leading) {
...
ItemsView
}
}
}
@ViewBuilder
var ItemsView: some View {
ForEach(items) { item in
ItemRow(item: item)
}
}
// ItemRow.swift
@Namespace private var animation
@State private var isShowingDetails = false
var body: some View {
HStack {
...
ZStack(alignment: .trailing) {
...
ItemDetailsView
}
}
@ViewBuilder
var ItemDetailsView: some View {
if showingItemDetails {
ExpandedDetailView
} else {
CompactDetailView
}
}
var CompactDetailView: some View {
HStack(alignment: .center) {
if item.isFlagged {
Image(systemName: "flag.circle.fill")
.matchedGeometryEffect(id: "flag", in: animation)
.foregroundColor(.orange)
}
if !item.wrappedTags.isEmpty {
ForEach(item.wrappedTags) { itemTag in
Image(systemName: "tag.circle")
.foregroundColor(itemTag.wrappedColor)
.matchedGeometryEffect(id: "\(itemTag.objectID)", in: animation)
}
}
if item.reminder != nil {
Image(systemName: reminderIcon)
.matchedGeometryEffect(id: "reminder", in: animation)
.foregroundColor(item.isOverdue && item.wrappedReminderRepeatType == .none ? .red : .accentColor)
}
if item.wrappedNotes != "" {
Image(systemName: "note.text")
.matchedGeometryEffect(id: "notes", in: animation)
}
if !item.wrappedSubItems.isEmpty {
Image(systemName: "list.bullet")
.matchedGeometryEffect(id: "subItems", in: animation)
}
}
.foregroundColor(itemRowColor)
.imageScale(.medium)
}
var ExpandedDetailView: some View {
VStack(alignment: .leading, spacing: 5) {
if item.isFlagged {
HStack(alignment: .center) {
Image(systemName: "flag.circle.fill")
.matchedGeometryEffect(id: "flag", in: animation)
Text("Flagged".uppercased())
}
.foregroundColor(.orange)
}
ForEach(item.wrappedTags) { itemTag in
HStack(alignment: .center) {
Image(systemName: "tag.circle")
.foregroundColor(itemTag.wrappedColor)
.matchedGeometryEffect(id: "\(itemTag.objectID)", in: animation)
Text(itemTag.wrappedName.uppercased())
.fontWeight(.light)
}
.foregroundColor(itemTag.wrappedColor)
}
if item.reminder != nil {
HStack(alignment: .center) {
Image(systemName: reminderIcon)
.matchedGeometryEffect(id: "reminder", in: animation)
VStack(alignment: .leading) {
if item.wrappedReminderRepeatType != .none {
Text(item.wrappedReminderRepeatType.rawValue)
}
Text(item.reminder!, formatter: dateFormatter)
}
}
.foregroundColor(item.isOverdue && item.wrappedReminderRepeatType == .none ? .red : .accentColor)
}
if item.wrappedNotes != "" {
HStack(alignment: .center) {
Image(systemName: "note.text")
.matchedGeometryEffect(id: "notes", in: animation)
Text(item.wrappedNotes)
.fontWeight(.light)
.minimumScaleFactor(1.0)
.lineLimit(2)
.truncationMode(.tail)
}
}
if !item.wrappedSubItems.isEmpty {
HStack(alignment: .center) {
Image(systemName: "list.bullet")
.matchedGeometryEffect(id: "subItems", in: animation)
VStack(alignment: .leading) {
ForEach(item.wrappedSubItems) { subItem in
HStack(alignment: .center) {
Image(systemName: subItem.isCompleted ? "checkmark.circle.fill" : "circle")
.imageScale(.small)
Text(subItem.name)
}
}
}
}
}
}
.foregroundColor(itemRowColor)
.imageScale(.medium)
.padding(.top, 1)
}
Hello,
I want to be able to tap on a previously-placed ModelEntity box and add a dot or a text at that location on the box (kind of like I'm adding an annotation on the box)
I have something like this, but not sure how I should do it correctly:
class MyARView: ARView {
// ...
private func didTap(_ gestureRecognizer: UITapGestureRecognizer) {
let pos = gestureRecognizer.location(in: self)
if !didPlaceCube {
placeCube(pos)
return
}
let hitTestResult = self.hitTest(pos)
guard let firstResult = hitTestResult.first else { return}
let entity = firstResult.entity
let textEntity = ModelEntity(mesh: .generateText("Hello there", extrusionDepth: 0.4, font: .boldSystemFont(ofSize: 0.05), containerFrame: .zero, alignment: .center, lineBreakMode: .byWordWrapping))
textEntity.setPosition(entity.position + firstResult.position, relativeTo: entity)
entity.addChild(textEntity)
}
// ...
}