Posts

Post not yet marked as solved
1 Replies
562 Views
How would one update the position of a SwiftUI Map without impacting the zoom (or distance from a MapCamera point of view). So want: a) map position being updated by incoming GPS co-ordinates b) user may then on the Map zoom in/out c) on subsequent GPS position changes I want to to keep the zoom/distance changes from the User and not reset these From the code below the the issue seems to be when getting the current "distance" (i.e. mapCamPost.camera?distance) that this value seems to go to "nil" after the User zooms in the map. struct GCMap: View { @StateObject var locationMgr = GcFlightState() @State private var mapCamPos: MapCameraPosition = .automatic var body: some View { ZStack { Map(position: $mapCamPos) { Annotation("UserLocation", coordinate: self.locationMgr.location.coordinate) { Image(systemName: "airplane.circle").rotationEffect(.degrees(270)) } } .onMapCameraChange() { print("onMapCameraChange \(mapCamPos.camera?.distance)") } .onReceive(locationMgr.$location) { location in mapCamPos = .camera(MapCamera( centerCoordinate: location.coordinate, distance: mapCamPos.camera?.distance ?? 1000, // <<=== heading: location.course )) } } } }
Posted
by callagga.
Last updated
.
Post marked as solved
2 Replies
740 Views
Anyone able to see how to use the new SwiftUI Map and WWDC @Observable concept to dynamically update my SwiftUI Map position and rotation based on the dynamic changes it picks up from my @Observable object. Note the updates are coming through as the Text labels show this. But how to get the Map position referencing the same values and updating them? The "onAppear" approach doesn't seem to work. import SwiftUI import MapKit @Observable final class NewLocationManager : NSObject, CLLocationManagerDelegate { var location: CLLocation? = nil var direction: CLLocationDirection = 0 private let locationManager = CLLocationManager() func startCurrentLocationUpdates() async throws { if locationManager.authorizationStatus == .notDetermined { locationManager.requestWhenInUseAuthorization() } for try await locationUpdate in CLLocationUpdate.liveUpdates() { guard let location = locationUpdate.location else { return } print("NewLocationManager: \(location.coordinate.latitude), \(location.coordinate.longitude)") self.location = location self.direction = self.direction + 1 } } } struct ContentView: View { var locationMgr = NewLocationManager() @State private var mapCamPos: MapCameraPosition = .automatic private let bigBen = CLLocationCoordinate2D(latitude: 51.500685, longitude: -0.124570) var body: some View { ZStack { Map(position: $mapCamPos) .onAppear { // Does NOT work - how to get position/direction updates working to Map (map should be moving/rotating) mapCamPos = .camera(MapCamera( centerCoordinate: self.locationMgr.location?.coordinate ?? bigBen, distance: 800, heading: self.locationMgr.direction )) } VStack (alignment: .leading) { Text("Location from observable: \(locationMgr.location?.description ?? "NIL")") // This works (they get updates regularly) Text("Direction from observable: \(locationMgr.direction)") // This works (they get updates regularly) Spacer() } } .task { try? await locationMgr.startCurrentLocationUpdates() } } } #Preview { ContentView() } Tag: wwdc2023-10043
Posted
by callagga.
Last updated
.
Post not yet marked as solved
1 Replies
530 Views
Trying to use new Swift @Observable to monitor GPS position within SwiftUI content view. But how do I tie the latest locations to the SwiftUI Map's mapCameraPosition? Well ideally the answer could cover: How to fix this error - So get map tracking along with the User Position, but also How to include facility to turn on/off the map moving to track the user position (which I'll need to do next). So could be tracking, then disable, move map around and have a look at things, then click button to start syncing the mapcameraposition to the GPS location again Refer to error I'm embedded in the code below. import SwiftUI import MapKit @Observable final class NewLocationManager : NSObject, CLLocationManagerDelegate { var location: CLLocation? = nil private let locationManager = CLLocationManager() func startCurrentLocationUpdates() async throws { if locationManager.authorizationStatus == .notDetermined { locationManager.requestWhenInUseAuthorization() } for try await locationUpdate in CLLocationUpdate.liveUpdates() { guard let location = locationUpdate.location else { return } self.location = location } } } struct ContentView: View { var newlocationManager = NewLocationManager() @State private var cameraPosition: MapCameraPosition = .region(MKCoordinateRegion( center: newlocationManager.location?.coordinate ?? <#default value#>, span: MKCoordinateSpan(latitudeDelta: 0.25, longitudeDelta: 0.25) )) // GET ERROR: Cannot use instance member 'newlocationManager' within property initializer; property initializers run before 'self' is available var body: some View { ZStack { Map(position: $cameraPosition) Text("New location manager: \(newlocationManager.location?.description ?? "NIL" )") // works } .task { try? await newlocationManager.startCurrentLocationUpdates() } } } #Preview { ContentView() }
Posted
by callagga.
Last updated
.
Post not yet marked as solved
2 Replies
2.3k Views
How would remove the delete button in SwiftUI List rows when in Edit Mode? Note the hamburger button on the right of the row that allows rows to be re-ordered needs to continue to function.Background - Want a list that has the "re-order" rows functions always enabled. Edit mode seems to enable this (i.e. leave List in edit mode) however do not want the red delete button on each row.
Posted
by callagga.
Last updated
.
Post marked as solved
5 Replies
1.8k Views
How should my code be modified to ensure that when an exception happens at the Core Data layer when adding a new item, that SwiftUI does NOT continue to show a new item? Background: When I run the below code I get an exception when adding a new item doing the "context.save()", HOWEVER whilst the new item request really failed (did not save to Core Data), the UI does show a new item. It is as if the "lists" variable in the @FetchRequest line is not behaving dynamically. Question - How do I fix code so that the application works properly?Code: import SwiftUI struct ContentView: View { @Environment(\.managedObjectContext) var context @FetchRequest(fetchRequest: List.allListFetchRequest()) var lists: FetchedResults private func addListItem() { let newList = List(context: context) newList.id = 1 newList.title = "Testing 123" do { try context.save() } catch let e as NSError { print("Could not save new List. \(e.debugDescription)") return } } var body: some View { NavigationView { VStack { ForEach(lists) { list in Text("List = \(list.title)") } } .navigationBarTitle( Text("My Todo Lists") ) .navigationBarItems( trailing: Button(action: {self.addListItem()} ) { Image(systemName: "plus.square") } ) } } }Example Output: Could not save new List. Error Domain=NSCocoaErrorDomain Code=133021 "(null)" UserInfo={NSExceptionOmitCallstacks=true, conflictList=( "NSConstraintConflict (0x600001fcccc0) for constraint (\n id\n): database: (null), conflictedObjects: (\n \"0x600000a7e360 \",\n \"0xfb1bb528bb57810c \"\n)" )}
Posted
by callagga.
Last updated
.
Post marked as solved
3 Replies
2.5k Views
How can one prevent auto-zooming with IOS MapKit when using userTrackingMode = .followWithHeading? That is I am setting the current user location to the centre of the screen, and have "view.userTrackingMode = .followWithHeading" so that the map orientates to north, but when you zoom in/out manually the MapView automatically overrides this and zooms back to the level it seems to prefer being at. I was to be able to zoom in, then the zoom level stays like this, whilst it keeps the map centred to user location, and keeps auto-rotating to keep map aligned to north. I am using SwiftUI so have effectively do have the location being passed into GCMapView as a parameter (as the means to keep the SwiftUI GCMapView up to date with latest user location). So not sure if this is causing an issue? Some key bits (have pulled some code out to show relevant lines) of the MapKit call backs I'm using: struct FlightView: View { @EnvironmentObject var locationManager : GCLocationManager @State var centreUserLocation : Bool = false var body: some View { GCMapView( flight: flight, userLocation: locationManager.userLocation, centreUserLocation: centreUserLocation, initalZoomDone: initalZoomDone ) } } struct GCMapView : UIViewRepresentable { let map = MKMapView() func makeUIView(context: Context) -> MKMapView { map.delegate = context.coordinator map.isRotateEnabled = true map.userTrackingMode = .followWithHeading map.showsUserLocation = true return map } func updateUIView(_ view: MKMapView, context: Context) { if let userLocation = userLocation { view.centerCoordinate = userLocation view.userTrackingMode = .followWithHeading // Needed to keep map rotating to align to North } } func makeCoordinator() -> Coordinator { return Coordinator(self) } class Coordinator: NSObject, MKMapViewDelegate { var parent: GCMapView init(_ parent: GCMapView) { self.parent = parent super.init() } } }
Posted
by callagga.
Last updated
.
Post not yet marked as solved
1 Replies
2.8k Views
When moving a row in a SwiftUI List I note that due to my "save Core Data" call the UI transition is jerky. I call save against core data as the list items orderNum has changed, so I'm saving that change that. Video and code below. If I remove the "GCCoreData.save()" there is no stutter.Animated GIF: https://i.stack.imgur.com/lsmtV.gifCode - Extract from my SwiftUI View private var tasksFetchRequest : FetchRequest private var gcTasks : FetchedResults { tasksFetchRequest.wrappedValue } init(withGcList gcList:GCList) { self.currentList = gcList self.tasksFetchRequest = FetchRequest( entity: GCTask.entity(), sortDescriptors: [NSSortDescriptor(key: "orderNum", ascending: true)], predicate: NSPredicate(format: "gcList == %@", currentList) ) } private func updateOrderNums(startIndex: Int, destIndex: Int, inc: Int) { var currIndex = startIndex while currIndex &lt;= destIndex { gcTasks[currIndex].orderNum = gcTasks[currIndex].orderNum + Int64(inc) currIndex += 1 } } fileprivate func moveRow(fromStartIndex taskStartIndex: IndexSet.Element, toDestIndex taskDestIndex: Int, movingDown moveDown: Bool) { gcTasks[taskStartIndex].orderNum = gcTasks[taskDestIndex].orderNum updateOrderNums( startIndex: moveDown ? taskStartIndex + 1 : taskDestIndex, destIndex: moveDown ? taskDestIndex : taskStartIndex - 1, inc: moveDown ? -1 : 1 ) // Save GCCoreData.save() // &lt;&lt;== JERKINESS OCCURS DUE TO THIS }Code - Library functions called class GCCoreData: NSObject { static func save() { let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext if context.hasChanges { do { try context.save() } catch { let nserror = error as NSError fatalError("Unresolved error performing Core Data save: \(nserror), \(nserror.userInfo)") } } } }Note that change the "GCCoreData.save()" line to the below also does not assist. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { GCCoreData.save() } [1]: https://i.stack.imgur.com/lsmtV.gif
Posted
by callagga.
Last updated
.
Post not yet marked as solved
0 Replies
652 Views
When using a List which I want to be in edit mode, but do NOT want to use "onDelete" (only want to use "onMove") there is an indentation occuring for the row (a bug I think). See below for example. Any work arounds until this is fixed/addressed?Snapshot Image https://i.stack.imgur.com/UAIIN.png Playground Codeimport SwiftUI import PlaygroundSupport let modelData: [String] = ["Eggs", "Milk", "Bread"] struct ListTestNoDelete: View { private func move(from uiStartIndexSet: IndexSet, to uiDestIndex: Int) { print("On Move") } var body: some View { NavigationView { VStack { List { ForEach(modelData, id: \.self) { str in HStack { Image(systemName: "square") Text(str) } } .onMove(perform: self.move) } .environment(\.editMode, .constant(.active)) .navigationBarTitle( Text("Test") ) } } } } let listTest = ListTestNoDelete() PlaygroundPage.current.liveView = UIHostingController(rootView: listTest)
Posted
by callagga.
Last updated
.
Post not yet marked as solved
0 Replies
1.2k Views
Is it possible to "re-order"/"move" items in a ScrollView as one can in a List? I have playground below which compiles/run fine, however the onDelete / onMove does does visualise any delete button or hamburger lines for dragging/moving?Is there a way to get my code working here so at least the "onMove" concept works?Note - I note that both List and ScrollView (I think) support protocol DynamicViewContent, so was assuming it should work(?)Image after running code: https://i.stack.imgur.com/c5R8g.pngCode (Playgrounds)import SwiftUI import PlaygroundSupport let modelData: [String] = ["Eggs", "Milk", "Bread"] struct ListTestNoDelete: View { private func move(from uiStartIndexSet: IndexSet, to uiDestIndex: Int) { print("On Move") } private func delete(at offsets : IndexSet) { print("On Delete") } var body: some View { NavigationView { VStack { ScrollView { ForEach(modelData, id: \.self) { str in HStack { Image(systemName: "square") Text(str) Spacer() }.padding(.horizontal, 3) } .onMove(perform: self.move) .onDelete(perform: self.delete) } .environment(\.editMode, .constant(.active)) .navigationBarTitle( Text("Test") ) } } } } let listTest = ListTestNoDelete() PlaygroundPage.current.liveView = UIHostingController(rootView: listTest)
Posted
by callagga.
Last updated
.
Post not yet marked as solved
0 Replies
730 Views
ould the below be the most appropriate way to handle updating the orderNum of items in a List when the manual order is stored in core data (as the orderNum field).Specifically I note when I try to cycle through my fetchedResults list after I have deleted an item, that item is in fact still there. So what I'm doing is doing a core data save, and then after this updating the orderNum and then resaving. OK approach?Key code sections: @Environment(\.managedObjectContext) var context @FetchRequest(fetchRequest: GCList.allListFetchRequest()) var gcLists: FetchedResults private func save() { // Save do { try self.context.save() } catch let e as NSError { print("ERROR : \(e.description)") } } private func deleteList(at offsets: IndexSet) { // Remove Items for index in offsets { let list = self.gcLists[index] self.context.delete(list) } self.save() // Redo orderNum *** NEED TO DO ANOTHER UPDATE/RESAVE LIKE THIS *** var i = 0 self.gcLists.forEach { gcItem in gcItem.orderNum = Int64(i) i += 1 } self.save() }
Posted
by callagga.
Last updated
.