3
3

More than 3 years have passed since last update.

MapKitを使ってSwiftUI2.0でのdelegateの書き方のサンプルを作ってみた

Last updated at Posted at 2020-11-06

TL;DR

Objective-Cで書いていたSDKなどを使ったdelegateのサンプルをCoreLocation を参考に作ってみました。ソースはこちら> https://github.com/dropcontrol/LocationUpdata
またSwiftUIのProperty Wrapperを使った方法として@ObservableObject@ObserbedObject@Stateの代わりに使っています。実際は単一のViewの中でのPropertyの変更なので@Stateで充分なんですが。@ObservableObjectを使う場合は複数のViewに跨ってPropertyの変更が必要な場合と言うことです。
参考:
https://rusutikaa.github.io/docs/developer.apple.com/documentation/swiftui/managing-model-data-in-your-app.html
https://capibara1969.com/2508/

delegateをどう書くか?

アプリ自体はMapKitを使って緯度経度を取得し、それを表示する、と言うだけの単機能アプリです。で、その緯度経度を取得する部分はCLLocationManagerDelegateで定義されている、func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {で行います。なのでdelegateをどう書くか?

struct ContentView: View {

    @ObservedObject var placeInfo = PlaceInfo()

    var manager = CLLocationManager()
    var managerDelegate = locationDelegate()


    var body: some View {


        VStack{

            HStack {
                Text("Latitude:")
                Text(placeInfo.latitude)
            }
            HStack {
                Text("longitude:")
                Text(placeInfo.longitude)
            }
            Button(action: {
                print("Button Tapped")

                placeInfo.latitude = managerDelegate.currentLatitude
                placeInfo.longitude = managerDelegate.currentLongitude

            }){
                Text("Current Location")
            }
        }
        .onAppear() {
            manager.delegate = managerDelegate
            managerDelegate.locationManagerDidChangeAuthorization(manager)

        }
    }
}

として、
* CLLocationManager()のインスタンス var manager を作る
* LocationDelegate()のインスタンス var managerDelegate を作る
* .onAppear()で manager.delegate = managerDeleagte してdelegateを設定する

Buttonの中にある、managerDelegate.locationManagerDidChangeAuthorization(manager)や、.onAppear()のmanagerDelegate.locationManagerDidChangeAuthorization(manager)はこれでアクセスできます。

delegate自体は以下のように記述。

class locationDelegate : NSObject, ObservableObject, CLLocationManagerDelegate {

    // delegateから取り出すための変数
    var currentLatitude: String = "none"
    var currentLongitude: String = "none"

    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {

        if manager.authorizationStatus == .authorizedWhenInUse {
            print("authorized")

            manager.startUpdatingLocation()

            // add "Privacy - Location Default Accuracy Reduced" in info.plist
            // and edit in souce code that value is <true/> or <false/>
            if manager.accuracyAuthorization != .fullAccuracy {
                print("reduce accuracy")

                // add "Privacy - Location Temporary Usage Description Dictionary" in info.plist
                // and set "Location" in Key
                manager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: "Location") {
                    (err) in
                    if err != nil {
                        print(err!)
                        return
                    }
                }
            }
        } else {
            print("not authorized")
            // add "Privacy - Location When In Use Usage Description" in info.plist
            manager.requestWhenInUseAuthorization()
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            print(String(format: "%+.06f", location.coordinate.latitude))
            print(String(format: "%+.06f", location.coordinate.longitude))

            currentLatitude = String(format: "%+.06f", location.coordinate.latitude)
            currentLongitude = String(format: "%+.06f", location.coordinate.longitude)

        }
    }
}

先の managerDelegate.locationManagerDidChangeAuthorization(manager)は、CoreLocationgが持つメソッドで、ユーザーに位置情報の使用許諾をとるアラートを出すものです(そこを通過しないと位置情報は取れない)。func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {}で定義されています。info.plistにて設定をすることでどのレベルでどのように情報を取得の許諾をとるのか?を設定できます。

次の func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {} で位置情報のアップデートを行っています。これがdelegateのファンクションです。実際このfunctionはどこからも呼ばれていなくて、位置情報が更新されるたびに呼ばれています。

@ObservableObject@ObserbedObject

チラッと書いたようにこのアプリではProperty Wrapperを@Stateで書いても全く問題ない(ソースにはそれもコメントアウトして残してあります)のですが、@ObservableObjectを使うと@Publisedを変数に着けることで更新を自動で反映し、かつ複数のビューに跨ってその変数を共有し、かつ1箇所にまとめてかける、と言う利点があります。

書き方

@ObservableObjectは以下のようにクラスとして定義します。

class PlaceInfo: ObservableObject {
    @Published var latitude: String = "none"
    @Published var longitude: String = "none"
}

ここで自動保管で@ObservedObjectを間違えて打ってるとエラーになります(当たり前ですが、やりがち)。

使うときは、、、

@ObservedObject var placeInfo = PlaceInfo() //こっちが Observed。

でインスタンス化して、あとは placeInfo.latitudeのようにすれば呼び出しも変数の書き換えも行えます。@Stateで書くよりもこっちの方がすっきり書けるな、と個人的には思います。単一のビューで使っても問題がある書き方には見えないのですが、変数のスコープを管理したい、と言う場合にはきちんと使い分けするのがいいんでしょうね。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3