포스트

BB Quotes (3)

Button에 Fetch 기능 적용하기

이제 비어있던 Button에 Fetch가 가능하도록 적용하자

1
2
3
4
5
Button {
Task {
    await vm.getData(for: show)
}
}

뭐 이건 많이해봤지만 await는 단독으로 사용 될수 없기에 Task가 반드시 필요하다.

Image

작동이 잘 되는걸 알 수 있다.

status 따라 작동을 나누기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
VStack {
Spacer(minLength: 60)

switch vm.status {
case .notStarted:
    EmptyView()
case .fetching:
    ProgressView()
case .success:
    Text("\"\(vm.quote.quote)\"")
        //생략
    
    ZStack(alignment: .bottom) {
        //생략
    }
    //생략
case .failed(let error):
    Text(error.localizedDescription)
}

Spacer()
//생략

}
.frame(width: geo.size.width, height: geo.size.height)

이렇게 구분을 해준다.

그러면 서버 통신에 상태에 따라 다르게 보여지게 된다.

Image

사용자는 이제 버튼을 눌렀을때 어떻게 진행이 되는지 간접적으로 알 수 있게 되었다.

그리고 위의 사진을 보면 알겠지만 로딩 중일때 버튼이 위로 올라가게 되는데 그걸 막기 위해 Vstack을 하나 더 씌워준다.

1
2
3
4
5
6
7
8
9
10
11
12
VStack {
VStack { // added
    Spacer(minLength: 60)
    switch vm.status {
    // 생략
    }
    // 생략
}
Button {
    // 생략
} 
}

아래 사진을 보면 Vstack을 추가해준 이유를 알 수 있다.

ImageImage

Image

직전것과 달리 버튼이 고정되면서 ui가 더 안정적으로 되었다.

QuoteView 재사용

이제 키워드가 “Breaking Bad” 인것에 대해서는 끝났으니, 이제는 “Better Call Saul”에 대해서 처리를 하자

크게 달라지는 건 없다.

Image

먼저 색을 만들어주고 이전에 BreakingBadgreen,yellow에서 button, shadow로 바꿔주었다.

그리고 이전글에서 배경화면에 적용했던것과 같은 방식으로 하면된다.

1
2
3
4
5
6
7
8
9
10
11
Button {
// 생략
} label: {
Text("Get Random Quote")
    .font(.title)
    .foregroundStyle(.white)
    .padding()
    .background(Color("\(show.replacingOccurrences(of: " ", with: ""))Button")) // changed
    .clipShape(.rect(cornerRadius: 7))
    .shadow(color: Color("\(show.replacingOccurrences(of: " ", with: ""))Button"), radius: 2) // changed
}

이렇게 적용을 해주면된다.

Image

이젠 각 tab에 따라 다르게 되는걸 알 수 있다.

Character View 만들기

Image

이젠 이렇게 fetch 를 했을때 나온 결과에 대해서 이미지를 클릭했을때 Character 정보가 나오는 Character View를 만들어 보도록 한다.

뭐 딱히 언급할만한것은 없다.

Image

현재 이렇게 디자인이 된 상태.

QuoteView에서 화면 띄우기

이것도 크게 어렵지 않다.

이전에 했던것 그대로 하면된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
struct QuoteView: View {
// 생략
@State var showCharacterInfo = false

var body: some View {
    GeometryReader { geo in
        ZStack {
            // 생략
            
            VStack {
                VStack {
                    Spacer(minLength: 60)
                    
                    switch vm.status {
                    case .notStarted:
                        EmptyView()
                    case .fetching:
                        ProgressView()
                    case .success:
                        // 생략
                        }
                        .frame(width: geo.size.width / 1.1, height: geo.size.height / 1.8)
                        .clipShape(.rect(cornerRadius: 50))
                        .onTapGesture { // new
                            showCharacterInfo.toggle()
                        }
                        
                    case .failed(let error):
                        Text(error.localizedDescription)
                    }
                    
                    Spacer()
                }
                // 생략
                
            }
            .frame(width: geo.size.width, height: geo.size.height)
        }
        .frame(width: geo.size.width, height: geo.size.height)
    }
    .ignoresSafeArea()
    .sheet(isPresented: $showCharacterInfo) { // new
        CharacterView(character: vm.character, show: show)
    }
}

sheet의 활성 여부를 물어볼 변수 하나와, ontapgesture, sheet modifier를 사용하여 만들어 주면 된다.

Image

실행하면 위와 같다.

DisclosureGroup 사용하기

이건 처음보는 내용이기에 Docs를 먼저 소개.

CharacterView 최하단에

1
2
3
4
5
6
7
8
9
10
11
VStack(alignment: .leading) {
    // 생략
    
    Divider()
    
    DisclosureGroup("Status (spoiler alert!):") {
        Text(character.status)
            .font(.title2)
    }
    
}

이렇게 DisclosureGroup을 만들어준다.

Image

이런식으로 Folding을 하여 내용을 보여주게 하는 기능이다.

보완

여기서 조금더 보완을 해서 json 데이터를 가지고 죽었을때의 디테일 그리고 살아있을때를 구분해서 나누도록 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
DisclosureGroup("Status (spoiler alert!):") {
    VStack(alignment: .leading) {
        Text(character.status)
            .font(.title2)
        
        if let death = character.death {
            AsyncImage(url: death.image) { image in
                image
                    .resizable()
                    .scaledToFill()
                    .clipShape(.rect(cornerRadius: 15))
            } placeholder: {
                ProgressView()
            }
            
            Text("How: \(death.details)")
                .padding(.bottom, 7)
            
            Text("Last words: \"\(death.lastWords)\"")
            
        }
    }
    .frame(maxWidth: .infinity, alignment: .leading)
}
.tint(.primary)

if를 통해 character.death에 값이 있다면 추가적인 정보를 보여지게끔한다.

Image

이렇게 더 자세한 정보가 나오게 된다.

Image

하지만 작품속 인물이 아직 시즌 중 살아있다면 이렇게 Alive로 표시되고 끝

앱 이름 설정하기

지금은

Image

이렇게 Display Name이 Blank라 프로젝트 명으로 앱 이름이 설정되는데, 여기에 “Say My Name”으로 해주면

  • before Image
  • after Image

이렇게 달라진걸 알 수 있다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.