Post

Replies

Boosts

Views

Activity

Reply to Looping views SwiftUI
Rather than try to define all of your views upfront, define the models that power them. You can then easily iterate through your model to generate the desired views. Additionally, rather than create separate state for each possible sheet that can be presented, there is an alternate sheet modifier that accepts a binding to an Identifiable instance. This drastically reduces reduces boilerplate, and possible sources of bugs. Finally AnyView should only ever be a last resort, as it removes the compiler and SwiftUI's ability to optimize possibly destroying performance. If you find yourself using AnyView there is almost always a better way. struct Model: Identifiable { 		var id: String { title } 		let title: String } struct DetailView: View { 		let title: String 		var body: some View { 				Text(title) 		} } struct ContentView: View { 		@State private var selectedItem: Model? 		private var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible())] 		private let items = [ 				Model(title: "View1"), 				Model(title: "View2"), 				Model(title: "View3"), 				Model(title: "View4") 		] 		var textSize: CGFloat { 				if UIDevice.current.userInterfaceIdiom == .pad { 						return 48 				} 				return 23 		} 		var title: CGFloat { 				if UIDevice.current.userInterfaceIdiom == .pad { 						return 60 				} 				return 34 		} 		var height: CGFloat { 				if UIDevice.current.userInterfaceIdiom == .pad { 						return 0.15 				} 				return 0.15 		} 		var weight: CGFloat { 				if UIDevice.current.userInterfaceIdiom == .pad { 						return 0.44 				} 				return 0.43 		} 		var body: some View { 				NavigationView { 						GeometryReader { geometry in 								ScrollView(.vertical, showsIndicators: true) { 										LazyVGrid(columns: gridItemLayout, spacing: 18) { 												ForEach(items) { item in 														Text(item.title) 																.foregroundColor(.black) 																.frame(width: geometry.size.width * weight, height: geometry.size.height * height) 																.background(Color.white) 																.onTapGesture { 																		selectItem(item) 																} 												} 										} 										.padding() 								} 						} 						.navigationTitle("Title") 				} 				.sheet(item: $selectedItem, content: view(for:)) 		} 		private func selectItem(_ item: Model) { 				withAnimation { self.selectedItem = item } 		} 		@ViewBuilder 		private func view(for item: Model) -> some View { 				DetailView(title: item.title) 				// switch item.id { 				// case "View1": View1() 				// case "View2": View2() 				// case "View3": View3() 				// case "View4": View4() 				// default: EmptyView() 				// } 		} }
Dec ’20
Reply to need code-sample using "NumberFormatter"
I'm guessing you don't actually want an NSNumber as Swift provides it own number types (Int, Double, etc.). Here is an example that uses Int, though this can be simply modified by setting formatter.allowFloats = true and changing number's type to Double. struct ContentView: View { 		static let formatter: NumberFormatter = { 				let formatter = NumberFormatter() 				formatter.allowsFloats = false 				return formatter 		}() 		@State var number: Int = 0 		var body: some View { 				TextField( 						"Number Field", 						value: $number, 						formatter: Self.formatter, 						onCommit: { 								print(number) 						} 				) 				.keyboardType(.numberPad) 				.textFieldStyle(RoundedBorderTextFieldStyle()) 				.padding() 		} }
Jan ’21
Reply to SwiftUI update of deleted CoreData entities from different Tab
In your final example, you are passing myPersistenceObjectParent.myPersistenceObjects into the ForEach view. Because NSManagedObjects are not notified of changes in their to-many relationships, when you call context.delete(myPersistenceObject) it does not trigger an update to the parent on line 5 of the final example and does not cause the view to be recomputed. I believe if you instead called the generated parent.removeFromMyPersistenceObjects(myPersistentObject) before calling context.delete it would cause an update to be triggered The simple solution would be to also init a FetchRequest in this view to fetch the myPersistenceObjects rather than directly accessing the relationship property on myPersistenceObjectParent. Alternately you will need to create a new ObservableObject that takes in your parent, and observes changes to it's context to identify when its children are deleted/modified. EDIT: I implied this, but to be clear @FetchRequest monitors changes to its query and triggers a view update for you, thereby removing the need for a separate model conforming to ObservableObject
Jan ’21
Reply to SwiftUI update of deleted CoreData entities from different Tab
@bernhard-st Whether or not the managed object is faulted is not an indication of it has been deleted or not, it is only an indication of whether the object is currently loaded into memory, so it is not best practice to rely on that for this purpose. Additionally, constantly fetching on the .onAppear is very inefficient. There is a simple way to observe changes to the managedObjectContext through a Notification. struct NotifyOfDeletionView: View { &#9;&#9;@Environment(\.managedObjectContext) var viewContext &#9;&#9;@Environment(\.presentationMode) var presentationMode &#9;&#9;@ObservedObject var managedObject: NSManagedObject &#9;&#9;var body: some View { &#9;&#9;&#9;&#9;Text(managedObject.objectID.description) &#9;&#9;&#9;&#9;&#9;&#9;.onReceive( &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave, object: viewContext), &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;perform: dismissIfObjectIsDeleted(_:) &#9;&#9;&#9;&#9;&#9;&#9;) &#9;&#9;} &#9;&#9;private func dismissIfObjectIsDeleted(_ notification: Notification) { let user &#9;&#9;&#9;&#9;if let userInfo = notification.userInfo, &#9;&#9;&#9;&#9;&#9; let deletedObjectIDs = userInfo[NSManagedObjectContext.NotificationKey.deletedObjectIDs] as? Set<NSManagedObjectID>, &#9;&#9;&#9;&#9;&#9; deletedObjectIDs.contains(managedObject.objectID) /* The object was deleted */ &#9;&#9;&#9;&#9;{ &#9;&#9;&#9;&#9;&#9;&#9;/* This will dismiss a modal (sheet/fullScreenCover) or pop the top view of a NavagationView stack */ &#9;&#9;&#9;&#9;&#9;&#9;presentationMode.wrappedValue.dismiss() &#9;&#9;&#9;&#9;} &#9;&#9;} } Note that you don't need to pass in an presentView binding. The environment has a PresentationMode as shown above. The @FetchRequest approach I described previously is still a perfectly valid approach (and it is implemented under the hood using these Notifications or something similar), but there are still use cases for using Notifications like the above. Whatever tool feels right for your use case ; ) Finally, I would discourage storing state about the view (viewAppeared) like in your most recent comment. It will inevitably lead to bugs. In SwiftUI you should always drive your views using your model. The onAppear and related modifiers allow you to respond to events, but a view shouldn't be different based on whether or not it is currently on screen.
Jan ’21