Skip to content

处理用户输入-HandlingUserInput

在地标APP中,用户可以标记他们喜欢的地方,

In the Landmarks app, a user can flag their favorite places, and filter the list to show just their favorites. To create this feature, you’ll start by adding a switch to the list so users can focus on just their favorites, and then you’ll add a star-shaped button that a user taps to flag a landmark as a favorite. Download the starter project and follow along with this tutorial, or open the finished project and explore the code on your own.

工程文件下载

Mark the User’s Favorite Landmarks

Begin by enhancing the list to show users their favorites at a glance. Add a star to eachLandmarkRowthat shows a favorite landmark.

Step 1

Open the starting point Xcode project, and then selectLandmarkRow.swiftin the Project navigator.

Step 2

After the spacer, add a star image inside anifstatement to test whether the current landmark is a favorite.

In SwiftUI blocks, you useifstatements to conditionally include views.

Step 3

Because system images are vector based, you can change their color with theforegroundColor(_:)modifier.

The star is present whenever a landmark’sisFavoriteproperty istrue. You’ll see how to modify that property later in this tutorial.

Filter the List View

You can customize the list view so that it shows all of the landmarks, or just the user’s favorites. To do this, you’ll need to add a bit of_state_to theLandmarkListtype.

State is a value, or a set of values, that can change over time, and that affects a view’s behavior, content, or layout. You use a property with the@Stateattribute to add state to a view.

Step 1

SelectLandmarkList.swiftin the Project navigator. Add a@Stateproperty calledshowFavoritesOnlytoLandmarkList, with its initial value set tofalse.

Step 2

Refresh the canvas by clicking theResumebutton.

When you make changes to your view’s structure, like adding or modifying a property, you need to manually refresh the canvas.

Step 3

Filter the list of landmarks by checking theshowFavoritesOnlyproperty and eachlandmark.isFavoritevalue.

Add a Control to Toggle the State

To give the user control over the list’s filter, you need to add a control that can alter the value ofshowFavoritesOnly. You do this by passing a binding to a toggle control.

A binding acts as a reference to a mutable state. When a user taps the toggle from off to on, and off again, the control uses the binding to update the view’s state accordingly.

Step 1

Create a nestedForEachgroup to transform the landmarks into rows.

To combine static and dynamic views in a list, or to combine two or more different groups of dynamic views, use theForEachtype instead of passing your collection of data toList.

Step 2

Add aToggleview as the first child of theListview, passing a binding toshowFavoritesOnly.

You use the$prefix to access a binding to a state variable, or one of its properties.

Step 3

Use the live preview and try out this new functionality by tapping the toggle.

Use an Observable Object for Storage

To prepare for the user to control which particular landmarks are favorites, you’ll first store the landmark data in an_observable object._

An observable object is a custom object for your data that can be bound to a view from storage in SwiftUI’s environment. SwiftUI watches for any changes to observable objects that could affect a view, and displays the correct version of the view after a change.

Step 1

Create a new Swift file namedUserData.swift.

Step 2

Declare a new model type that conforms to theObservableObjectprotocol from the Combine framework.

SwiftUI subscribes to your observable object, and updates any views that need refreshing when the data changes.

Step 3

Add stored properties forshowFavoritesOnlyandlandmarks, along with their initial values.

An observable object needs to publish any changes to its data, so that its subscribers can pick up the change.

Step 4

Add the@Publishedattribute to each property in the model.

Adopt the Model Object in Your Views

Now that you’ve created theUserDataobject, you need to update your views to adopt it as the data store for your app.

Step 1

InLandmarkList.swift, replace theshowFavoritesOnlydeclaration with an@EnvironmentObjectproperty, and add anenvironmentObject(_:)modifier to the preview.

ThisuserDataproperty gets its value automatically, as long as theenvironmentObject(_:)modifier has been applied to a parent.

Step 2

Replace the uses ofshowFavoritesOnlyby accessing the same property onuserData.

Just like on@Stateproperties, you can access a binding to a member of theuserDataobject by using the$prefix.

Step 3

UseuserData.landmarksas the data when creating theForEachinstance.

Step 4

InSceneDelegate.swift, add theenvironmentObject(_:)modifier to theLandmarkList.

If you build and run Landmarks in the simulator or on a device, rather than using the preview, this update ensures thatLandmarkListhas aUserDataobject in the environment.

Step 5

Update theLandmarkDetailview to work with theUserDataobject in the environment.

You’ll uselandmarkIndexwhen accessing or updating the landmark’s favorite status, so that you’re always accessing the correct version of that data.

Step 6

Switch back toLandmarkList.swiftand turn on the live preview to verify that everything is working as it should be.

Create a Favorite Button for Each Landmark

The Landmarks app can now switch between a filtered and unfiltered view of the landmarks, but the list of favorite landmarks is still hard coded. To allow the user to add and remove favorites, you need to add a favorite button to the landmark detail view.

Step 1

InLandmarkDetail.swift, embed the landmark’s name in anHStack.

Step 2

Create a new button next to the landmark’s name. Use anif-elseconditional statement to provide different images that indicate whether the landmark is a favorite.

In the button’sactionclosure, the code useslandmarkIndexwith theuserDataobject to update the landmark in place.

Step 3

Switch back toLandmarkList.swift, and turn on the live preview.

As you navigate from the list to the detail and tap the button, those changes persist when you return to the list. Because both views are accessing the same model object in the environment, the two views maintain consistency.