I had the same problem, and after a few hours, I managed to get something working as required
- At launch, it shows the user location if he authorised it, but waits for him to tap the button to activate the follow.
- If he authorised and taps the button, it follows him.
- If for some reason the app cannot access his location (denied access, restrictions…), it will tell him to change it in Settings and redirect him there.
- If he changes the authorization while running the app, it will change automatically.
- If the map is following him, the button disappears.
- If he drags the map (stop following), the button appears again.
- (The button supports Dark Mode)
I really hope it'll help you, I think I'll put it on GitHub some day. I'll add the link here if I do.
First, I didn't want to recreate the MKMapView every time, so I put it in a class I called MapViewContainer
I don't think it's a good practice, though ♂️
If you don't want to use it, just replace @EnvironmentObject private var mapViewContainer: MapViewContainer by let mapView = MKMapView(frame: .zero) in MKMapViewRepresentable (and change the mapViewContainer.mapView for mapView)
import MapKit
class MapViewContainer: ObservableObject {
    
    @Published public private(set) var mapView = MKMapView(frame: .zero)
    
}
Then, I could create my MapViewRepresentable
import SwiftUI
import MapKit
// MARK: - MKMapViewRepresentable
struct MKMapViewRepresentable: UIViewRepresentable {
    
    var userTrackingMode: Binding<MKUserTrackingMode>
    
    @EnvironmentObject private var mapViewContainer: MapViewContainer
    
    func makeUIView(context: UIViewRepresentableContext<MKMapViewRepresentable>) -> MKMapView {
        mapViewContainer.mapView.delegate = context.coordinator
        
        context.coordinator.followUserIfPossible()
        
        return mapViewContainer.mapView
    }
    
    func updateUIView(_ mapView: MKMapView, context: UIViewRepresentableContext<MKMapViewRepresentable>) {
        if mapView.userTrackingMode != userTrackingMode.wrappedValue {
            mapView.setUserTrackingMode(userTrackingMode.wrappedValue, animated: true)
        }
    }
    
    func makeCoordinator() -> MapViewCoordinator {
        let coordinator = MapViewCoordinator(self)
        return coordinator
    }
    
    // MARK: - Coordinator
    
    class MapViewCoordinator: NSObject, MKMapViewDelegate, CLLocationManagerDelegate {
        
        var control: MKMapViewRepresentable
        
        let locationManager = CLLocationManager()
        
        init(_ control: MKMapViewRepresentable) {
            self.control = control
            
            super.init()
            
            setupLocationManager()
        }
        
        func setupLocationManager() {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.pausesLocationUpdatesAutomatically = true
        }
        
        func followUserIfPossible() {
            switch CLLocationManager.authorizationStatus() {
            case .authorizedAlways, .authorizedWhenInUse:
                control.userTrackingMode.wrappedValue = .follow
            default:
                break
            }
        }
        
        private func present(_ alert: UIAlertController, animated: Bool = true, completion: (() -> Void)? = nil) {
            // UIApplication.shared.keyWindow has been deprecated in iOS 13,
            // so you need a little workaround to avoid the compiler warning
            // https://stackoverflow.com/a/58031897/10967642
            
            let keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
            keyWindow?.rootViewController?.present(alert, animated: animated, completion: completion)
        }
        
        // MARK: MKMapViewDelegate
        
        func mapView(_ mapView: MKMapView, didChange mode: MKUserTrackingMode, animated: Bool) {
            #if DEBUG
            print("\(type(of: self)).\(#function): userTrackingMode=", terminator: "")
            switch mode {
            case .follow:            print(".follow")
            case .followWithHeading: print(".followWithHeading")
            case .none:              print(".none")
            @unknown default:        print("@unknown")
            }
            #endif
            
            if CLLocationManager.locationServicesEnabled() {
                switch mode {
                case .follow, .followWithHeading:
                    switch CLLocationManager.authorizationStatus() {
                    case .notDetermined:
                        locationManager.requestWhenInUseAuthorization()
                    case .restricted:
                        // Possibly due to active restrictions such as parental controls being in place
                        let alert = UIAlertController(title: "Location Permission Restricted", message: "The app cannot access your location. This is possibly due to active restrictions such as parental controls being in place. Please disable or remove them and enable location permissions in settings.", preferredStyle: .alert)
                        alert.addAction(UIAlertAction(title: "Settings", style: .default) { _ in
                            // Redirect to Settings app
                            UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
                        })
                        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
                        present(alert)
                        
                        DispatchQueue.main.async {
                            self.control.userTrackingMode.wrappedValue = .none
                        }
                    case .denied:
                        let alert = UIAlertController(title: "Location Permission Denied", message: "Please enable location permissions in settings.", preferredStyle: .alert)
                        alert.addAction(UIAlertAction(title: "Settings", style: .default) { _ in
                            // Redirect to Settings app
                            UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
                        })
                        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
                        present(alert)
                        
                        DispatchQueue.main.async {
                            self.control.userTrackingMode.wrappedValue = .none
                        }
                    default:
                        DispatchQueue.main.async {
                            self.control.userTrackingMode.wrappedValue = mode
                        }
                    }
                default:
                    DispatchQueue.main.async {
                        self.control.userTrackingMode.wrappedValue = mode
                    }
                }
            } else {
                let alert = UIAlertController(title: "Location Services Disabled", message: "Please enable location services in settings.", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "Settings", style: .default) { _ in
                    // Redirect to Settings app
                    UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
                })
                alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
                present(alert)
                
                DispatchQueue.main.async {
                    self.control.userTrackingMode.wrappedValue = mode
                }
            }
        }
        
        // MARK: CLLocationManagerDelegate
        
        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
            #if DEBUG
            print("\(type(of: self)).\(#function): status=", terminator: "")
            switch status {
            case .notDetermined:       print(".notDetermined")
            case .restricted:          print(".restricted")
            case .denied:              print(".denied")
            case .authorizedAlways:    print(".authorizedAlways")
            case .authorizedWhenInUse: print(".authorizedWhenInUse")
            @unknown default:          print("@unknown")
            }
            #endif
            
            switch status {
            case .authorizedAlways, .authorizedWhenInUse:
                locationManager.startUpdatingLocation()
                control.mapViewContainer.mapView.setUserTrackingMode(control.userTrackingMode.wrappedValue, animated: true)
            default:
                control.mapViewContainer.mapView.setUserTrackingMode(.none, animated: true)
            }
        }
        
    }
    
}
And finally, put it in a SwiftUI View
import SwiftUI
import CoreLocation.CLLocation
import MapKit.MKAnnotationView
import MapKit.MKUserLocation
struct MapView: View {
    
    @State private var userTrackingMode: MKUserTrackingMode = .none
    
    var body: some View {
        ZStack {
            MKMapViewRepresentable(userTrackingMode: $userTrackingMode)
                .environmentObject(MapViewContainer())
                .edgesIgnoringSafeArea(.all)
            VStack {
                if !(userTrackingMode == .follow || userTrackingMode == .followWithHeading) {
                    HStack {
                        Spacer()
                        Button(action: { self.followUser() }) {
                            Image(systemName: "location.fill")
                                .modifier(MapButton(backgroundColor: .primary))
                        }
                        .padding(.trailing)
                    }
                    .padding(.top)
                }
                Spacer()
            }
        }
    }
    
    private func followUser() {
        userTrackingMode = .follow
    }
    
}
fileprivate struct MapButton: ViewModifier {
    
    let backgroundColor: Color
    var fontColor: Color = Color(UIColor.systemBackground)
    
    func body(content: Content) -> some View {
        content
            .padding()
            .background(self.backgroundColor.opacity(0.9))
            .foregroundColor(self.fontColor)
            .font(.title)
            .clipShape(Circle())
    }
    
}