2026'da MapKit SwiftUI Mülakatı: Açıklamalar, Kaplamalar ve Konum Belirleme

iOS mülakatları için SwiftUI ile MapKit'te uzmanlaşın: özel açıklamalar, kaplamalar, konum belirleme, yer arama ve Maps entegrasyon kalıpları.

iOS Geliştiricileri için MapKit SwiftUI Mülakat Soruları

Haritalar birçok iOS uygulamasında temel bir özellik olarak yer alır: teslimat, fitness, emlak ve konum tabanlı sosyal ağlar. iOS 17'den itibaren MapKit, harita entegrasyonunu önemli ölçüde basitleştiren native bir SwiftUI API'si sunmaktadır. İşe alım uzmanları teknik mülakatlarda bu yetkinlikleri düzenli olarak değerlendirmektedir.

Rehber Yapısı

Her soru, gerçek bir teknik mülakatın formatını yansıtır; ayrıntılı yanıtlar ve çalışan kod içerir. Kavramlar temel düzeyden ileri düzeye doğru ilerler.

SwiftUI'de Harita Temelleri

Soru 1: SwiftUI ile temel bir harita nasıl gösterilir?

iOS 17'den itibaren SwiftUI, önceki UIViewRepresentable yaklaşımının yerini alan native Map bileşenini sunar. Map görünümü bir kamera konumu ve gösterilecek içerik kabul eder.

BasicMapView.swiftswift
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
    }
}

position özelliği farklı modlara izin veren bir enum olan MapCameraPosition kullanır: .automatic (otomatik ayarlama), .region (sabit bölge), .camera (tam kontrol) veya .userLocation (kullanıcıya ortalanmış).

Soru 2: Mevcut farklı harita stilleri nelerdir?

MapKit, farklı kullanım durumlarına uygun çeşitli harita stilleri sunar. .mapStyle() modifier'ı haritanın görsel görünümünü yapılandırır.

MapStyles.swiftswift
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)
        }
    }
}
İlgi Noktaları

.standard stili, gösterilen kategorileri filtrelemek için bir pointsOfInterest parametresi kabul eder: .including([.restaurant, .cafe]) veya .excluding([.nightlife]).

Soru 3: Haritaya işaretçiler ve açıklamalar nasıl eklenir?

İşaretçiler haritada ilgi noktalarını temsil eder. SwiftUI MapKit çeşitli türler sunar: Marker (standart pin), Annotation (özel görünüm) ve MapCircle/MapPolygon (geometrik şekiller).

MarkersAndAnnotations.swiftswift
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)
    }
}

Temel fark: Marker native Apple Maps render'ını (standart pin) kullanırken, Annotation herhangi bir SwiftUI görünümüyle tam özelleştirmeye olanak tanır.

Konum Belirleme ve İzinler

Soru 4: Konum izinleri nasıl yönetilir ve kullanıcı konumu nasıl gösterilir?

Konum belirleme açık izinler ve farklı yetkilendirme durumlarının doğru şekilde ele alınmasını gerektirir. CLLocationManager izin isteklerini yönetir.

LocationManager.swiftswift
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)
    }
}
Zorunlu Info.plist

NSLocationWhenInUseUsageDescription ve/veya NSLocationAlwaysUsageDescription anahtarları, konum kullanımının net bir açıklamasıyla Info.plist'te tanımlanmalıdır.

Soru 5: Gerçek zamanlı konum nasıl izlenir ve bir rota nasıl çizilir?

Gerçek zamanlı izleme, fitness veya navigasyon uygulamaları için kritik önemdedir. Bu soru, sürekli konum verisi akışını yönetme becerisini sınar.

RouteTrackingView.swiftswift
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)
        }
    }
}

iOS mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Kaplamalar ve Geometrik Şekiller

Soru 6: Haritaya özel kaplamalar nasıl çizilir?

Kaplamalar, harita üzerinde bölgeler, rotalar veya alanlar göstermeye olanak tanır. SwiftUI MapKit MapCircle, MapPolygon ve MapPolyline sunar.

MapOverlays.swiftswift
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)
        }
    }
}

Soru 7: İki nokta arasında bir rota nasıl gösterilir?

Rota hesaplama, Apple sunucularından yol almak için MKDirections kullanır. Görüntüleme daha sonra MapPolyline ile yapılır.

RouteDirections.swiftswift
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"
        }
    }
}

Yer Arama ve Coğrafi Kodlama

Soru 8: MKLocalSearch ile yer arama nasıl uygulanır?

MKLocalSearch, ilgi noktaları, adresler veya işletmeler için arama yapmaya olanak tanır. Bu özellik, konum bazlı arama çubuğu olan uygulamalar için temel önemdedir.

PlaceSearch.swiftswift
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()
    }
}

Soru 9: Bir adres koordinatlara nasıl dönüştürülür (coğrafi kodlama)?

Coğrafi kodlama, bir metin adresini GPS koordinatlarına dönüştürür. Ters coğrafi kodlama ise bunun tersini gerçekleştirir.

Geocoding.swiftswift
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()
            }
        }
    }
}
Coğrafi Kodlama Limitleri

Apple'ın coğrafi kodlayıcısı istek limitleri uygular. Yüksek hacimler için yerel önbellek ya da Mapbox veya Google Maps gibi üçüncü taraf hizmetler önerilir.

İleri Seviye Kalıplar ve En İyi Uygulamalar

Soru 10: Çok sayıda işaretçi ile performans nasıl optimize edilir?

Yüzlerce işaretçi göstermek performansı etkileyebilir. Kümeleme ve aşamalı yükleme etkili çözümlerdir.

ClusteringMapView.swiftswift
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)
        }
    }
}

Soru 11: MapKit diğer Apple servisleriyle nasıl entegre edilir?

MapKit, zenginleştirilmiş bir deneyim sunmak için çeşitli Apple framework'leri ile entegre olur: Look Around, CarPlay, WidgetKit.

LookAroundIntegration.swiftswift
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
    }
}

iOS mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Sonuç

SwiftUI ile MapKit, iOS uygulamalarına harita entegrasyonu için modern ve güçlü bir API sağlar. Açıklamalar, kaplamalar, konum belirleme ve yer arama konusundaki ustalık, teknik mülakatlarda deneyimli iOS geliştiricilerini öne çıkarır.

Gözden Geçirme Listesi

  • Map ile harita göstermek ve MapCameraPosition yapılandırmak
  • ✅ Farklı harita stillerini kullanmak (standard, imagery, hybrid)
  • ✅ Özel Marker ve Annotation eklemek
  • CLLocationManager ile konum izinlerini yönetmek
  • ✅ Gerçek zamanlı konumu izlemek ve rota çizmek
  • ✅ Kaplamalar çizmek (MapCircle, MapPolygon, MapPolyline)
  • MKDirections ile rotaları hesaplamak
  • MKLocalSearch ile yer arama uygulamak
  • ✅ Coğrafi kodlama ve ters coğrafi kodlama yapmak
  • ✅ İşaretçi kümeleme ile performansı optimize etmek
  • ✅ İmmersif görünüm için Look Around entegre etmek

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Etiketler

#mapkit
#swiftui
#ios
#geolocation
#interview

Paylaş

İlgili makaleler