How can i get location on tap in mapbox v10 in swift - swiftui

I want to detect a user tap on the map so i can add a marker and get the coordinates of that point. I was able to do that on android and with react but it seems impossible in swiftui because i can't find new ways to do that.
Currently i have my mapview like this.
import SwiftUI
import UIKit
import MapboxMaps
struct MapBoxMapView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> MapViewController {
return MapViewController()
}
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
}
}
class MapViewController: UIViewController {
internal var mapView: MapView!
override func viewDidLoad() {
super.viewDidLoad()
let myResourceOptions = ResourceOptions(accessToken: "MY_TOKEN")
let myCameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 0, longitude: 0), zoom: 10)
let initOptions = MapInitOptions(
resourceOptions: myResourceOptions,
cameraOptions: myCameraOptions,
styleURI: StyleURI(rawValue: StyleURI.satellite.rawValue)
)
mapView = MapView(frame: view.bounds, mapInitOptions: initOptions)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(mapView)
}
}
What can i do to make it possible? I searched on v10 documentation but found nothing.

Related

Google maps SDK not working on SwiftUI app

I've followed the steps of adding a map to my iOS app and then created a struct to "bridge" between swiftUI and UIkit but for some reason when I call the struct nothing would show whatsoever.
This is my viewcontroller:
import CoreLocation
import UIKit
import GoogleMaps
class ViewController: UIViewController, CLLocationManagerDelegate{
let manager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
GMSServices.provideAPIKey("xxx")
print("License: \n\n\(GMSServices.openSourceLicenseInfo())")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else {
return
}
let coordinate = location .coordinate
let camera = GMSCameraPosition.camera(withLatitude: coordinate.latitude, longitude: coordinate.longitude, zoom: 6.0)
let mapView = GMSMapView.map(withFrame: view.frame, camera: camera)
view.addSubview(mapView)
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: -coordinate.latitude, longitude: coordinate.longitude)
marker.title = "Sydney"
marker.snippet = "Australia"
marker.map = mapView
}
}
and this is the struct created in my content view:
struct YourViewControllerView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> ViewController {
return ViewController()
}
func updateUIViewController(_ uiViewController: ViewController, context: Context) {
}
}
I've searched for similar problems but everyone else had the problem of the map not loading (marker and google logo still show), whereas in my case nothing shows on the screen at all.
Just to reiterate, my Maps SDK for iOS is enabled.
I've been trying to find a solution for a week now so help is greatly appreciated.

Remove title when pushing EKCalendarChooser to Navigation Stack with SwiftUI

I'm working on an app where I want to push the EKCalendarChooser View Controller to the navigation stack with a navigation link. Everything works as expected apart from the fact that I can't get rid of some magic title/label.
I want to hide the title marked with the red rectangle in the image.
I'm using the following code to push the view:
NavigationLink(destination: CalendarChooser(eventStore: self.eventStore)
.edgesIgnoringSafeArea([.top,.bottom])
.navigationTitle("My Navigation Title")) {
Text("Calendar Selection")
}
And this is my UIViewControllerRepresentable
import SwiftUI
import EventKitUI
struct CalendarChooser: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
#Environment(\.presentationMode) var presentationMode
let eventStore: EKEventStore
func makeUIViewController(context: UIViewControllerRepresentableContext<CalendarChooser>) -> UINavigationController {
let chooser = EKCalendarChooser(selectionStyle: .multiple, displayStyle: .allCalendars, entityType: .event, eventStore: eventStore)
chooser.selectedCalendars = Set(eventStore.selectableCalendarsFromSettings)
chooser.delegate = context.coordinator
chooser.showsDoneButton = false
chooser.showsCancelButton = false
return UINavigationController(rootViewController: chooser)
}
func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<CalendarChooser>) {
}
class Coordinator: NSObject, UINavigationControllerDelegate, EKCalendarChooserDelegate {
var parent: CalendarChooser
init(_ parent: CalendarChooser) {
self.parent = parent
}
func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
let selectedCalendarIDs = calendarChooser.selectedCalendars.compactMap { $0.calendarIdentifier }
UserDefaults.savedCalendarIDs = selectedCalendarIDs
NotificationCenter.default.post(name: .calendarSelectionDidChange, object: nil)
parent.presentationMode.wrappedValue.dismiss()
}
func calendarChooserDidCancel(_ calendarChooser: EKCalendarChooser) {
parent.presentationMode.wrappedValue.dismiss()
}
}
}
Note that I'm not even sure that I'm on the right track here and I'm open for any solution.
I think I've found a solution to my own problem. With a small modification
to my UIViewControllerRepresentable the view looks the way I want it to. More specifically to the updateUIViewController function:
func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<CalendarChooser>) {
uiViewController.setNavigationBarHidden(true, animated: false) // This line!
}
By doing this I keep the navigation controls and title from the navigation link, which looks like this:

Why doesn't my AVVideoCapturePreview render in the layer?

I'm trying to implement a camera preview view in SwiftUI, for which I have the following code:
import SwiftUI
import AVFoundation
struct CameraPreview: UIViewRepresentable {
let session: AVCaptureSession
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .gray
let videoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
videoPreviewLayer.frame = view.bounds
videoPreviewLayer.videoGravity = .resizeAspectFill
videoPreviewLayer.connection?.videoOrientation = .portrait
view.layer.addSublayer(videoPreviewLayer)
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
for layer in uiView.layer.sublayers ?? [] {
layer.frame = uiView.bounds
}
}
}
However I do see the gray background view that I set on the view, but it never starts showing the camera output. I've set a AVCaptureVideoDataOutputSampleBufferDelegate class and I can see the frames being captured and processed, yet for some reason it does not start rendering the output.
I have this other snippet that DOES render the output, but it does so by setting the preview layer as the root layer which is what I want to avoid, here's the code that works:
struct CameraPreview: UIViewRepresentable {
let session: AVCaptureSession
func makeUIView(context: Context) -> UIView {
let view = VideoView()
view.backgroundColor = .gray
view.previewLayer.session = session
view.previewLayer.videoGravity = .resizeAspectFill
view.previewLayer.connection?.videoOrientation = .portrait
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
for layer in uiView.layer.sublayers ?? [] {
layer.frame = uiView.bounds
}
}
class VideoView: UIView {
override class var layerClass: AnyClass {
AVCaptureVideoPreviewLayer.self
}
var previewLayer: AVCaptureVideoPreviewLayer {
layer as! AVCaptureVideoPreviewLayer
}
}
}
Some examples I found showed I should be able to show the preview like I do in the first example. I've tried initializing the session with inputs before and after the preview view is created and I've gotten the same result. Am I missing anything? am I not retaining the layer or is there a special configuration for the session to look out for? to make it work I simply swap the implementations and the one with the inner class does render?
Any help is really appreciated.
Some resources:
https://nsscreencast.com/episodes/296-camera-capture-preview-layer-sample-buffer
https://www.appcoda.com/avfoundation-swift-guide/
https://developer.apple.com/documentation/vision/recognizing_objects_in_live_capture

Finger and Pencil width on PKCanvasView Different

I am trying to build out an application with the ability to use both apple pencil and finger input to draw. However, when I set the width of the tool to a sufficiently small number (1) the pencil input displays correctly, but when I use my finger it draws thicker lines. I would like uniformity in width when switching between pencil and finger. Any help would be appreciated. Bellow is the minimal reproducible code.
import Foundation
import SwiftUI
import PencilKit
struct ContentView: View {
var body: some View {
CanvasRepresentable()
}
}
struct CanvasRepresentable: UIViewRepresentable {
class Coordinator: NSObject, PKCanvasViewDelegate {
var parent: CanvasRepresentable
init(_ parent: CanvasRepresentable) {
self.parent = parent
}
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIView(context: Context) -> PKCanvasView {
let c = PKCanvasView()
c.isOpaque = false
c.allowsFingerDrawing = true
c.delegate = context.coordinator
c.tool = PKInkingTool(.pen, color: .black, width: 1)
return c
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {
let tool = uiView.tool as! PKInkingTool
print(tool.width)
}
}

Where to put delegates of CLLocationManager using Swift UI?

I've been on and off Swift for the years so, sorry if I'm doing something stupid, the Swift UI thing isn't helping my confusion either. I have the code I'm working with at the bottom.
So I'm making a Geofencing app using SwiftUI, I've got the basics up and running but still have a bit to go with it,
I have the Geofencing coordinates and stuff inside the UserData environment variable, which I get off a json elsewhere.
Have a SwiftUI object that has a map out with the current position and initializes a CLLocationManager to deal with geofencing.
I'm trying to implement the delegate on the same file by
locationManager.delegate = self
in the setupManager(),
but it will cause
Cannot assign value of type 'GeofencingView' to type 'CLLocationManagerDelegate?'
The lack of SwiftUI specific information online seems to be causing the most confusion for me at the moment, I think what I should do is,
create a customized NsObject that handles the delegates, but in that case I'm not sure how to pass the #EnviromentObject
Find out how to put the delegates on the UIViewRepresentable object.
Any pointers on what I'm doing wrong would be greatly appreciated.
import SwiftUI
import MapKit
import CoreLocation
struct GeofencingView: UIViewRepresentable {
#EnvironmentObject var userData: UserData
var notification = LocalNotificationManager()
var locationManager = CLLocationManager()
func setupManager() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
self.startGoefenceMonitoring()
}
func startGoefenceMonitoring() {
print("MapListView::startGeofenceMonitoring")
for landmark in self.userData.landmarks {
let moniteringCordinate = CLLocationCoordinate2DMake(landmark.locationCoordinate.longitude, landmark.locationCoordinate.latitude)
let moniteringRegion = CLCircularRegion.init(center: moniteringCordinate, radius: 20.0, identifier: "\(landmark.id)" )
locationManager.startMonitoring(for: moniteringRegion)
}
}
func makeUIView(context: Context) -> MKMapView {
setupManager()
let mapView = MKMapView(frame: UIScreen.main.bounds)
let count = self.userData.landmarks.count
var annotationArray: [MKAnnotation] = []
var num:Int = 0
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
struct GeofencingView_Preview: PreviewProvider {
static var previews: some View {
GeofencingView()
.environmentObject(UserData())
}
}