SwiftUI - 간단한 List만들기(+HStack도 간단히 써보기)

YSYD·2021년 3월 7일
0

SwiftUI

목록 보기
1/3

SwiftUI 간단한 프로젝트 생성 및 튜토리얼 따라해보기

프로젝트 생성 시 Interface에서 SwiftUI를 선택한다.

ContentView 라는 Struct가 자동 생성되어있다.

여기서 정리할 내용

  • SwiftUI 에서 Viewe들은 struct이다. 필수는 아니지만 강력하게(!) 권장한다.
  • 모든 View는 자동생성된 ContentView처럼 View protocol을 따라야한다.
  • View protocol에는 body라는 computed property를 필요로하는데, 이는 view를 위한 실제 layout을 담는다.
  • body는 View를 return하는데 opaque return types(Opaque Types에 관한 내용은 문서 를 참고) 라고 불리는 Swift 기능이다. - 이건 하나의 특정한 view의 정렬이고 그걸 신경쓰지 않는다는 의미
  • content view안에 있는 Text는 SwiftUI 에서 text string들을 보여줄 때 사용한다. 바로 label로 쓸수도 있고 navigation bar나 버튼을 구성할 때에도 쓸수도 있다.
  • padding() method는 이 view 주변으로 공간을 주겠다는 의미이다.
  • SwiftUI에서는 이런식으로 modifier를 호출해서 text view가 어떻게 보여질지, 행동할지 변경할수 있다.

오른쪽 preview화면에서 resume버튼을 누르면 Hello, world! 텍스트가 잘보인다.

이제 body에 아래 코드를 넣어보자.

    var body: some View {
        NavigationView {
            List {
                Text("Hello World")
                Text("Hello World")
                Text("Hello World")
            }
            .navigationTitle("Menu")
        }
    }
  • SwiftUI에서는 navigation control을 위해NavigationView를 사용하는데 , UINavigationBar의 스타일과 view controller를 stack처럼 쌓는 UINavigationController의 방식을 합친 것이다.
  • navigationTitle() modifier를 써서 타이틀을 넣어줄 수있다.

https://www.hackingwithswift.com/samples/idine.zip 에서 받은 menu.json 파일을 프로젝트에 추가하고 메뉴들과 테이블 섹션의 정보를 담을 MenuSection과 MenuItem을 추가했다.

  • Bundle을 extension해서 decode할 메소드도 친히 넣어주었다.
extension Bundle {
    func decode<T: Decodable>(_ type: T.Type, from file: String) -> T {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }

        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }

        let decoder = JSONDecoder()

        guard let loaded = try? decoder.decode(T.self, from: data) else {
            fatalError("Failed to decode \(file) from bundle.")
        }

        return loaded
    }
}
  • 이제다시 contentView로 돌아와서 struct안에 menu를 선언해주었다.
 let menu = Bundle.main.decode([MenuSection].self, from: "menu.json")
  • section별로 아이템들을 보여주기위해 List 안쪽을 수정했다.

       var body: some View {
           NavigationView {
               List {
                   ForEach(menu) { section in
                       Text(section.name)
                       ForEach(section.items) { item in
                           Text(item.name)
                       }
                   }
               }
               .navigationTitle("Menu")
           }
       }
  • ForEach Block을 이용해서 menu에 있는 section 과 item들을 Text로 보여준다.

    이제 Section을 Section답게 수정해보자.

  • List안쪽에 Text대신 Section을 만들고 그 안에 아이템들을 보여주는 ForEach문을 넣어준다.

  • NavigationView 에 GroupedListStyle() 을 적용해 좀더 이쁘게 구성해 본다.

    var body: some View {
          NavigationView {
              List {
                  ForEach(menu) { section in
                      Section(header: Text(section.name)) {
                          ForEach(section.items) { item in
                              Text(item.name)
                          }
                      }
                  }
              }
              .navigationTitle("Menu")
          }.listStyle(GroupedListStyle())
      }

  • 이제 메뉴아이템 row를 업그레이드해보자.

  • ItemRow struct를 하나 만들고 View프로토콜을 따르게한다.

  • MenuItem에 preview 확인용도로 사용할 example을 만들어둔다.

struct ItemRow : View {
    let item: MenuItem
    
    var body: some View {
        Text(item.name)
    }
}
struct ItemRow_Previews: PreviewProvider {
    static var previews: some View {
        ItemRow(item: MenuItem.example)
    }
}
  • body에서 두가지 View가 리턴되는 경우 어떻게 처리될까?

       var body: some View {
           Image(item.thumbnailImage)
           Text(item.name)
       }
  • 위 처럼 Image와 Text가 있는 경우 SwiftUI는 첫번째 View만을 보여준다.(여기서는 Image만을 보여준다.)

  • View를 정렬해서 보여주고 싶다면 HStack 이나 VStack을 사용하면된다.

  • body안쪽을 아래처럼 수정해본다.

    var body: some View {
        HStack {
            Image(item.thumbnailImage)
                .clipShape(Circle())
                .overlay(Circle().stroke(Color.gray, lineWidth: 2))

            VStack(alignment: .leading) {
                Text(item.name)
                    .font(.headline)
                Text("$\(item.price)")
            }
        }
    }

  • 이제 다시 contentView로 돌아가서 ItemRow를 사용해 메뉴들을 보여주자.
    var body: some View {
        NavigationView {
            List {
                ForEach(menu) { section in
                    Section(header: Text(section.name)) {
                        ForEach(section.items) { item in
                            ItemRow(item: item)
                        }
                    }
                }
            }
            .navigationTitle("Menu")
        }.listStyle(GroupedListStyle())
    }

profile
YSYD

0개의 댓글