Project10의 네 번째 포스팅입니다.
디테일 화면 구현하기
전 포스팅에서 내비게이션 링크까지 연결을 했습니다. 이제 실제로 상세 화면을 구현할 차례입니다.
[SwiftUI View
] 템플릿을 통해서 ProductDetailView.swift
라고 이름 짓고 다음 코드를 채워 넣어주겠습니다. 우선 아래 코드를 넣어주고 하나하나씩 구현하도록 할게요.
// ProductDetailView.swift import SwiftUI struct ProductDetailView: View { // 정보를 전달받기 위한 프로퍼티 선언 let product: Product var body: some View { VStack(spacing: 0) { productImage //영화의 이미지가 올 부분 orderView // 영화 정보를 출력하고 그 영화를 구매 하기 위한 뷰 } .edgesIgnoringSafeArea(.top) } }
edgesIgnoringSafeArea(.top)
수식어를 넣어서 윗부분까지 채워주도록 했고, 이제 productImage
의 뷰를 표현해보겠습니다.
productImage
영화의 이미지를 나타내는 부분
// ProductDetailView.swift var body: some View { VStack { productImage //영화의 이미지가 올 부분 orderView // 영화 정보를 출력하고 그 영화를 구매 하기 위한 뷰 } .edgesIgnoringSafeArea(.top) } } private extension ProductDetailView { var productImage: some View { Image(self.product.ImageName) .resizable() .scaledToFill() } }
orderView
orderView
에는 여러 코드가 들어가기 때문에 역할에 따라 여러 개의 뷰로 나누어 주도록 하겠습니다.
// ProductDetailView.swift private extension ProductDetailView { var productImage: some View { Image(self.product.ImageName) .resizable() .scaledToFill() } } var orderView: some View { GeometryReader { VStack(alignment: .leading) { self.productDescripsion //영화제목과 즐겨찾기 버튼 이미지 추가 Spacer() // 아래 코드는 잠시 주석 처리 // self.priceInfo // 가격 // self.orderButton // 주문버튼 } .frame(height: $0.size.height + 10) .padding(30) .background(Color.white) .cornerRadius(20) .shadow(color: Color.black.opacity(0.4), radius: 10, x: 0, y: -5) } }
ProductDescription
자 이제 productDescripsion
부분을 구현해주겠습니다. 이곳에는 캐릭터명, 즐겨찾기 버튼, 설명 등을 표시하는 뷰가 됩니다.
var productDescripsion: some View { VStack(alignment: .leading, spacing: 10) { HStack { Text(product.name) .font(.largeTitle) .foregroundColor(.black) Spacer() Image(systemName: "heart") // 즐겨찾기 버튼 .font(.title2) .foregroundColor(.lightRed) .frame(width: 32, height: 32) } Text(product.description) //상품 설명 .foregroundColor(.gray) } } }
한 가지 함수를 작성해줘야 하는데 한 문장으로 길게 된 텍스트를 화면에 적절하게 나태 나게 위해 두 줄로 나누어 주는 기능을 수행하도록 하겠습니다. 이 코드는 아래 게시글에서 자세하게 확인할 수 있습니다. 이번 포스팅에서는 설명하지 않겠습니다. :)
아래와 같이 함수를 작성해주고 Text
부분을 Text(splitText(product.description))
으로 수정해줍니다.
// ProductDetailView.swift import SwiftUI struct ProductDetailView: View { ... } private extension ProductDetailView { // MARK : View var productImage: some View { ...} var orderView: some View { ... } var productDescripsion: some View { VStack(alignment: .leading, spacing: 10) { HStack { ... } Text(splitText(product.description)) .foregroundColor(.gray) } } /* 한 문장으로 길게 구성된 텍스트를 화면에 적장하게 나타내기위해 두 줄로 나누워 주는 기능 */ func splitText(_ text: String) -> String { guard !text.isEmpty else { return text } let centerIdx = text.index(text.startIndex, offsetBy: text.count / 2) let centerSpaceIdx = text[..<centerIdx].lastIndex(of: " ") ?? text[centerIdx...].firstIndex(of: " ") ?? text.index(before: text.endIndex) let afterSpaceIdx = text.index(after: centerSpaceIdx) let lhsString = text[..<afterSpaceIdx].trimmingCharacters(in: .whitespaces) let rhsString = text[afterSpaceIdx...].trimmingCharacters(in: .whitespaces) return String(lhsString + "\n" + rhsString) } }
현재까지의 코드

PriceInfo
이제 orderView
에서 주석 처리한 priceInfo
부분을 활성화시킨 뒤, 아래와 같이 코드를 작성해주겠습니다. product
의 price
부분은 Int
정수 이기 때문에 Text("product.price")
로 바로 가져오게 되면 컴파일 오류가 생깁니다. 그렇기 때문에 반드시 문자열 보간을 사용하여 호출해야 합니다.
// ProductDetailView.swift var priceInfo: some View { HStack { Text("₩") + Text("\(product.price)").font(.title).fontWeight(.medium) Spacer() } .foregroundColor(.black) }
orderButton
주문을 위한 버튼을 만들어 보겠습니다. 마찬가지로 orderView
에서 주석 처리한 부분을 활성화시켜줍니다.
// ProductDetailView.swift var orderButton: some View { // 주문하는 버튼 Button(action: { // some action }) { Capsule() .fill(Color.lightRed) // 너비는 주어진 공간을 최대로 사용, 높이는 최소30, 최대 60으로 지정 .frame(maxWidth: .infinity, minHeight: 30, maxHeight: 60) .overlay(Text("주문하기") .font(.system(size: 23)).fontWeight(.medium) .foregroundColor(.white)) .padding(.vertical, 10) } }
PriewView / Home View 코드 수정
프리뷰와 Home
뷰 에도 아래와 같이 코드를 추가해줘야 정상적으로 실행이 됩니다.
// ProductDetailView.swift struct ProductDetailView_Previews: PreviewProvider { static var previews: some View { ProductDetailView(product: ProductList[0]) } }
import SwiftUI struct Home: View { let movie: Movie var body: some View { NavigationView { List(movie.products) { product in NavigationLink( destination: ProductDetailView(product: product) //내비게이션 back 버튼 숨김 .navigationBarBackButtonHidden(true)) { ProductRow(product: product) } } .navigationBarTitle("지브리 스튜디오") } } }

앱을 실행해보면 크기가 제각각 다른 것을 확인할 수 있죠? 이유는 이미지 크기가 전부 동일하지 않기 때문입니다. 이것을 해결하려면 productImage
프로퍼티에서 이미지를 GeometryReader
로 감싸줘야 합니다.
productImage
// ProductDetailView.swift var productImage: some View { GeometryReader{ geo in Image(self.product.ImageName) .resizable() .scaledToFill() // 이미지 중앙을 기준으로 양옆을 자름 .frame(width: geo.size.width, height: geo.size.height) .clipped() } }
이제 모든 뷰가 동일한 크기로 구현되었고, 위 코드에서 추가한 frame
부분을 한번 비교해보도록 하겠습니다.

읽어주셔서 감사합니다🤟
[프로젝트/간단] - SwiftUI Project10 : 영화 캐릭터 정보 앱 #1
'PROJECT > Simple' 카테고리의 다른 글
SwiftUI Project11 : Shape를 활용하여 뷰 꾸미기 (3) | 2021.04.24 |
---|---|
SwiftUI Project10 : 영화 캐릭터 정보 앱 #3 - JSON (0) | 2021.04.19 |
SwiftUI Project10 : 영화 캐릭터 정보 앱 #2 - 모델생성 (0) | 2021.04.17 |
SwiftUI Project10 : 영화 캐릭터 정보 앱 #1 (0) | 2021.04.16 |
SwiftUI Project9 : CustomTabView (geometryReader) (0) | 2021.03.26 |