[SwiftUI] SwiftUI 기초 -공식 튜토리얼(3)

2023. 5. 4. 01:53SWIFT/SwiftUI

728x90
반응형

🧸 이전 글

 

[SwiftUI] SwiftUI 기초 -공식 튜토리얼(2)

🧸 이전 글 [SwiftUI] SwiftUI 기초 -공식 튜토리얼(1) 🧸 시작 SwiftUI 로 앱 만들기 파일 만들기 Interface -> SwiftUI 로 변경하고 Next 🧸 Inspector 로 수정하기 command + 클릭해서 Show SwiftUI Inspector... 를 클릭

nlestory.tistory.com

 

 

🧸 시작

✔️ 사용자 입력 처리

사용자는 자신이 좋아하는 장소에 플래그를 지정하고 목록을 필터링하여 자신이 좋아하는 장소만 표시할 수 있다. 이 기능을 만드려면 사용자가 자신의 즐겨찾기만 집중할 수 있도록 목록에 스위치를 추가한 다음 사용자가 랜드마크를 즐겨찾기로 표시하기 위해 탭하는 변 모양의 버튼을 추가하는 작업을 처리한다.

 

 

🧸 사용자가 좋아하는 랜드마크 표시

사용자가 즐겨찾기를 한 눈에 볼 수 있도록 목록을 개선한다. 목록에 별표를 추가한다.

 

랜드마크 모델에 즐겨찾기 속성을 추가한다.

struct Landmark: Hashable, Codable, Identifiable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
    var isFavorite: Bool
    
    ...
}

 

LandmarkRow 에서 별표 이미지를 생성한다.

struct LandmarkRow: View {
    //랜드마크 정보 프로퍼티
    var landmark: Landmark
    
    var body: some View {
        HStack {
            landmark.image
                .resizable()
                .frame(width: 50, height: 50)
            Text("\(landmark.name)")
            
            Spacer()
            
            if landmark.isFavorite {
                Image(systemName: "star.fill")
                    .foregroundColor(.yellow)
            }
        }
    }
}

 

 

 

🧸 목록 뷰 필터링

모든 랜드마크 또는 사용자의 즐겨찾기만 표시하도록 목록 보기를 사용자 정의할 수 있다. 이렇게 하려면 리스트를 약간 수정해야 한다.

//즐겨찾기만 보여줄것 인지를 확인하는 프로퍼티
@State
private var showFavoritesOnly: Bool = true

//랜드마크 필터링
var filteredLandmarks: [Landmark] {
    landmarks.filter { landmark in
        (!showFavoritesOnly || landmark.isFavorite)
    }
}

랜드마크배열을 필터링하여 showFavoritesOnly 값이 false일 경우나 landmark.isFavorite 값이 true 인 값을 추가한다.

var body: some View {
    NavigationView {
        //필터링된 배열로 변경
        List(filteredLandmarks) { landmark in
            NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                LandmarkRow(landmark: landmark)
            }
        }
        .navigationTitle("Landmarks")
    }
}

그리고 리스트에 출력하는 배열을 필터링된 배열로 변경한다.

필터링배열 적용 전 필터링배열 적용 후

 

 

 

🧸 상태를 전환하는 컨트롤 추가

목록의 필터에 대한 사용자 제어를 제공하려면 showFavoritesOnly의 값을 변경할 수 있는 제어를 추가해야 한다. 토글 컨트롤에 바인딩을 전달하여 작업을 수행한다.

바인딩은 변경가능한 상태에 대한 참조 역할을 한다. 사용자가 끄기에서 켜기로 토글을 변경하고 다시 끄면 컨트롤이 바인딩을 사용하여 그에 따라 보이의 상태를 업데이트한다.

 

showFavoritesOnly 의 초기값을 false로 변경한다.

//즐겨찾기만 보여줄것 인지를 확인하는 프로퍼티
@State
private var showFavoritesOnly: Bool = true

 

그리고 리스트 안에 토글을 추가한다.

기존의 코드였던 리스트안에 토글을 추가하게 되면 모든 행위에 토글이 추가되게 된다.

List(filteredLandmarks) { landmark in
    Toggle(isOn: $showFavoritesOnly) {
        Text("즐겨찾기만 보기")
    }

    NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
        LandmarkRow(landmark: landmark)
    }
}

즐겨찾기만 보기 토글은 제일 위에 하나만 있어야하므로 반복문을 사용하여 변경해준다.

List {
    //토글 추가
    Toggle(isOn: $showFavoritesOnly) {
        Text("즐겨찾기만 보기")
    }
    //필터링된 배열로 변경
    ForEach(filteredLandmarks) { landmark in
        NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
            LandmarkRow(landmark: landmark)
        }
    }
}

필터링된 배열을 보면 showFavoritesOnly의 값이 false 일 때이거나 landmark.isFavorite 의 값이 true 일 경우의 배열인데

showFavoritesOnly의 값이 false이면 모든 랜드마크의 데이터가 들어가고 true 값이 되면 landmark.isFavorite의 값이 true인 것만 보여주게 된다.

토글 클릭 전 토글 클릭 후

 

 

 

🧸 저장소에 Observable 객체 사용

사용자가 어떤 특정 랜드마크가 즐겨찾기인지 제어할 수 있도록 준비하려면 먼저 랜드마크 데이터를 관찰 가능한 개체에 저장한다.

 

ModelData 파일에서

import Combine

final class ModelData: ObservableObject {
    //랜드마크 배열
    @Published
    var landmarks: [Landmark] = load("landmarkData.json")
}

먼저 Combine을 import 해주고 클래스를 생성하여 @Published 를 선언해준다.

감시대상 클래스에는 @ObservedObject를 선언하고 변경됨을 알릴 변수는에는 @Published를 선언한다.

이 부분은 Combine을 다시 공부해야겠다.

 

 

 

🧸 뷰에서 모델 개체 채택

이제 모델 데이터 개체가 만들어졌으니 뷰를 업데이트하여 앱의 데이터 저장소로 채택해야 한다.

 

LandmarkList 파일 수정

struct LandmarkList: View {
    @EnvironmentObject
    var modelData: ModelData
    
    ...
    
    var filteredLandmarks: [Landmark] {
        modelData.landmarks.filter { landmark in
            (!showFavoritesOnly || landmark.isFavorite)
        }
    }
    
    ...
}

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
            .environmentObject(ModelData())
    }
}

 

LandmarkDetail 파일 수정

struct LandmarkDetail_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: ModelData().landmarks[0])
    }
}

 

LandmarkRow 파일 수정

struct LandmarkRow_Previews: PreviewProvider {
    static var landmarks = ModelData().landmarks
    
    static var previews: some View {
    
    ...
    
	}
}

 

ContentView 파일 수정

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(ModelData())
    }
}

 

SwiftUITestApp 파일 수정

@main
struct SwiftUITestApp: App {
    @StateObject
    private var modelData = ModelData()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(modelData)
        }
    }
}

데이터 수정 후 제대로 작동하는 지 확인 ~

 

 

🧸 각 랜드마크에 대한 즐겨찾기 버튼 만들기

필터링된 랜드마크 보기와 필터링되지 않은 랜드마크 보기 간의 전환은 할 수 있지만 즐겨찾는 랜드마크 목록은 여전히 하드코딩되어있다.

사용자가 즐겨찾기를 추가 및 제거할 수 있도록 하려면 랜드마크 상세보기에 즐겨찾기 버튼을 추가해야 한다.

 

SwiftUI 파일(파일명 : FavoriteButton) 을 생성한다.

struct FavoriteButton: View {
    //데이터 연동
    @Binding
    var isSet: Bool
    
    var body: some View {
        Button {
            //토글은 true->false, false->true 로 변경해준다.
            isSet.toggle()
        } label: {
            //isSet==true 면 노란색의 채워진 별, false 면 회색의 비워진 별
            Label("Toggle Favorite", systemImage: isSet ? "star.fill" : "star")
                .labelStyle(.iconOnly)
                .foregroundColor(isSet ? .yellow : .gray)
        }
    }
}

struct FavoriteButton_Previews: PreviewProvider {
    static var previews: some View {
        //초기값을 true로 지정
        FavoriteButton(isSet: .constant(true))
    }
}

isSet 상태를 토글하고 상태에 따라 모양과 색상을 변경하는 버튼을 만든다.

 

LandmarkDetail 파일 수정

struct LandmarkDetail: View {
    @EnvironmentObject
    var modelData: ModelData
    
    var landmark: Landmark
    
    var landmarkIndex: Int {
        modelData.landmarks.firstIndex(where: {
            $0.id == landmark.id
        })!
    }

    ...
}

struct LandmarkDetail_Previews: PreviewProvider {
    static var modelData = ModelData()
    
    static var previews: some View {
        LandmarkDetail(landmark: modelData.landmarks[0])
            .environmentObject(modelData)
    }
}

랜드마크의 인덱스를 구한다. 

 

그리고 랜드마크이름 옆에 즐겨찾기 버튼을 추가한다.

HStack {
    Text(landmark.name)
        .font(.title)
    FavoriteButton(isSet: $modelData.landmarks[landmarkIndex].isFavorite)
}

위에서 구한 인덱스를 가지고 데이터의 즐겨찾기 여부를 확인한다. 

즐겨찾기여부를 isSet의 속성에 전달한다.

 

 

✔️  완성

즐겨찾기 전 즐겨찾기 클릭 즐겨찾기 후

 

 

 

 

 

 

 

 

 

 

[예제 소스코드 깃허브 링크]

https://github.com/HANLeeeee/SwiftUITest/tree/main/SwiftUITest3

 

GitHub - HANLeeeee/SwiftUITest

Contribute to HANLeeeee/SwiftUITest development by creating an account on GitHub.

github.com

[참고자료]

Apple_Developer_Tutorials_SwiftUI(3)

 

Handling User Input | Apple Developer Documentation

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-sh

developer.apple.com

 

 

 

 

728x90
반응형

'SWIFT > SwiftUI' 카테고리의 다른 글

[SwiftUI] SwiftUI 기초 -공식 튜토리얼(2)  (0) 2023.05.04
[SwiftUI] SwiftUI 기초 -공식 튜토리얼(1)  (1) 2023.05.03