에 데이터를 전달하 UIViewRepresentable 기능을 SwiftUI 보기

0

질문

사용자 보기를 위한 배송소 지도에서,그 주소에 의해 식별 마커의 중간에 위치하고 있다. 다음 주소를 통해 얻은 이 표시입니다. 는 방법을 표시한 주소에서 사용자 인터페이스?

struct MapView: UIViewRepresentable {

@Binding var centerCoordinate: CLLocationCoordinate2D

var currentLocation: CLLocationCoordinate2D?
var withAnnotation: MKPointAnnotation?

class Coordinator: NSObject, MKMapViewDelegate {
    
    var parent: MapView
    var addressLabel: String = "222"
    
    init(_ parent: MapView) {
        self.parent = parent
    }
    
    func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
        if !mapView.showsUserLocation {
            parent.centerCoordinate = mapView.centerCoordinate
        }
    }
    
    ...
    
    func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool){
        
        let center = getCenterLocation(for: mapView)
        let geoCoder = CLGeocoder()
        
        geoCoder.reverseGeocodeLocation(center) { [weak self] (placemarks, error) in
            guard let self = self else { return }
            
            if let _ = error {
                //TODO: Show alert informing the user
                print("error")
                return
            }
            
            guard let placemark = placemarks?.first else {
                //TODO: Show alert informing the user
                return
            }
            
            let streetNumber = placemark.subThoroughfare ?? ""
            let streetName = placemark.thoroughfare ?? ""
            
            DispatchQueue.main.async {
                self.addressLabel =  String("\(streetName) | \(streetNumber)")
                print(self.addressLabel)
                
            }
        }
    }
}

func makeCoordinator() -> Coordinator {
    Coordinator(self)
}

func makeUIView(context: Context) -> MKMapView {
    let mapView = MKMapView()
    mapView.delegate = context.coordinator
    mapView.showsUserLocation = false
    return mapView
}

func updateUIView(_ uiView: MKMapView, context: Context) {
    if let currentLocation = self.currentLocation {
        if let annotation = self.withAnnotation {
            uiView.removeAnnotation(annotation)
        }
        uiView.showsUserLocation = true
        let region = MKCoordinateRegion(center: currentLocation, latitudinalMeters: 1000, longitudinalMeters: 1000)
        uiView.setRegion(region, animated: true)
    } else if let annotation = self.withAnnotation {
        uiView.removeAnnotations(uiView.annotations)
        uiView.addAnnotation(annotation)
        }
    }
}

내가 노력하고 주소를 통과하는 프로그램입니다. 무엇 대부분의 올바른 방법으로 하면 이렇게 할 수 있습니까? 인터페이스에서,내가 원하는 주소를 얻기 위해서 끊임없이 변화하는 변수 addressLabel

import SwiftUI
import MapKit

fileprivate let locationFetcher = LocationFetcher()

struct LocationView: View {

@State var centerCoordinate = CLLocationCoordinate2D()
@State var currentLocation: CLLocationCoordinate2D?
@State var annotation: MKPointAnnotation?

var body: some View {
    ZStack {
    
        MapView(centerCoordinate: $centerCoordinate, currentLocation: currentLocation, withAnnotation: annotation)
            .edgesIgnoringSafeArea([.leading, .trailing, .bottom])
            .onAppear(perform: {
                locationFetcher.start()
            })
    }
    .overlay(
    
        ZStack {


            Text("\(*MapView(centerCoordinate: $centerCoordinate, currentLocation: currentLocation, withAnnotation: annotation).makeCoordinator().addressLabel OMG????*)")
            
                .offset(y: 44)
        }
    
    )
}

struct LocationView_Previews: PreviewProvider {
    static var previews: some View {
        LocationView()
    }
}

이렇게 하려면 어떻게 해야 합니까?

사전에 감사합니다

geolocation properties swift swiftui
2021-11-23 15:26:40
1

최고의 응답

1

여기 하나 접근 방식이다. 단일 원본의 진리는 모두 UIKit 및 SwiftUI 액세스할 수 있습니다.

@available(iOS 15.0, *)
struct LocationView: View {
    //It is better to have one source of truth
    @StateObject var vm: MapViewModel = MapViewModel()
    
    var body: some View {
        ZStack {
            MapView(vm: vm)
                .edgesIgnoringSafeArea([.leading, .trailing, .bottom])
                .onAppear(perform: {
                    //locationFetcher.start() //No Code provided
                })
        }
        .overlay(
            HStack{
                Spacer()
                Text(vm.addressLabel)
                Spacer()
                //Using offset is subjective since screen sizes change just center it
            }
            
            
        )
        //Sample alert that adapts to what is
        .alert(isPresented: $vm.errorAlert.isPresented, error: vm.errorAlert.error, actions: {
            
            if vm.errorAlert.defaultAction != nil{
                Button("ok", role: .none, action: vm.errorAlert.defaultAction!)
            }
            
            if vm.errorAlert.cancelAction != nil{
                Button("cancel", role: .cancel, action: vm.errorAlert.cancelAction!)
            }
            
            if vm.errorAlert.defaultAction == nil && vm.errorAlert.cancelAction == nil {
                Button("ok", role: .none, action: {})
            }
        })
    }
}
//UIKit and SwiftUI will have access to this ViewModel so all the data can have one souce of truth
class MapViewModel: ObservableObject{
    //All the variables live here
    @Published  var addressLabel: String = "222"
    @Published var centerCoordinate: CLLocationCoordinate2D = CLLocationCoordinate2D()
    
    @Published var currentLocation: CLLocationCoordinate2D? = nil
    @Published var withAnnotation: MKPointAnnotation? = nil
    @Published var annotation: MKPointAnnotation?
    //This tuple variable allows you to have a dynamic alert in the view
    @Published var errorAlert: (isPresented: Bool, error: MapErrors, defaultAction: (() -> Void)?, cancelAction: (() -> Void)?) = (false, MapErrors.unknown, nil, nil)
    //The new alert requires a LocalizedError
    enum MapErrors: LocalizedStringKey, LocalizedError{
        case unknown
        case failedToRetrievePlacemark
        case failedToReverseGeocode
        case randomForTestPurposes
        //Add localizable.strings to you project and add these keys so you get localized messages
        var errorDescription: String?{
            switch self{
                
            case .unknown:
                return "unknown".localizedCapitalized
            case .failedToRetrievePlacemark:
                return "failedToRetrievePlacemark".localizedCapitalized
                
            case .failedToReverseGeocode:
                return "failedToReverseGeocode".localizedCapitalized
                
            case .randomForTestPurposes:
                return "randomForTestPurposes".localizedCapitalized
                
            }
        }
    }
    //Presenting with this will ensure that errors keep from gettting lost by creating a loop until they can be presented
    func presentError(isPresented: Bool, error: MapErrors, defaultAction: (() -> Void)?, cancelAction: (() -> Void)?, count: Int = 1){
        //If there is an alert already showing
        if errorAlert.isPresented{
            //See if the current error has been on screen for 10 seconds
            if count >= 10{
                //If it has dismiss it so the new error can be posted
                errorAlert.isPresented = false
            }
            //Call the method again in 1 second
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                let newCount = count + 1
                self.presentError(isPresented: isPresented, error: error, defaultAction: defaultAction, cancelAction: cancelAction, count: newCount)
            }
        }else{
            errorAlert = (isPresented, error, defaultAction, cancelAction)
        }
    }
    
}
struct MapView: UIViewRepresentable {
    @ObservedObject var vm: MapViewModel
    
    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: MapView
        
        init(_ parent: MapView) {
            self.parent = parent
        }
        
        func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
            if !mapView.showsUserLocation {
                parent.vm.centerCoordinate = mapView.centerCoordinate
            }
        }
        
        
        func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool){
            getAddress(center: mapView.centerCoordinate)
            //Just to demostrate the error
            //You can remove this whenever
#if DEBUG
            if Bool.random(){
                self.parent.vm.presentError(isPresented: true, error: MapViewModel.MapErrors.randomForTestPurposes, defaultAction: nil, cancelAction: nil)
                
            }
#endif
            
        }
        //Gets the addess from CLGeocoder if available
        func getAddress(center: CLLocationCoordinate2D){
            let geoCoder = CLGeocoder()
            
            geoCoder.reverseGeocodeLocation(CLLocation(latitude: center.latitude, longitude: center.longitude)) { [weak self] (placemarks, error) in
                guard let self = self else { return }
                
                if let _ = error {
                    //TODO: Show alert informing the user
                    print("error")
                    self.parent.vm.presentError(isPresented: true, error: MapViewModel.MapErrors.failedToReverseGeocode, defaultAction: nil, cancelAction: nil)
                    return
                }
                
                guard let placemark = placemarks?.first else {
                    //TODO: Show alert informing the user
                    self.parent.vm.presentError(isPresented: true, error: MapViewModel.MapErrors.failedToRetrievePlacemark, defaultAction: nil, cancelAction: nil)
                    return
                }
                
                let streetNumber = placemark.subThoroughfare ?? ""
                let streetName = placemark.thoroughfare ?? ""
                
                DispatchQueue.main.async {
                    self.parent.vm.addressLabel =  String("\(streetName) | \(streetNumber)")
                    print(self.parent.vm.addressLabel)
                    
                }
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.delegate = context.coordinator
        mapView.showsUserLocation = false
        return mapView
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
        if let currentLocation = vm.currentLocation {
            if let annotation = vm.withAnnotation {
                uiView.removeAnnotation(annotation)
            }
            uiView.showsUserLocation = true
            let region = MKCoordinateRegion(center: currentLocation, latitudinalMeters: 1000, longitudinalMeters: 1000)
            uiView.setRegion(region, animated: true)
        } else if let annotation = vm.withAnnotation {
            uiView.removeAnnotations(uiView.annotations)
            uiView.addAnnotation(annotation)
        }
    }
}
2021-11-23 17:18:56

다른 언어로

이 페이지는 다른 언어로되어 있습니다

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................