Wawancara MapKit SwiftUI di 2026: Anotasi, Overlay, dan Geolokasi
Kuasai MapKit dengan SwiftUI untuk wawancara iOS: anotasi kustom, overlay, geolokasi, pencarian tempat, dan pola integrasi Maps.

Peta merupakan fitur inti dalam banyak aplikasi iOS: pengiriman, kebugaran, properti, dan jejaring sosial berbasis lokasi. Sejak iOS 17, MapKit menyediakan API SwiftUI native yang menyederhanakan integrasi peta secara signifikan. Para perekrut secara rutin menilai keterampilan ini dalam wawancara teknis.
Setiap pertanyaan mencerminkan format wawancara teknis sungguhan, dengan jawaban terperinci dan kode yang berfungsi. Konsep berkembang dari fundamental ke lanjutan.
Dasar Peta di SwiftUI
Pertanyaan 1: Bagaimana cara menampilkan peta dasar dengan SwiftUI?
Sejak iOS 17, SwiftUI menyediakan komponen Map native yang menggantikan pendekatan UIViewRepresentable sebelumnya. View Map menerima posisi kamera dan konten untuk ditampilkan.
import SwiftUI
import MapKit
struct BasicMapView: View {
// Camera position controlling center and zoom
@State private var cameraPosition: MapCameraPosition = .automatic
var body: some View {
// Basic map without additional content
Map(position: $cameraPosition)
}
}
// Map centered on a specific region
struct CenteredMapView: View {
// Defines a region centered on Paris
@State private var cameraPosition: MapCameraPosition = .region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 48.8566, longitude: 2.3522),
// Span defines the zoom level (in degrees)
span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
)
)
var body: some View {
Map(position: $cameraPosition) {
// Map content (markers, annotations, etc.)
}
.mapStyle(.standard) // Map style: standard, imagery, hybrid
}
}Properti position menggunakan MapCameraPosition, sebuah enum yang memungkinkan beberapa mode: .automatic (penyesuaian otomatis), .region (region tetap), .camera (kontrol penuh), atau .userLocation (terpusat pada pengguna).
Pertanyaan 2: Apa saja gaya peta yang tersedia?
MapKit menawarkan beberapa gaya peta yang sesuai untuk berbagai kasus penggunaan. Modifier .mapStyle() mengonfigurasi tampilan visual peta.
import SwiftUI
import MapKit
struct MapStylesDemo: View {
@State private var position: MapCameraPosition = .automatic
@State private var selectedStyle: MapStyleOption = .standard
var body: some View {
VStack {
Map(position: $position)
.mapStyle(selectedStyle.style)
.frame(height: 400)
// Style selector
Picker("Style", selection: $selectedStyle) {
ForEach(MapStyleOption.allCases) { option in
Text(option.name).tag(option)
}
}
.pickerStyle(.segmented)
.padding()
}
}
}
enum MapStyleOption: String, CaseIterable, Identifiable {
case standard
case imagery
case hybrid
case standardElevated
case imageryElevated
var id: String { rawValue }
var name: String {
switch self {
case .standard: return "Standard"
case .imagery: return "Satellite"
case .hybrid: return "Hybrid"
case .standardElevated: return "Terrain"
case .imageryElevated: return "Satellite 3D"
}
}
var style: MapStyle {
switch self {
case .standard:
// Classic road map
return .standard
case .imagery:
// Satellite view without labels
return .imagery
case .hybrid:
// Satellite with roads and names
return .hybrid
case .standardElevated:
// Standard with 3D terrain
return .standard(elevation: .realistic)
case .imageryElevated:
// Satellite with 3D terrain
return .imagery(elevation: .realistic)
}
}
}Gaya .standard menerima parameter pointsOfInterest untuk menyaring kategori yang ditampilkan: .including([.restaurant, .cafe]) atau .excluding([.nightlife]).
Pertanyaan 3: Bagaimana cara menambahkan penanda dan anotasi pada peta?
Penanda mewakili titik menarik di peta. SwiftUI MapKit menawarkan beberapa tipe: Marker (pin standar), Annotation (view kustom), dan MapCircle/MapPolygon (bentuk geometris).
import SwiftUI
import MapKit
// Place model conforming to Identifiable
struct Place: Identifiable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
let category: PlaceCategory
}
enum PlaceCategory {
case restaurant, hotel, attraction
var icon: String {
switch self {
case .restaurant: return "fork.knife"
case .hotel: return "bed.double"
case .attraction: return "star"
}
}
var tint: Color {
switch self {
case .restaurant: return .orange
case .hotel: return .blue
case .attraction: return .purple
}
}
}
struct MarkersMapView: View {
@State private var position: MapCameraPosition = .region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 48.8566, longitude: 2.3522),
span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
)
)
let places: [Place] = [
Place(name: "Eiffel Tower", coordinate: CLLocationCoordinate2D(latitude: 48.8584, longitude: 2.2945), category: .attraction),
Place(name: "Le Meurice", coordinate: CLLocationCoordinate2D(latitude: 48.8651, longitude: 2.3281), category: .hotel),
Place(name: "Le Jules Verne", coordinate: CLLocationCoordinate2D(latitude: 48.8583, longitude: 2.2944), category: .restaurant)
]
var body: some View {
Map(position: $position) {
// Markers with system icon
ForEach(places) { place in
Marker(place.name, systemImage: place.category.icon, coordinate: place.coordinate)
.tint(place.category.tint)
}
}
}
}
// Custom annotations with SwiftUI view
struct CustomAnnotationsView: View {
@State private var position: MapCameraPosition = .automatic
@State private var selectedPlace: Place?
let places: [Place] = [] // Data
var body: some View {
Map(position: $position, selection: $selectedPlace) {
ForEach(places) { place in
// Annotation with custom view
Annotation(place.name, coordinate: place.coordinate) {
PlaceAnnotationView(place: place, isSelected: selectedPlace?.id == place.id)
}
}
}
}
}
// Custom view for annotation
struct PlaceAnnotationView: View {
let place: Place
let isSelected: Bool
var body: some View {
VStack(spacing: 4) {
// Icon with colored background
Image(systemName: place.category.icon)
.font(.system(size: isSelected ? 20 : 16))
.foregroundStyle(.white)
.padding(8)
.background(place.category.tint)
.clipShape(Circle())
.shadow(radius: 4)
// Name displayed when selected
if isSelected {
Text(place.name)
.font(.caption)
.fontWeight(.medium)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
}
.animation(.spring(duration: 0.3), value: isSelected)
}
}Perbedaan utamanya: Marker menggunakan rendering native Apple Maps (pin standar), sedangkan Annotation memungkinkan kustomisasi penuh dengan view SwiftUI apa pun.
Geolokasi dan Izin
Pertanyaan 4: Bagaimana cara menangani izin lokasi dan menampilkan posisi pengguna?
Geolokasi memerlukan izin eksplisit dan penanganan yang tepat untuk berbagai status otorisasi. CLLocationManager menangani permintaan izin.
import SwiftUI
import MapKit
import CoreLocation
@Observable
class LocationManager: NSObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
// Location state
var location: CLLocation?
var authorizationStatus: CLAuthorizationStatus = .notDetermined
var isAuthorized: Bool {
authorizationStatus == .authorizedWhenInUse || authorizationStatus == .authorizedAlways
}
override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
// Get current status
authorizationStatus = manager.authorizationStatus
}
// Request location permission
func requestAuthorization() {
manager.requestWhenInUseAuthorization()
}
// Start location tracking
func startUpdatingLocation() {
guard isAuthorized else { return }
manager.startUpdatingLocation()
}
// Stop tracking to save battery
func stopUpdatingLocation() {
manager.stopUpdatingLocation()
}
// MARK: - CLLocationManagerDelegate
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
authorizationStatus = manager.authorizationStatus
if isAuthorized {
startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Take the most recent position
location = locations.last
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location error: \(error.localizedDescription)")
}
}
// View with map and user location
struct UserLocationMapView: View {
@State private var locationManager = LocationManager()
@State private var cameraPosition: MapCameraPosition = .userLocation(fallback: .automatic)
var body: some View {
Map(position: $cameraPosition) {
// Display the blue user location dot
UserAnnotation()
}
.mapControls {
// Button to recenter on position
MapUserLocationButton()
// Compass
MapCompass()
// Scale
MapScaleView()
}
.onAppear {
locationManager.requestAuthorization()
}
.overlay(alignment: .top) {
if !locationManager.isAuthorized {
PermissionBanner()
}
}
}
}
// Banner requesting authorization
struct PermissionBanner: View {
var body: some View {
HStack {
Image(systemName: "location.slash")
Text("Enable location in Settings")
Spacer()
Button("Open") {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
}
.buttonStyle(.bordered)
}
.padding()
.background(.ultraThinMaterial)
}
}Kunci NSLocationWhenInUseUsageDescription dan/atau NSLocationAlwaysUsageDescription harus didefinisikan di Info.plist dengan deskripsi penggunaan lokasi yang jelas.
Pertanyaan 5: Bagaimana cara melacak posisi secara real-time dan menggambar rute?
Pelacakan real-time sangat penting untuk aplikasi kebugaran atau navigasi. Pertanyaan ini menguji kemampuan menangani aliran data lokasi yang berkelanjutan.
import SwiftUI
import MapKit
import CoreLocation
@Observable
class RouteTracker: NSObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
// Position history for the trace
var routeCoordinates: [CLLocationCoordinate2D] = []
var currentLocation: CLLocation?
var isTracking = false
// Route statistics
var totalDistance: CLLocationDistance = 0
private var lastLocation: CLLocation?
override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
// Update even in background
manager.allowsBackgroundLocationUpdates = true
manager.pausesLocationUpdatesAutomatically = false
}
func startTracking() {
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
isTracking = true
routeCoordinates = []
totalDistance = 0
lastLocation = nil
}
func stopTracking() {
manager.stopUpdatingLocation()
isTracking = false
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let newLocation = locations.last else { return }
// Filter inaccurate positions
guard newLocation.horizontalAccuracy < 20 else { return }
currentLocation = newLocation
routeCoordinates.append(newLocation.coordinate)
// Calculate distance traveled
if let last = lastLocation {
totalDistance += newLocation.distance(from: last)
}
lastLocation = newLocation
}
}
struct RouteTrackingView: View {
@State private var tracker = RouteTracker()
@State private var cameraPosition: MapCameraPosition = .userLocation(fallback: .automatic)
var body: some View {
ZStack {
Map(position: $cameraPosition) {
// Current position
UserAnnotation()
// Route trace as polyline
if tracker.routeCoordinates.count > 1 {
MapPolyline(coordinates: tracker.routeCoordinates)
.stroke(.blue, lineWidth: 4)
}
}
.mapStyle(.standard)
// Control interface
VStack {
Spacer()
// Statistics
HStack {
VStack(alignment: .leading) {
Text("Distance")
.font(.caption)
.foregroundStyle(.secondary)
Text(formatDistance(tracker.totalDistance))
.font(.title2)
.fontWeight(.bold)
}
Spacer()
}
.padding()
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 12))
.padding()
// Start/stop button
Button {
if tracker.isTracking {
tracker.stopTracking()
} else {
tracker.startTracking()
}
} label: {
Label(
tracker.isTracking ? "Stop" : "Start",
systemImage: tracker.isTracking ? "stop.fill" : "play.fill"
)
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.tint(tracker.isTracking ? .red : .green)
.padding(.horizontal)
.padding(.bottom)
}
}
}
private func formatDistance(_ meters: CLLocationDistance) -> String {
if meters < 1000 {
return String(format: "%.0f m", meters)
} else {
return String(format: "%.2f km", meters / 1000)
}
}
}Siap menguasai wawancara iOS Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Overlay dan Bentuk Geometris
Pertanyaan 6: Bagaimana cara menggambar overlay kustom pada peta?
Overlay memungkinkan menampilkan zona, rute, atau wilayah pada peta. SwiftUI MapKit menawarkan MapCircle, MapPolygon, dan MapPolyline.
import SwiftUI
import MapKit
struct DeliveryZoneMapView: View {
@State private var position: MapCameraPosition = .region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 48.8566, longitude: 2.3522),
span: MKCoordinateSpan(latitudeDelta: 0.15, longitudeDelta: 0.15)
)
)
// Delivery zones with different times
let deliveryZones: [DeliveryZone] = [
DeliveryZone(
name: "Express Zone",
center: CLLocationCoordinate2D(latitude: 48.8566, longitude: 2.3522),
radius: 2000,
deliveryTime: "15 min",
color: .green
),
DeliveryZone(
name: "Standard Zone",
center: CLLocationCoordinate2D(latitude: 48.8566, longitude: 2.3522),
radius: 5000,
deliveryTime: "30 min",
color: .orange
)
]
var body: some View {
Map(position: $position) {
// Circles for delivery zones
ForEach(deliveryZones) { zone in
MapCircle(center: zone.center, radius: zone.radius)
.foregroundStyle(zone.color.opacity(0.2))
.stroke(zone.color, lineWidth: 2)
}
// Restaurant marker
Marker("Restaurant", systemImage: "storefront", coordinate: CLLocationCoordinate2D(latitude: 48.8566, longitude: 2.3522))
.tint(.red)
}
.overlay(alignment: .bottomLeading) {
// Legend
VStack(alignment: .leading, spacing: 8) {
ForEach(deliveryZones) { zone in
HStack {
Circle()
.fill(zone.color)
.frame(width: 12, height: 12)
Text("\(zone.name) - \(zone.deliveryTime)")
.font(.caption)
}
}
}
.padding()
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 8))
.padding()
}
}
}
struct DeliveryZone: Identifiable {
let id = UUID()
let name: String
let center: CLLocationCoordinate2D
let radius: CLLocationDistance
let deliveryTime: String
let color: Color
}
// Custom polygon (complex geographic zone)
struct PolygonOverlayView: View {
@State private var position: MapCameraPosition = .automatic
// Neighborhood coordinates
let neighborhoodCoordinates: [CLLocationCoordinate2D] = [
CLLocationCoordinate2D(latitude: 48.853, longitude: 2.347),
CLLocationCoordinate2D(latitude: 48.858, longitude: 2.352),
CLLocationCoordinate2D(latitude: 48.862, longitude: 2.348),
CLLocationCoordinate2D(latitude: 48.860, longitude: 2.340),
CLLocationCoordinate2D(latitude: 48.855, longitude: 2.342)
]
var body: some View {
Map(position: $position) {
// Polygon automatically closed
MapPolygon(coordinates: neighborhoodCoordinates)
.foregroundStyle(.blue.opacity(0.3))
.stroke(.blue, lineWidth: 3)
}
}
}Pertanyaan 7: Bagaimana cara menampilkan rute antara dua titik?
Perhitungan rute menggunakan MKDirections untuk mendapatkan rute dari server Apple. Tampilan kemudian dilakukan dengan MapPolyline.
import SwiftUI
import MapKit
@Observable
class RouteCalculator {
var route: MKRoute?
var isCalculating = false
var error: String?
// Calculate route between two points
func calculateRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) async {
isCalculating = true
error = nil
// Create source and destination placemarks
let sourcePlacemark = MKPlacemark(coordinate: source)
let destinationPlacemark = MKPlacemark(coordinate: destination)
// Configure request
let request = MKDirections.Request()
request.source = MKMapItem(placemark: sourcePlacemark)
request.destination = MKMapItem(placemark: destinationPlacemark)
request.transportType = .automobile // .walking, .transit available
request.requestsAlternateRoutes = false
let directions = MKDirections(request: request)
do {
let response = try await directions.calculate()
// Take the first route
route = response.routes.first
} catch {
self.error = error.localizedDescription
}
isCalculating = false
}
}
struct DirectionsMapView: View {
@State private var calculator = RouteCalculator()
@State private var position: MapCameraPosition = .automatic
let startPoint = CLLocationCoordinate2D(latitude: 48.8738, longitude: 2.2950) // Arc de Triomphe
let endPoint = CLLocationCoordinate2D(latitude: 48.8530, longitude: 2.3499) // Notre-Dame
var body: some View {
ZStack {
Map(position: $position) {
// Start and end markers
Marker("Start", systemImage: "car.fill", coordinate: startPoint)
.tint(.green)
Marker("End", systemImage: "flag.fill", coordinate: endPoint)
.tint(.red)
// Route trace
if let route = calculator.route {
MapPolyline(route.polyline)
.stroke(.blue, lineWidth: 5)
}
}
// Route information
if let route = calculator.route {
VStack {
Spacer()
RouteInfoCard(route: route)
}
}
if calculator.isCalculating {
ProgressView("Calculating route...")
.padding()
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
}
.task {
await calculator.calculateRoute(from: startPoint, to: endPoint)
}
}
}
struct RouteInfoCard: View {
let route: MKRoute
var body: some View {
HStack(spacing: 20) {
VStack(alignment: .leading) {
Text("Distance")
.font(.caption)
.foregroundStyle(.secondary)
Text(formatDistance(route.distance))
.font(.headline)
}
Divider()
.frame(height: 30)
VStack(alignment: .leading) {
Text("Estimated Time")
.font(.caption)
.foregroundStyle(.secondary)
Text(formatDuration(route.expectedTravelTime))
.font(.headline)
}
Spacer()
}
.padding()
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 12))
.padding()
}
private func formatDistance(_ meters: CLLocationDistance) -> String {
let km = meters / 1000
return String(format: "%.1f km", km)
}
private func formatDuration(_ seconds: TimeInterval) -> String {
let minutes = Int(seconds) / 60
if minutes < 60 {
return "\(minutes) min"
} else {
let hours = minutes / 60
let remainingMinutes = minutes % 60
return "\(hours)h \(remainingMinutes)min"
}
}
}Pencarian Tempat dan Geocoding
Pertanyaan 8: Bagaimana cara mengimplementasikan pencarian tempat dengan MKLocalSearch?
MKLocalSearch memungkinkan pencarian titik menarik, alamat, atau bisnis. Fitur ini sangat penting untuk aplikasi dengan bilah pencarian berbasis lokasi.
import SwiftUI
import MapKit
@Observable
class PlaceSearchManager {
var searchResults: [MKMapItem] = []
var isSearching = false
var searchText = ""
private var searchTask: Task<Void, Never>?
// Search with debounce
func search(query: String, in region: MKCoordinateRegion) {
searchTask?.cancel()
guard !query.isEmpty else {
searchResults = []
return
}
searchTask = Task {
// 300ms debounce
try? await Task.sleep(for: .milliseconds(300))
guard !Task.isCancelled else { return }
isSearching = true
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = query
request.region = region
request.resultTypes = [.pointOfInterest, .address]
let search = MKLocalSearch(request: request)
do {
let response = try await search.start()
if !Task.isCancelled {
searchResults = response.mapItems
}
} catch {
print("Search error: \(error.localizedDescription)")
searchResults = []
}
isSearching = false
}
}
func clearResults() {
searchResults = []
searchText = ""
}
}
struct SearchableMapView: View {
@State private var searchManager = PlaceSearchManager()
@State private var position: MapCameraPosition = .region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 48.8566, longitude: 2.3522),
span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
)
)
@State private var selectedItem: MKMapItem?
@State private var visibleRegion: MKCoordinateRegion?
var body: some View {
NavigationStack {
Map(position: $position, selection: $selectedItem) {
// Display search results
ForEach(searchManager.searchResults, id: \.self) { item in
Marker(item: item)
}
}
.onMapCameraChange { context in
// Capture visible region for search
visibleRegion = context.region
}
.searchable(text: $searchManager.searchText, prompt: "Search for a place")
.onChange(of: searchManager.searchText) { _, newValue in
if let region = visibleRegion {
searchManager.search(query: newValue, in: region)
}
}
.overlay(alignment: .bottom) {
if let item = selectedItem {
PlaceDetailCard(item: item)
}
}
.navigationTitle("Map")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct PlaceDetailCard: View {
let item: MKMapItem
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(item.name ?? "Unknown Place")
.font(.headline)
if let address = item.placemark.title {
Text(address)
.font(.subheadline)
.foregroundStyle(.secondary)
}
HStack {
if let phone = item.phoneNumber {
Button {
if let url = URL(string: "tel:\(phone)") {
UIApplication.shared.open(url)
}
} label: {
Label("Call", systemImage: "phone")
}
.buttonStyle(.bordered)
}
Button {
item.openInMaps()
} label: {
Label("Directions", systemImage: "arrow.triangle.turn.up.right.diamond")
}
.buttonStyle(.borderedProminent)
}
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(.ultraThickMaterial)
.clipShape(RoundedRectangle(cornerRadius: 16))
.padding()
}
}Pertanyaan 9: Bagaimana cara mengonversi alamat ke koordinat (geocoding)?
Geocoding mengubah alamat teks menjadi koordinat GPS. Reverse geocoding melakukan operasi sebaliknya.
import SwiftUI
import MapKit
import CoreLocation
@Observable
class GeocodingManager {
private let geocoder = CLGeocoder()
var coordinate: CLLocationCoordinate2D?
var address: String?
var isLoading = false
var error: String?
// Geocoding: address → coordinates
func geocode(address: String) async {
isLoading = true
error = nil
do {
let placemarks = try await geocoder.geocodeAddressString(address)
if let placemark = placemarks.first,
let location = placemark.location {
coordinate = location.coordinate
} else {
error = "Address not found"
}
} catch {
self.error = error.localizedDescription
}
isLoading = false
}
// Reverse geocoding: coordinates → address
func reverseGeocode(coordinate: CLLocationCoordinate2D) async {
isLoading = true
error = nil
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
do {
let placemarks = try await geocoder.reverseGeocodeLocation(location)
if let placemark = placemarks.first {
address = formatAddress(placemark)
} else {
error = "Address not found"
}
} catch {
self.error = error.localizedDescription
}
isLoading = false
}
// Format readable address
private func formatAddress(_ placemark: CLPlacemark) -> String {
var components: [String] = []
if let street = placemark.thoroughfare {
if let number = placemark.subThoroughfare {
components.append("\(number) \(street)")
} else {
components.append(street)
}
}
if let city = placemark.locality {
components.append(city)
}
if let country = placemark.country {
components.append(country)
}
return components.joined(separator: ", ")
}
}
struct GeocodingDemoView: View {
@State private var manager = GeocodingManager()
@State private var addressInput = ""
@State private var position: MapCameraPosition = .automatic
var body: some View {
VStack {
// Address input field
HStack {
TextField("Enter an address", text: $addressInput)
.textFieldStyle(.roundedBorder)
Button("Search") {
Task {
await manager.geocode(address: addressInput)
if let coord = manager.coordinate {
position = .region(MKCoordinateRegion(
center: coord,
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
))
}
}
}
.buttonStyle(.borderedProminent)
.disabled(addressInput.isEmpty || manager.isLoading)
}
.padding()
// Map with marker
Map(position: $position) {
if let coord = manager.coordinate {
Marker("Result", coordinate: coord)
.tint(.red)
}
}
if let error = manager.error {
Text(error)
.foregroundStyle(.red)
.padding()
}
}
}
}Geocoder Apple memberlakukan batasan permintaan. Untuk volume tinggi, disarankan menggunakan cache lokal atau layanan pihak ketiga seperti Mapbox atau Google Maps.
Pola Lanjutan dan Praktik Terbaik
Pertanyaan 10: Bagaimana cara mengoptimalkan performa dengan banyak penanda?
Menampilkan ratusan penanda dapat memengaruhi performa. Clustering dan pemuatan progresif merupakan solusi yang efektif.
import SwiftUI
import MapKit
struct ClusteringMapView: View {
@State private var position: MapCameraPosition = .automatic
@State private var visiblePlaces: [Place] = []
@State private var visibleRegion: MKCoordinateRegion?
// All data (potentially thousands)
let allPlaces: [Place]
var body: some View {
Map(position: $position) {
// Display only visible places
ForEach(visiblePlaces) { place in
Marker(place.name, coordinate: place.coordinate)
}
}
.onMapCameraChange(frequency: .onEnd) { context in
// Update when user stops moving
visibleRegion = context.region
updateVisiblePlaces(in: context.region)
}
}
// Filter places in visible region
private func updateVisiblePlaces(in region: MKCoordinateRegion) {
let minLat = region.center.latitude - region.span.latitudeDelta / 2
let maxLat = region.center.latitude + region.span.latitudeDelta / 2
let minLon = region.center.longitude - region.span.longitudeDelta / 2
let maxLon = region.center.longitude + region.span.longitudeDelta / 2
// Spatial filtering
var filtered = allPlaces.filter { place in
place.coordinate.latitude >= minLat &&
place.coordinate.latitude <= maxLat &&
place.coordinate.longitude >= minLon &&
place.coordinate.longitude <= maxLon
}
// Limit number of displayed markers
if filtered.count > 100 {
// Sampling or clustering
filtered = Array(filtered.prefix(100))
}
visiblePlaces = filtered
}
}
// Simplified manual clustering
struct ClusteredPlace: Identifiable {
let id = UUID()
let coordinate: CLLocationCoordinate2D
let count: Int
let places: [Place]
var isCluster: Bool { count > 1 }
}
@Observable
class ClusterManager {
var clusters: [ClusteredPlace] = []
// Group nearby places based on zoom level
func cluster(places: [Place], in region: MKCoordinateRegion) {
let gridSize = region.span.latitudeDelta / 10
var grid: [String: [Place]] = [:]
for place in places {
// Grid key based on position
let gridX = Int(place.coordinate.longitude / gridSize)
let gridY = Int(place.coordinate.latitude / gridSize)
let key = "\(gridX),\(gridY)"
grid[key, default: []].append(place)
}
// Convert to clusters
clusters = grid.map { (_, places) in
let centerLat = places.map(\.coordinate.latitude).reduce(0, +) / Double(places.count)
let centerLon = places.map(\.coordinate.longitude).reduce(0, +) / Double(places.count)
return ClusteredPlace(
coordinate: CLLocationCoordinate2D(latitude: centerLat, longitude: centerLon),
count: places.count,
places: places
)
}
}
}
struct ClusterAnnotationView: View {
let cluster: ClusteredPlace
var body: some View {
if cluster.isCluster {
// Display a circle with count
ZStack {
Circle()
.fill(.blue)
.frame(width: 40, height: 40)
Text("\(cluster.count)")
.font(.system(size: 14, weight: .bold))
.foregroundStyle(.white)
}
} else {
// Simple marker
Image(systemName: "mappin.circle.fill")
.font(.title)
.foregroundStyle(.red)
}
}
}Pertanyaan 11: Bagaimana cara mengintegrasikan MapKit dengan layanan Apple lainnya?
MapKit terintegrasi dengan beberapa framework Apple untuk menawarkan pengalaman yang lebih kaya: Look Around, CarPlay, WidgetKit.
import SwiftUI
import MapKit
struct LookAroundMapView: View {
@State private var position: MapCameraPosition = .automatic
@State private var selectedPlace: Place?
@State private var lookAroundScene: MKLookAroundScene?
@State private var showLookAround = false
let places: [Place]
var body: some View {
Map(position: $position, selection: $selectedPlace) {
ForEach(places) { place in
Marker(place.name, coordinate: place.coordinate)
}
}
.onChange(of: selectedPlace) { _, newPlace in
if let place = newPlace {
Task {
await loadLookAroundScene(for: place.coordinate)
}
}
}
.sheet(isPresented: $showLookAround) {
if let scene = lookAroundScene {
LookAroundPreview(scene: scene)
.frame(height: 300)
}
}
.safeAreaInset(edge: .bottom) {
if selectedPlace != nil && lookAroundScene != nil {
Button("View in Look Around") {
showLookAround = true
}
.buttonStyle(.borderedProminent)
.padding()
}
}
}
// Load Look Around scene for a position
private func loadLookAroundScene(for coordinate: CLLocationCoordinate2D) async {
let request = MKLookAroundSceneRequest(coordinate: coordinate)
do {
lookAroundScene = try await request.scene
} catch {
lookAroundScene = nil
print("Look Around not available: \(error.localizedDescription)")
}
}
}
// Interactive Look Around component
struct LookAroundPreview: View {
let scene: MKLookAroundScene
@State private var isNavigating = false
var body: some View {
LookAroundPreviewRepresentable(scene: scene, isNavigating: $isNavigating)
.overlay(alignment: .topTrailing) {
Button {
isNavigating.toggle()
} label: {
Image(systemName: isNavigating ? "stop.fill" : "play.fill")
.padding()
.background(.ultraThinMaterial)
.clipShape(Circle())
}
.padding()
}
}
}
// UIViewRepresentable for Look Around
struct LookAroundPreviewRepresentable: UIViewRepresentable {
let scene: MKLookAroundScene
@Binding var isNavigating: Bool
func makeUIView(context: Context) -> MKLookAroundViewController {
let controller = MKLookAroundViewController(scene: scene)
controller.isNavigationEnabled = isNavigating
return controller
}
func updateUIView(_ uiView: MKLookAroundViewController, context: Context) {
uiView.isNavigationEnabled = isNavigating
}
}Siap menguasai wawancara iOS Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Kesimpulan
MapKit dengan SwiftUI menyediakan API modern dan kuat untuk mengintegrasikan peta ke dalam aplikasi iOS. Penguasaan anotasi, overlay, geolokasi, dan pencarian tempat membedakan pengembang iOS yang berpengalaman dalam wawancara teknis.
Daftar Periksa Tinjauan
- ✅ Menampilkan peta dengan
Mapdan mengonfigurasiMapCameraPosition - ✅ Menggunakan berbagai gaya peta (
standard,imagery,hybrid) - ✅ Menambahkan
MarkerdanAnnotationkustom - ✅ Menangani izin lokasi dengan
CLLocationManager - ✅ Melacak posisi secara real-time dan menggambar rute
- ✅ Menggambar overlay (
MapCircle,MapPolygon,MapPolyline) - ✅ Menghitung rute dengan
MKDirections - ✅ Mengimplementasikan pencarian tempat dengan
MKLocalSearch - ✅ Melakukan geocoding dan reverse geocoding
- ✅ Mengoptimalkan performa dengan clustering penanda
- ✅ Mengintegrasikan Look Around untuk tampilan imersif
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

Pertanyaan wawancara SwiftUI NavigationStack: Pola navigasi 2026
Persiapkan wawancara iOS dengan pertanyaan penting seputar NavigationStack, NavigationPath, dan pola navigasi modern di SwiftUI.

Wawancara StoreKit 2: Manajemen Langganan dan Validasi Tanda Terima
Kuasai pertanyaan wawancara iOS tentang StoreKit 2, manajemen langganan, validasi tanda terima, dan implementasi pembelian dalam aplikasi dengan contoh kode Swift praktis.

Vision Framework dan CoreML: pertanyaan wawancara iOS tentang ML on-device
Persiapkan wawancara iOS dengan pertanyaan penting tentang Vision Framework dan CoreML: pengenalan gambar, deteksi objek, dan ML on-device.