BLACK FRIDAY SALE: Save big on all my Swift books and bundles! >>

Adding user locations to a map

Paul Hudson    @twostraws   

This project is going to be based around a map view, asking users to add places to the map that they want to visit. To do that we need to place a Map so that it takes up our whole view, track its center coordinate, and then also whether or not the user is viewing place details, what annotations they have, and more.

We’re going to start with a full-screen Map view, then place a translucent circle on top to represent the center point. Although this view will have a binding to track the center coordinate, we don’t need to use that to place the circle – a simple ZStack will make sure the circle always stays in the center of the map.

First, add an extra import line so we get access to MapKit’s data types:

import MapKit

Second, add a property inside ContentView that will store the current state of the map. Later on we’re going to use this to add a place mark:

@State private var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 50, longitude: 0), span: MKCoordinateSpan(latitudeDelta: 25, longitudeDelta: 25))

That starts the map so that most of Western Europe and North Africa are visible.

And now we can fill in the body property

ZStack {
    Map(coordinateRegion: $mapRegion)
        .ignoresSafeArea()
    Circle()
        .fill(.blue)
        .opacity(0.3)
        .frame(width: 32, height: 32)
}

If you run the app now you’ll see you can move the map around freely, but there’s always a blue circle showing exactly where the center is.

All this work by itself isn’t terribly interesting, so the next step is to add a button in the bottom-right that lets us add place marks to the map. We’re already inside a ZStack, so the easiest way to align this button is to place it inside a VStack and a HStack with spacers before it each time. Both those spacers end up occupying the full vertical and horizontal space that’s left over, making whatever comes at the end sit comfortably in the bottom-right corner.

We’ll add some functionality for the button soon, but first let’s get it in place and add some basic styling to make it look good.

Please add this VStack below the Circle:

VStack {
    Spacer()
    HStack {
        Spacer()
        Button {
            // create a new location
        } label: {
            Image(systemName: "plus")
        }
        .padding()
        .background(.black.opacity(0.75))
        .foregroundColor(.white)
        .font(.title)
        .clipShape(Circle())
        .padding(.trailing)
    }
}

Notice how I added the padding() modifier twice there – once is to make sure the button is bigger before we add a background color, and the second time to push it away from the trailing edge.

Where things get interesting is how we place locations on the map. We’ve bound the location of the map to a property in ContentView, but now we need to send in an array of locations we want to show.

This takes a few steps, starting with a basic definition of the type of locations we’re creating in our app. This needs to conform to a few protocols:

  • Identifiable, so we can create many location markers in our map.
  • Codable, so we can load and save map data easily.
  • Equatable, so we can find one particular location in an array of locations.

In terms of the data it will contain, we’ll give each location a name and description, plus a latitude and longitude. We’ll also need to add a unique identifier so SwiftUI is happy to create them from dynamic data.

So, create a new Swift file called Location.swift, giving it this code:

struct Location: Identifiable, Codable, Equatable {
    let id: UUID
    var name: String
    var description: String
    let latitude: Double
    let longitude: Double
}

Storing latitude and longitude separately gives us Codable conformance out of the box, which is always nice to have. We’ll add a little more to that shortly, but it’s enough to get us moving.

Now that we have a data type where we can store an individual location, we need an array of those to store all the places the user wants to visit. We’ll put this into ContentView for now just we can get moving, but again we’ll return to it shortly to add more.

So, start by adding this property to ContentView:

@State private var locations = [Location]()

Next, we want to add a location to that whenever the + button is tapped, so replace the // create a new location comment with this:

let newLocation = Location(id: UUID(), name: "New location", description: "", latitude: mapRegion.center.latitude, longitude: mapRegion.center.longitude)
locations.append(newLocation)

Finally, update ContentView so that it sends in the locations array to be converted into annotations:

Map(coordinateRegion: $mapRegion, annotationItems: locations) { location in
    MapMarker(coordinate: CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
}
.ignoresSafeArea()

That’s enough map work for now, so go ahead and run your app again – you should be able to move around as much as you need, then press the + button to add locations.

I know it took a fair amount of work to get set up, but at least you can see the basics of the app coming together!

Hacking with Swift is sponsored by RevenueCat

SPONSORED In-app subscriptions are a pain to implement, hard to test, and full of edge cases. RevenueCat makes it straightforward and reliable so you can get back to building your app. Oh, and it's free if your app makes less than $10k/mo.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.7/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.