-
Handling User Input 예제 정리SwiftUI/Document 예제 2020. 12. 19. 10:00
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.
Landmark 앱에서, 유저는 좋아하는 장소들을 표시할 수 있고, 그 곳들을 보기위해 list를 filter 할 수 있다. 이 feature를 만들기 위해서는 유저가 그 곳들에 집중할 수 있는 list에 대한 swift를 추가해야하고 좋아하는 landmark라고 표시하는 tap할 수 있는 button을 별 모양으로 추가하시작 프로젝트를 다운로드하고 이 튜토리얼을 따르거나 완료된 프로젝트를 열고 직접 코드를 보자.
Section 1
Mark the User’s Favorite Landmarks
Begin by enhancing the list to show users their favorites at a glance. Add a property to the Landmark structure to read the initial state of a landmark as a favorite, and then add a star to each LandmarkRow that shows a favorite landmark.
-> 유저의 즐겨찾기를 보여주는 list를 만들기를 시작하다. Landmark 구조체에 즐겨찾기 랜드마크의 상태를 읽을 수 있는 property를 만들고, 즐겨찾기 랜드마크를 보여줄 LandmarkRow에 star를 추가하자.
Step 1
Open the starting point Xcode project or the project you finished in the previous tutorial, and select Landmark.swift in the Project navigator.
-> starting point XCode 프로젝트를 열거나 이전 튜토리얼에서 완료한 프로젝트를 열어서 Landmark.swift 파일을 선택하라
Step 2
Add an isFavorite property to the Landmark structure.
The landmarkData.json file has a key with this name for each landmark. Because Landmark conforms to Codable, you can read the value associated with the key by creating a new property with the same name as the key.
-> isFavorite property를 Landmark 구조체에 추가하라.
landmarkData.json 파일은 각각의 landmark마다 isFavorite이라는 이름의 키를 가지고 있다. Landmark 구조체는 Codable 프로토콜을 채택하고 있으니 isFavorite이라는 프로퍼티로 같은 이름의 key에 있는 값 연관 값으로 읽을 수 있다.
Step 3
Select LandmarkRow.swift in the Project navigator.
-> LandmarkRow.swift 파일을 navigator에서 선택하라
Step 4
After the spacer, add a star image inside an if statement to test whether the current landmark is a favorite.
In SwiftUI blocks, you use if statements to conditionally include views.
-> spacer 아래에 현재 landmark가 favorite인지 아닌지를 판단하는 if 문 내에 star image를 추가하라
SwiftUI block들 안에는 조건에 따라 view를 추가하고 싶을때 사용할 수 있다.
Step 5
Because system images are vector based, you can change their color with the foregroundColor(_:) modifier.
The star is present whenever a landmark’s isFavorite property is true. You’ll see how to modify that property later in this tutorial.
-> system image들은 기본적으로 vertor 이미지들이기 때문에, foregroundColor(_:) modifier를 이용하여 색깔을 변경할 수 있다.
star는 landmark의 isFavorite property가 true일때 보여진다. 이 후 튜토리얼에서 이 property를 어떻게 수정하는지 알 수 있다.
Section 2
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 the LandmarkList type.
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 @State attribute to add state to a view.
-> list를 커스터마이징 하여 모든 랜드마크를 보여줄지 유저의 즐겨찾기만 보여줄지 설정할 수 있다. 이를 하기 위해선 LandmarkList type에 몇가지 상태 추가가 필요하다.
State는 변경될 수 있고 뷰나 컨텐츠나 레이아웃 등에 영향을 줄 수 있는 value이자 values의 집합이다. view에 state를 추가하기 위한 @State 속성을 가진 property를 만들자.
*정리 : 그냥 사용자의 선택에 따라 전체를 보여줄지 즐겨찾기를 보여줄지를 판단할 property를 만들자는 설명이다.
Step 1
Select LandmarkList.swift in the Project navigator and revert the preview to show only a single version of the list.
-> LandmarkList.swift를 선택한 후 preview를 오직 하나의 list만 보여지도록 변경하라
Step 2
Add a @State property called showFavoritesOnly with its initial value set to false.
Because you use state properties to hold information that’s specific to a view and its subviews, you always create state as private.
-> showFavoritesOnly 라는 이름의 초기값은 false를 가지는 @State property를 하나 만들어라
특정 뷰에와 하위 뷰에 대한 정보를 가지고 있는 state property들이 필요하기 때문에 항상 state는 private하게 만들어라.
Step 3
Click Resume to refresh the canvas.
When you make changes to your view’s structure, like adding or modifying a property, you need to manually refresh the canvas.
-> Resume을 눌러 canvas를 리프레쉬 시켜라
property를 수정하거나 추가한 것과 같이 view의 structure에 변경을 했을 때, 직접 canvas를 리프레쉬 시켜라
Step 4
Compute a filtered version of the landmarks list by checking the showFavoritesOnly property and each landmark.isFavoritevalue.
-> 각각의 landmark.isFavorite과 showFavoritesOnly property를 변경하여 landmarks list의 버젼을 변경하도록 하라
*참고: 자 멘붕 왔을거다. 우선 우리 함께 filter에 대해서 알아보자
*filter(_:)란?
-> 배열에서 특정 조건에 맞는 아이템들만 따로 배열로 만들고 싶을때 사용하는 함수
isIncluded라는 이름의 매개변수를 받는데 isIncluded 매개변수는 배열의 element 하나를 매개변수로 받고, Bool 값을 리턴하는 클로져이다. 풀어서 설명해보자면 특정 배열의 각 element들을 하나씩 isIncluded 클로져의 매개변수로 넘기고 조건을 만족하는 즉, 조건문에서 true인 element들만을 순서대로 포함하여 배열을 새롭게 return 해준다.
*참고 2: filter 함수가 아니라 || 에서 멘붕이 왔다면 구글에 "swift 논리연산자"에 대해서 찾아보라
Step 5
Use the filtered version of the list of landmarks in the List.
-> List에 있는 랜드마크들의 구별된 list 버젼을 사용하라
Step 6
Change the initial value of showFavoritesOnly to true to see how the list reacts.
-> showFavoritesOnly를 true로 변경하고 어떻게 list가 반응하는지 봐라
Section 3
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 of showFavoritesOnly. 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.
-> 유저가 list의 filter을 컨트롤 하도록 하기 위해선, showFavoritesOnly 값을 수정할 수 있는 control을 추가해 주어야 한다. 당신은 이것을 toggle control에게 전달해줌으로서 할 수 있다.
binding은 변경가능한 state에게 참조하는 역할을 한다. 유저가 toggle을 off에서 on으로 누르고 다시 off를 눌렀을때, control은 뷰의 상태를 업데이트 하기 위해 biding을 사용한다.
Step 1
Create a nested ForEach group 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 the ForEach type instead of passing your collection of data to List.
-> landmark들을 row들로 변경시키기 위해 ForEach 그룹을 생성하라
list 에서 static views와 dynamic views를 결합하거나, 두 개 이상의 다른 dynamic view들의 group들을 결합하려면, List에 data collection을 넘기기 보단 ForEach를 사용하라.
Step 2
Add a Toggle view as the first child of the List view, passing a binding to showFavoritesOnly.
You use the $ prefix to access a binding to a state variable, or one of its properties.
-> List view의 첫번째 하위 뷰로 showFavoritesOnly에 binding을 넘겨줄 Toggle view를 추가하라.
$ 접두사를 사용하여 state 변수 또는 그것의 property들 중 하나에 접근할 수 있습니다.
Step 3
Before moving on, return the default value of showsFavoritesOnly to false.
-> 다음으로 이동하기 전에 showsFavoritesOnly를 false로 줘봐라
Step 4
Use the live preview and try out this new functionality by tapping the toggle.
-> preview를 사용해보고 toggle을 탭 하여 새로운 기능을 사용해봐라
Section 4
Use an Observable Object for Storage -> 저장을 위해 Observable Object를 사용
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.
-> 유저가 특정 랜드마크에 즐겨찾기를 하는 것을 컨트롤 할 수 있도록 하기 위해선, landmark data를 observable object에 저장한다.
observable object는 SwiftUI 내의 저장소(=storage)에서 view에 binding 될 수 있는 데이터에 대한 custom object(=객체)이다. SwiftUI는 view에 영향을 줄 수 있는 observable object들의 변화를 감지하고, 변경된다면 그 뷰를 보여준다.
Step 1
In the project’s navigation pane, select ModelData.swift.
-> ModelData.swift 파일을 선택한다
Step 2
Declare a new model type that conforms to the ObservableObject protocol from the Combine framework.
SwiftUI subscribes to your observable object, and updates any views that need refreshing when the data changes.
-> Combine 프레임워크에 있는 ObservableObject 프로토콜을 채택하는 새로운 model 타입을 선언하라.
SwiftUI는 당신의 observable object를 구독하고 data가 변화할 때 refresh가 필요한 view들을 업데이트 한다.
*여기서의 구독은 계속 지켜보고 있다, 감지하다 라는 정도의 의미다.
Step 3
Move the landmarks array into the model.
-> landmarks 배열을 model로 옮겨라
An observable object needs to publish any changes to its data, so that its subscribers can pick up the change.
-> observable object는 subscriber(=구독자)가 변화를 선택할 수 있도록 data에 대한 어떠한 변화라도 publish(=게시)해야 한다.Step 4
Add the @Published attribute to the landmarks array.
-> landmarks 배열에 @Published 속성을 추가하라
Section 5
Adopt the Model Object in Your Views -> View에 Model 객체 채택
Now that you’ve created the ModelData object, you need to update your views to adopt it as the data store for your app.
-> 이제 ModelData 객체를 생성했다. 이제 view를 업데이트 하여 앱의 data store로 채택해야 한다.
Step 1
In LandmarkList.swift, add an @EnvironmentObject property declaration to the view, and an environmentObject(_:) modifier to the preview.
The modelData property gets its value automatically, as long as the environmentObject(_:) modifier has been applied to a parent.
-> LandmarkList.swift 파일에서 뷰에 @EnvironmentObject property를 추가하고 preview에 environmentObject(_:) modifier를 추가하라.
modelData property는 environmetObject(_:) modifier가 parent에 적용되어 있는 한 자동적으로 값을 가진다
Step 2
Use modelData.landmarks as the data when filtering landmarks.
-> modelData.landmarks를 landmarks를 filterling 할 때 데이터로 사용하라
Step 3
Update the LandmarkDetail view to work with the ModelData object in the environment.
-> environment의 ModelData 객체와 함께 작동하는 LandmarkDetail view를 업데이트 하라
Step 4
Update the LandmarkRow preview to work with the ModelData object.
-> ModelData object와 함께 작동하는 LandmarkRow preview를 업데이트 하라
Step 5
Update the ContentView preview to add the model object to the environment, which makes the object available to any subview.
A preview fails if any subview requires a model object in the environment, but the view you are previewing doesn’t have the environmentObject(_:) modifier.
-> ContentView preview를 업데이트 하여 모델 객체를 환경에 추가함으로서 모든 하위 뷰들이 해당 object를 사용할 수 있다. 환경에 모델 객체가 필요한 하위 뷰에 environmentObject(_:) modifier가 없으면 미리 보기가 실패합니다.
Next, you’ll update the app instance to put the model object in the environment when you run the app in the simulator or on a device.
-> 이제 시뮬레이터나 device에서 앱을 실행할 때, 환경에 있는 model 객체를 보낼 app 인스턴스를 업데이트 할 것이다.Step 6
Update the LandmarksApp to create a model instance and supply it to ContentView using the environmentObject(_:) modifier.
Use the @StateObject attribute to initialize a model object for a given property only once during the life time of the app. This is true when you use the attribute in an app instance, as shown here, as well as when you use it in a view.
-> model 인스턴스를 생성하고 이를 environmentObject(_:) modifier를 사용하는 ContentView에 적용시키기 위해 LandmarksApp을 업데이트해라. @StateObject 속성을 사용하여 app이 살아있는 동안 딱 한번 주어지는 property로서 model object를 초기화 한다. 앱 인스턴스에서 사용할 때와 view 에서 사용할 때 모두 해당된다.
*참고 : @StateObject 속성은 앱 수명 동안 한 번만 App 프로토콜을 채택한 구조체 내에서 초기화 시켜서 계속 사용한다.
Step 7
Switch back to LandmarkList.swift and turn on the live preview to verify that everything is working properly.
-> LandmarkList.swift 파일로 돌아가서 live preview를 키고 제대로 동작하는지 확인하라
Section 6
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.
-> 랜드마크 앱은 정렬된 랜드마크 뷰와 정렬되지 않은 뷰들을 변경할 수 있지만, 즐겨찾기 랜드만크 리스트는 아직도 hard-coded(=직접 데이터 입력)되어 있다. 유저가 즐겨찾기를 지우거나 추가하도록 하려면 favorite button을 랜드마크 detiail view에 추가하는 것이 필요하다
You’ll first create a reusable FavoriteButton.
-> 첫번째로 재사용 가능한(=reusable) FavoriteButton을 만들 것이다Step 1
Create a new view called FavoriteButton.swift.
-> FavoriteButton.swift라고 불리는 view를 하나 만들자
Step 2
Add an isSet binding that indicates the button’s current state, and provide a constant value for the preview.
Because you use a binding, changes made inside this view propagate back to the data source.
-> button의 현재 state를 표시하는 inSet binding을 추가하고 preview에 상수 값을 제공하라
binding을 사용하기 때문에, 이 뷰에서 변경한 내용이 다시 data 원본으로 전달된다
Step 3
Create a Button with an action that toggles the isSet state, and that changes its appearance based on the state.
-> isSet state를 toggle하는 액션을 가진 Button을 만들고 state에 따라 변경되도록 하라.
Step 4
Collect the general purpose CircleImage.swift, MapView.swift, and FavoriteButton.swift, into a Helpers group, and the landmark views into a Landmarks group.
-> CircleImage.swift, MapView.swift, FavoriteButton.swift를 Helper group으로 묶고 landmarks 뷰들을 Landmakrs 그룹으로 묶어라
Next, you’ll add the FavoriteButton to the detail view, binding the button’s isSet property to the isFavorite property of a given landmark.
-> 다음은 FavoriteButton을 detail view에 추가하고, 버튼의 isSet property를 landmark에 있는 isFavorite property과 연결할 것이다Step 5
Switch to LandmarkDetail.swift, and compute the index of the input landmark by comparing it with the model data.
To support this, you also need access to the environment’s model data.
-> LandmarkDetail.swift로 들어가서, model data에 있는 landmark와 들어온 landmark의 인덱스를 비교하여 index를 확인하라. 이를 위해서 enviroment's model data에 접근할 필요해야 한다.
Step 6
Embed the landmark’s name in an HStack with a new FavoriteButton; provide a binding to the isFavorite property with the dollar sign ($).
Use landmarkIndex with the modelData object to ensure that the button updates the isFavorite property of the landmark stored in your model object.
-> landmarks'name을 새로운 FavortieButton과 함께 HStack으로 묶어라. 이는 dollar sign($)과 함께 isFavorite property에 binding을 제공한다.
landmarkIndex를 model object에 저장된 landmark의 isFavorite 속성을 버튼을 업데이트하는 modelData object와 함께 사용하라
Step 7
Switch back to LandmarkList.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 access the same model object in the environment, the two views maintain consistency.
-> LandmarkList.swift 로 돌아가서 live preview를 켜라
list에서 detail로 이동 후 button을 누르면, list에 돌아가도 변화는 유지되어 있을 것이다. 양쪽 view들이 environent의 똑같은 model object에 접근하기 때문에 두 view들 모두 지속될 수 있다.
environmentObject(_:) modifier.
You apply this modifier so that views further down in the view hierarchy can read data objects passed down through the environment.
-> 이 modifier를 사용하면 view hierarchy 하위에 있는 뷰가 environmet로 부터 받은 data object들을 읽을 수 있다.
binding의 역할은 무엇인가요?
A binding controls the storage for a value, so you can pass data around to different views that need to read or write it.
-> binding은 value 저장소를 컨트롤 한다. 따라서 저장된 값을 읽거나 작성할 필요가 있는 다른 뷰들에게 데이터를 전달해줄 수 있다.
Use the @State property wrapper to mark a value as state, declare the property as private, and give it a default value.
-> @State property를 사용하여 value가 state를 감지하도록 하고, property를 private으로 선언하고, default value를 주어라
*읽어주셔서 감사합니다! 피드백은 정신건강에 많은 도움이 됩니다*
참고 링크 :
developer.apple.com/tutorials/swiftui/handling-user-input
Apple Developer Documentation
developer.apple.com
'SwiftUI > Document 예제' 카테고리의 다른 글
Building Lists and Navigation 예제 정리 (0) 2020.12.18 Introducing Swift UI 예제 정리 (0) 2020.12.16