[SwiftUI] SwiftUI에서 UIKit 사용하기

숑이·2023년 12월 23일
0

SwiftUI

목록 보기
2/4
post-thumbnail
post-custom-banner

SwiftUI에서 지원되지 않는 기능을 구현할 때, UIKit의 기능이 필요할 수 있습니다.
이 때, UIViewRepresentable 프로토콜을 채택하여 구현하면, SwiftUI에서 UIKit의 View를 사용할 수 있습니다.

참고: https://developer.apple.com/documentation/swiftui/uiviewrepresentable

UIViewRepresentable 필수 메서드

  • makeUIView(context:) - 사용할 UIView를 생성하고 초기화하는 메서드
  • updateUIView(_ uiView:, context:) - UIView의 뷰 업데이트가 필요할 때 호출되는 메서드

사용 방법

import SwiftUI
import MapKit

struct MapViewRepresentable: UIViewRepresentable {
    let mapView = MKMapView()
    
    /// 사용할 UIView를 생성하고, 초기화하는 메서드
    func makeUIView(context: Context) -> MKMapView {
        return mapView
    }
    
    /// UIView의 뷰 업데이트가 필요할 때 호출되는 메서드
    func updateUIView(_ uiView: MKMapView, context: Context) {
        
    }
}

MapKit을 사용해서 지도를 보여주고 싶을 때, UIKit에서는 MKMapView 인스턴스를 생성해서 구현할 수 있었습니다. SwiftUI에서도 MKMapView를 사용하기 위해서는 UIViewRepresentable 프로토콜을 채택하고 구현할 필요가 있습니다.

struct MapView: View {
    var body: some View {
        MapViewRepresentable()
            .ignoresSafeArea()
    }
}

그리고 SwiftUI에서는 UIViewRepresentable을 구현한 구조체를 위와 같이 사용하면 됩니다.

Coordinator

  • UIViewRepresentable은 Coordinator 타입이 존재합니다.
  • Coordinator는 UIView의 Delegate 역할을 합니다.
  • UIView를 초기화 하는 makeUIView(context:) 메서드에서 coordinator 클래스 인스턴스를 delegate로 넘겨줍니다.
  • coordinator는 context를 통해서 접근이 가능합니다.
  • UIKit -> SwiftUI로 데이터 전달 기능을 수행합니다.

Coordinator 사용

Coordinator를 통해 delegate 메서드를 구현하고, 사용자의 현재 위치로 지도를 이동시키는 예제입니다.

extension MapViewRepresentable {
    final class MapViewCoordinator: NSObject, MKMapViewDelegate {
        private let parent: MapViewRepresentable
        private let locationManager = CLLocationManager()
        
        init(parent: MapViewRepresentable) {
            self.parent = parent
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.requestWhenInUseAuthorization()
            super.init()
        }
        
        //MARK: - MKMapViewDelegate
        func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
            let coordinate = userLocation.coordinate
            let region = MKCoordinateRegion(
                center: coordinate,
                span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
            )
            parent.mapView.setRegion(region, animated: true)
        }
    }
}

Coordinator를 생성할 때, MapViewRepresentable을 파라미터로 받아 프로퍼티로 저장합니다. 이를 통해 mapView에 접근할 수 있습니다.

locationManager는 위치 권한을 요청하기 위해 사용합니다. 위치 권한이 없으면, delegate 메서드를 구현해도 사용자의 위치 정보를 가져올 수 없습니다.

또한, info.plist 에서 위 요소를 추가해줘야합니다.

MKMapViewDelegate를 채택하고, mapView(didUpdate:) 메서드를 구현했습니다.
이 메서드를 통해 사용자의 현재 위치를 알아낼 수 있습니다.

struct MapViewRepresentable: UIViewRepresentable {
    /// 사용할 UIView를 생성하고, 초기화하는 메서드
    func makeUIView(context: Context) -> MKMapView {
        mapView.showsUserLocation = true
        mapView.delegate = context.coordinator
        return mapView
    }
    
    /// UIView의 뷰 업데이트가 필요할 때 호출되는 메서드
    func updateUIView(_ uiView: MKMapView, context: Context) { }
    
    func makeCoordinator() -> MapViewCoordinator {
        return MapViewCoordinator(parent: self)
    }
}

UIViewRepresentable에서는 makeCoordinator() 메서드를 통해 Coordinator 인스턴스를 생성해 리턴하고, makeUIView(context:) 메서드에서 delegate를 설정합니다.

profile
iOS앱 개발자가 될테야
post-custom-banner

0개의 댓글