センタMapプロジェクト


紹介する


地図上にN個の位置がある場合、中間位置の地図上に表示されるサービスです.

期間


2021.09.30 ~ 10.01😇

ぎじゅつ

  • Swift
  • MVC Pattern
  • MapKit
  • 羽状バニラ


    https://github.com/sanghee-dev/Center-Map
    YappUltraHardPractice/Sanghee/Practice2/Practice2 at personal/Sanghee-2 · della-padula/YappUltraHardPractice

    特長


    要求

  • コードライブラリを使用して自動的にレイアウトします.
  • MacKit.
  • の中間位置を計算するために独立して設計されたモジュール(単点モードを使用).
  • の中間位置を計算した後に追加されたすべての位置座標に対してZoom-OutまたはZoom-In処理を行い、画面に入る.
  • 場所を追加するには:

  • は最大10個です.
  • 地図上で、長触指定位置でAlertが表示されます.
  • 位置が追加されるたびに、中間位置が計算され、中間位置が変更されます.
  • 追加位置と中間位置はマークで表示され、異なる色が指定されます.
  • 場所を削除するには、次の手順に従います。

  • に追加された位置マーカーをタッチするとAlertが表示されます.
  • が削除されるたびに、中間位置が計算され、中間位置が変更されます.
  • の位置マーカーが1つ未満の場合、中間位置マーカーも消えます.
  • 画面


    ウェルカムスクリーン

  • の位置を要求した後、ユーザの位置に応じて地図を拡大する.

  • 場所の追加

  • 地図上で、長触指定位置でAlertが表示されます.
  • の例を選択して、その場所にタグを追加します.

  • 中央マーク

  • の位置が2つを超える場合、中間距離を計算してタグを表示します.
  • の位置を追加したら、中心距離を再計算して中心マーカーを変更します.

  • 場所の削除

  • マークに触れたときにAlertを解放します.
  • の例を選択すると、タグが削除されます.
  • タグを削除した後、中心距離を再計算して中心タグを変更します.

  • コードの説明


    もっと詳しいコードは羽センターで確認してください.

    MapViewController


    変数、定数宣言


    LocationManagerと注記マネージャをインポートします.モノトーンモードで作られているので~.生成されたものにsharedでアクセスします.注記properties傍観者を使用し、値を設定して3つの関数を実行します.また、centerAnnotationでは、位置タグが変更されるたびに、中心タグも変更されます.変更する場合は、以前の中央タグを削除します.したがって、didSetを使用して新しい中央タグを格納すると、古い中央タグが削除されます.以前のタグはdidSetからoldValueにアクセスできます.最後にmapViewを生成します.
    class MapViewController: UIViewController {
        private let locationManager = LocationManager.shared
        private let annotationManager = AnnotationManager.shared
        private let mapView = MKMapView()
    
        private var annotations: [MKPointAnnotation] = [] {
            didSet {
                showAnnotations()
                addCenterAnnotation()
                zoomMapView()
            }
        }
        private var centerAnnotation: MKPointAnnotation? {
            didSet {
                if let oldValue = oldValue {
                    deleteAnnotation(oldValue)
                }
            }
        }

    ロングタッチ時の位置値の取得


    viewDidLoadになると、ビューにロングタッチジェスチャーを認識する機能が追加されます.長時間タッチした場合はgetCoordinate関数を実行します.この関数は、既存のmapViewで作成したタッチ位置を座標に変換し、annotations配列に追加します.
    private func addLongGesture() {
        let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(getCoordinate))
        view.addGestureRecognizer(longGesture)
    }
        
    @objc
    private func getCoordinate(_ longGesture: UILongPressGestureRecognizer) {
        let touchPoint = longGesture.location(in: mapView)
        let coordinate = mapView.convert(touchPoint, toCoordinateFrom: mapView)
        addAnnotation(coordinate)
    }

    位置マーカーを含むマップビューのズームとズーム


    まず、タグが1つ未満でない場合、関数は実行されません(タグを追加するとスケールが悪くなります).spaceはviewとのpadding値です.アノテーションでzoomRectという長方形を作成します.次に、MapViewを設定して長方形を表示します.
    private func zoomMapView() {
        guard annotations.count > 1 else { return }
            
        var zoomRect: MKMapRect = MKMapRect.null
        let padding: CGFloat = 50
    
        for annotation in annotations {
            let point = MKMapPoint(annotation.coordinate)
            let rect = MKMapRect(x: point.x, y: point.y, width: 0.1, height: 0.1)
                
            zoomRect = zoomRect.isNull ? rect : zoomRect.union(rect)
        }
            
        mapView.setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding), animated: true)
    }

    起動時にプレイヤー位置を中心に地図を拡大


    ユーザ位置はmapViewのuserLocation変数によって取得されます.この位置を中心に拡大します.
    func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
        let region = MKCoordinateRegion(center: userLocation.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05))
        mapView.setRegion(region, animated: true)
    }

    Utility


    LocationManager


    中間位置計算モジュールは単独で設計され,Singletonモードを採用した.モノトーンモードとは、特定の用途で1つのオブジェクトのみを生成し、共用したい場合に使用するデザインモードです.sharedという変数を一度作成し、コード全体でLocationManagerを使用します.共有アクセス使用.その後、init関数にprivateを追加して再生成を防止し、外部へのアクセスを阻止します.権限によって要求が異なる.
    import CoreLocation
    
    class LocationManager: NSObject, CLLocationManagerDelegate {
        static let shared = LocationManager()
        private var manager: CLLocationManager = CLLocationManager()
        
        private override init() {
            super.init()
            manager.delegate = self
        }
    
        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
            if status == .authorizedWhenInUse {
                askLocation()
            }
        }
        
        func askLocation() {
            switch manager.authorizationStatus {
            case .authorizedAlways: manager.startUpdatingLocation()
            case .authorizedWhenInUse: manager.requestAlwaysAuthorization()
            case .notDetermined: manager.requestWhenInUseAuthorization()
            case .restricted, .denied: break
            @unknown default: break
            }
        }
    }

    AnnotationManager


    AnnotationManagerもモノトーンモードを使用しています.このマネージャはannotations配列を受け入れ、中心位置に戻ります.
    import MapKit
    import UIKit
    
    class AnnotationManager {
        static let shared = AnnotationManager()
        
        private init() {}
    
        func getCenterCoordinate(_ annotations: [MKAnnotation]) -> CLLocationCoordinate2D {
            let count = Double(annotations.count)
            let latitude = annotations.map({ $0.coordinate }).map({ $0.latitude }).reduce(0, +) / count
            let longitude = annotations.map({ $0.coordinate }).map({ $0.longitude }).reduce(0, +) / count
            let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
            
            return coordinate
        }
    }

    Extensions


    コードライブラリの自動レイアウト


    Snapkitではなくコードライブラリ自動レイアウトが使用されているため、UIViewではレイアウトを指定するアンカーポイントとcenter、centerX、centerYという関数が生成されます.paddingの間隔の場合、デフォルトは0です.次にconstaintsを更新します.この関数では、updateConstraintsメソッドを呼び出す必要があります.実際にneedsUpdateConstraints()が出力されると、trueからfalseに変わることがわかります.
    extension UIView {
        func anchor(top: NSLayoutYAxisAnchor? = nil,
                    left: NSLayoutXAxisAnchor? = nil,
                    ...
                    paddingTop: CGFloat = 0,
                    paddingLeft: CGFloat = 0,
                    ...) {
            translatesAutoresizingMaskIntoConstraints = false
            
            if let top = top {
                topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
            }
            if let left = left {
                leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
            }
            ...
            updateConstraintsIfNeeded()
        }
        
        func center(inView view: UIView, constant: CGFloat = 0) {
            translatesAutoresizingMaskIntoConstraints = false
            centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: constant).isActive = true
            centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: constant).isActive = true
            updateConstraintsIfNeeded()
        }
        ...
    }

    extension CLLocationCoordinate2D


    CLLocationCoordinate 2 DはEqtableを採用していないタイプです.Equatableは、プロトコルで値が同じかどうかを比較できます.これらのタイプの値を比較して、同じかどうかを判断したいため、extension==関数が生成されます.
    import CoreLocation
    
    extension CLLocationCoordinate2D: Equatable {
        public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool {
            return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
        }
    }

    PrintFn


    デバッグ時にのみ出力するためにprint関数を上書きします.objectを出力すると「DEBUG:(object)」と出力され、他の情報と混合することなく出力結果が表示されます.
    public func print(_ object: Any) {
        #if DEBUG
        Swift.print("DEBUG: \(object)")
        #endif
    }