Due to API changes, part of this article has been invalid, please check the latest complete Chinese tutorial and codeGithub.com/WillieWangW…

Wechat Technology Group

SwiftUI represents the direction of building App in the future. Welcome to join us to exchange technology and solve problems.

Add group needs to apply now, you can add my wechat first, note “SwiftUI”, I will pull you into the group.

Create and compose views

This section will guide you through building iOS app Landmarks to discover and share your favorite places. Let’s start by building a view that shows the details of the landmark.

Landmarks use stacks to combine and layer components such as image and text to lay out the view. If we want to add a map to a view, we need to introduce the standard MapKit component. As we make changes to the design, Xcode gives us real-time feedback so we can see how those changes translate into code.

Download the project file and follow these steps.

  • Estimated completion time: 40 minutes
  • Initial project file: Download

1. Create a new project and browse the Canvas

Use SwiftUI’s APP template to create a new Xcode project and browse the Canvas.

1.1 Open Xcode. In the Xcode startup window, click Create a new Xcode Project or choose File > New > Project.

1.2 Select the iOS platform, Single View App template, and click Next.

1.3 Enter Landmarks as Product Name, check the Use SwiftUI check box, and click Next. Select a location to save the project.

1.4 In Project Navigator, select ContentView.swift.

By default, the SwiftUI View file declares two structures. The first structure follows the View protocol and describes the content and layout of the View. The second structure declares a preview of the view.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

1.5 In the Canvas, click Resume to display the preview.

Tip: If there is no canvas, choose Editor > Editor and Canvas to display it.

1.6 Change Hello World to your own greeting in the Body property. As the code changes, the preview is updated in real time.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello SwiftUI!")
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

2. Customize the Text View

To customize the view’s display, you can change the code yourself, or use Inspector to help you write the code.

Inspectors may be used to work with any of the inspectors during the building of Landmarks: writing the source code, modifying canvas, or going through inspectors. The code will be kept up to date regardless of the inspectors.

Next, we use Inspector to customize the Text View.

2.1 In preview, hold down Command and click the greeting to display the edit window, and then select Inspect.

The edit window shows different properties that can be modified, depending on its View type.

2.2 Use Inspector to change the text to Turtle Rock, which is the name of the first landmark shown in the app.

2.3 Change Font to Title.

This change will make the text use the system font, which will then correctly display the user’s preferred font size and Settings.

Edit the code by hand to add the .color(.green) modifier; this changes the text’s color to green.

To customize a SwiftUI view, you call methods called modifiers. Modifiers wrap a view to change its display or other properties. Each modifier Returns a new view, so it’s common to chain multiple MODIfiers, stacked material.

2.4 Add.color(.green) to the code to change the color of the text to green.

To customize SwiftUI’s View, we can call a class of methods called modifiers. These methods wrap a view to change its display or other properties. Each modiFIERS method returns a new view, so you can call multiple modifiers chained.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Turtle Rock")
            .font(.title)
            .color(.green)
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

The real source of the view is actually code, and Xcode updates our code immediately when we use Inspector to change or remove modifiers.

2.5 This time we open Inspector by holding Command in the code editing area, clicking on the declaration of Text, and then selecting Inspect. Click on the Color menu and select Inherited, which changes the text back to black.

2.6 Note that Xcode automatically updates the code for changes, such as removing.color(.green).

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Turtle Rock")
            .font(.title)

    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

3. Stack the View

After creating the title View in the previous section, add the Text View, which displays detailed information about the landmark, such as the name of the park and the state it is in.

When creating a SwiftUI View, we can describe its content, layout, and behavior in the View’s body property. Since the body property only returns a single view, we can use Stacks to combine and embed multiple Views in horizontal, vertical, or back-to-front order.

In this section, we use a horizontal stack to display park details, and a vertical stack to place the title on top of the details.

You can use Xcode’s editing capabilities to embed a view into a container, or you can use Inspector or Help to find more help.

3.1 Hold down Command and click the initialization method of Text View, and select Embed in VStack in the edit window.

Next, we drag a Text view from the Library and add it to the stack.

3.2 Click the plus (+) button in the upper right corner of Xcode to open Library and drag a Text View after Turtle Rock in the code.

3.3 Change the Placeholder to Joshua Tree National Park.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Turtle Rock")
                .font(.title)
            Text("Joshua Tree National Park")
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

Adjust the location view to fit the layout requirements.

3.4 Set the font for the location view to.subheadline.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Turtle Rock")
                .font(.title)
            Text("Joshua Tree National Park")
                .font(.subheadline)
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

3.5 Editing the VStack initialization method, align views in leading mode.

By default, Stacks centers the content along its axis and sets the spacing to fit the context.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            Text("Joshua Tree National Park")
                .font(.subheadline)3
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

Next, we add another text view to the right of the site to show the state where the park is located.

3.6 Hold down Command on canvas, click Joshua Tree National Park, and select Embed in HStack.

3.7 Add a new Text view after the location, change the Placeholder to California, and then set the font to.subheadline.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            HStack {
                Text("Joshua Tree National Park")
                    .font(.subheadline)
                Text("California")
                    .font(.subheadline)
            }
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

3.8 Add a Spacer to the horizontal stack to split and fix Joshua Tree National Park and California so they share the entire screen width.

Spacer can expand its contained views so that they share all the space of their parent view, rather than defining their size only by their content.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            HStack {
                Text("Joshua Tree National Park")
                    .font(.subheadline)
                Spacer()
                Text("California")
                    .font(.subheadline)
            }
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

Finally, use the.padding() modifier to leave some space for the name and information of the landmark.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            HStack {
                Text("Joshua Tree National Park")
                    .font(.subheadline)
                Spacer()
                Text("California")
                    .font(.subheadline)
            }
        }
        .padding()
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

4. Customize the Image View

With the name and location view in place, let’s add an image to the landmark.

You don’t need to add a lot of code, just create a custom view and add a mask, border, and shadow to the image.

The image is first added to the project’s Asset Catalog.

4.1 Find Turtlerock. PNG in the Resources folder of your project and drag it into the editor of the Asset Catalog. Xcode will create an image set for the image.

Next, create a new SwiftUI View to customize the Image View.

4.2 Choose File > New > File to open the template selector. In the User Interface, select SwiftUI View, and click Next. Name the file circleImage.swift, and click Create.

The preparations are now complete.

4.3 replace the text view with a Turtle Rock Image using the Image(_:) initialization method.

CircleImage.swift

import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
    }
}

struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
Copy the code

4.4 Call.clipShape(Circle()) to crop the image to a Circle.

Circles can be used as a mask shape or a view can be formed by stroke or fill.

CircleImage.swift

import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
            .clipShape(Circle())
    }
}

struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
Copy the code

4.5 Create another gray stroke circle and add it as an overlay to the image to form the border of the image.

CircleImage.swift

import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
            .clipShape(Circle())
            .overlay(
                Circle().stroke(Color.gray, lineWidth: 4))
    }
}

struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
Copy the code

4.6 Add a shadow with a radius of 10 points.

CircleImage.swift

import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
            .clipShape(Circle())
            .overlay(
                Circle().stroke(Color.gray, lineWidth: 4))
            .shadow(radius: 10)
    }
}

struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
Copy the code

4.7 Change the color of the border to white to complete the Image View.

CircleImage.swift

import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
            .clipShape(Circle())
            .overlay(
                Circle().stroke(Color.white, lineWidth: 4))
            .shadow(radius: 10)
    }
}

struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
Copy the code

5. Use UIKit and SwiftUI together

Now that we’re ready to create a MapView, we’ll use the MKMapView class in MapKit to render the map.

Using UIView subclasses in SwiftUI requires wrapping other views in a SwiftUI View that follows the UIViewRepresentable protocol. SwiftUI includes protocols similar to WatchKit and AppKit View.

First, we create a custom view that renders MKMapView.

5.1 Choose File > New > File, select the iOS platform, select SwiftUI View template, and click Next. Name the new file mapView.swift, and click Create.

5.2 Add an import statement to MapKit stating that the MapView type complies with UIViewRepresentable.

Xcode errors can be ignored, and the next few steps will address them.

MapView.swift

import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {
    var body: some View {
        Text("Hello World")
    }
}

struct MapView_Preview: PreviewProvider {
    static var previews: some View {
        MapView()
    }
}
Copy the code

UIViewRepresentable protocol implements two methods: makeUIView(context:) to create an MKMapView, and updateUIView(_:context:) to configure the view and respond to changes.

5.3 replace the body property with the makeUIView(context:) method, which creates and returns an empty MKMapView.

MapView.swift

import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {
	
	typealias UIViewType = MKMapView

    func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
        return MKMapView(frame: .zero)
    }
}

struct MapView_Preview: PreviewProvider {
    static var previews: some View {
        MapView()
    }
}
Copy the code

5.4 Implement the updateUIView(_:context:) method and set the coordinates of the Map View to center it on Turtle Rock.

MapView.swift

import SwiftUI
import MapKit

struct MapView : UIViewRepresentable {
    
    typealias UIViewType = MKMapView
    
    func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
        return MKMapView(frame: .zero)
    }
    
    func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {
        letCoordinate = CLLocationCoordinate2D(latitude: 34.011286, longitude: -116.166868)letSpan = mkcoordinate espan (latitudeDelta: 2.0, longitude udedelta: 2.0)let region = MKCoordinateRegion(center: coordinate, span: span)
        uiView.setRegion(region, animated: true)
    }
}

struct MapView_Preview: PreviewProvider {
    static var previews: some View {
        MapView()
    }
}
Copy the code

Only SwiftUI View is displayed when the preview is in static mode. Because MKMapView is a UIView subclass, you need to switch to live mode to see the map.

5.5 Click Live Preview to switch the Preview to Live mode, sometimes using the Try Again or Resume button.

After a while, you’ll see a map of Joshua Tree National Park, home of Turtle Rock.

6. Write the detail View

Now we have all the components we need: name, place, circular image, and map.

Continue with the current tools and assemble these components into a detail view that fits the final design.

6.1 In the project navigation, select the contentView.swift file.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            HStack {
                Text("Joshua Tree National Park")
                    .font(.subheadline)
                Spacer()
                Text("California")
                    .font(.subheadline)
            }
        }
        .padding()
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

6.2 Embed the old VStack into the new VStack.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

6.3 Add a custom MapView to the top of the stack and use the frame(width:height:) method to set the MapView size.

If only height is specified, the view automatically adjusts the width of its content. In this section, the MapView expands and fills any available space.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .frame(height: 300)

            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

6.4 Click the Live Preview button to view the rendered map in the composite view.

During this process, we can continue to edit the View.

6.5 Adding the CircleImage to the Stack.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .frame(height: 300)

            CircleImage()

            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

6.6 To place the Image View at the top of the Map View, we need to set the image to an offset of -130 points and fill it with -130 points from the bottom.

As the image moves up, it makes room for text.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .frame(height: 300)

            CircleImage()
                .offset(y: -130)
                .padding(.bottom, -130)

            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

6.7 Add a spacer at the bottom of the external VStack to push the content to the top of the screen.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .frame(height: 300)

            CircleImage()
                .offset(y: -130)
                .padding(.bottom, -130)

            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()

            Spacer()
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

6.8 Finally, to extend the map content to the top edge of the screen, you need to add edgesIgnoringSafeArea(.top) to the Map View.

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .edgesIgnoringSafeArea(.top)
                .frame(height: 300)

            CircleImage()
                .offset(y: -130)
                .padding(.bottom, -130)

            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()

            Spacer()
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code