[SwiftUI] Composing Complex Interfaces

์›๋ฌธ: ํŠœํ† ๋ฆฌ์–ผ ํŽ˜์ด์ง€

Composing Complex Interfaces

์ด๋ฒˆ ์žฅ์—์„œ๋Š” ์—ฌ๋Ÿฌ ๋ทฐ๋“ค์„ ํ•ฉ์นœ ํ•˜๋‚˜์˜ ํ™”๋ฉด์„ ๋งŒ๋“ค์–ด๋ณธ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€๋„ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ทฐ๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ์ด๋ฒˆ ์žฅ์—์„œ๋Š” ์Šคํฌ๋กค๋ทฐ๋ฅผ ํ•ฉ์น˜๋Š” ๊ฒƒ..! storyboard๋กœ ์น˜๋ฉด tableView ์•ˆ์— collectionView๊ฐ€ ์žˆ๋Š” ๋Š๋‚Œ์ด๋ž„๊นŒ.

Add a Category View

์•ฑ์„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ ๊ฐ€์žฅ ๋จผ์ € ๋ณผ ํ™”๋ฉด์ธ Home ํ™”๋ฉด์„ ์ œ์ž‘ํ•œ๋‹ค.

  1. Home ํ™”๋ฉด์ด ๋  CategoryHome SwiftUI View ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค.
  2. body ๋‚ด์— NavigationView๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  title์€ โ€œFeaturedโ€๋กœ ์ง€์ •ํ•œ๋‹ค.

Create a Category List

์‚ฌ์‹ค ์šฐ๋ฆฌ๊ฐ€ ํŠœํ† ๋ฆฌ์–ผ ์ฒ˜์Œ์— ๋ฐ›์•˜๋˜ landmarkData.json ํŒŒ์ผ ์•ˆ์—๋Š” category ์ •๋ณด๋„ ํ•จ๊ป˜ ์žˆ์—ˆ๋Š”๋ฐ, ์•„์ง ๊ทธ๊ฑด ์‚ฌ์šฉ์„ ์•ˆํ•ด์„œ Landmark ๊ตฌ์กฐ์ฒด์— ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•˜์—ˆ๋‹ค.

  1. Landmark ์ฝ”๋“œ ์ˆ˜์ •

์ด๋ฒˆ ์„น์…˜์—์„œ category๋ฅผ ์ถ”๊ฐ€ํ•ด ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๋„๋ก Landmark ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.

var category: Category
    enum Category: String, CaseIterable, Codable {
        case lakes = "Lakes"
        case rivers = "Rivers"
        case mountains = "Mountains"
    }

category์—๋Š” lakes, revers, mountains ์„ธ ์ข…๋ฅ˜๊ฐ€ ์žˆ๊ณ  json ํŒŒ์‹ฑ์„ ์œ„ํ•ด ์—ญ์‹œ๋‚˜ Codable์„ ์ฑ„ํƒํ–ˆ๋‹ค. ์ถ”ํ›„ ํ™”๋ฉด์— ์นดํ…Œ๊ณ ๋ฆฌ ๋ช…์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด String enum์œผ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค.

  1. categories ํ”„๋กœํผํ‹ฐ ์ƒ์„ฑ ModelData์—์„œ๋Š” category ๋ณ„๋กœ landmark๋ฅผ ๋ถ„๋ฅ˜ํ•  ์ˆ˜ ์žˆ๋„๋ก [String: [Landmark]] ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

Dictionary์—์„œ grouping์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ชฐ๋ž๋Š”๋ฐ, ์ด๊ฒƒ๋„ ๋ณ„๋„ ํฌ์ŠคํŒ…์œผ๋กœ ๋‚จ๊ฒจ๋‘ฌ์•ผ๊ฒ ๋‹ค.

  1. CategoryHome์—์„œ categories ์ ‘๊ทผํ•ด ๋…ธ์ถœํ•˜๋„๋ก ์ˆ˜์ • ์ด์ œ ์•„๊นŒ ๋งŒ๋“ค์–ด๋‘” CategoryHome์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค.

modeldata์— ์žˆ๋Š” category์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด @EnvironmentObject var modelData: ModelData๋ฅผ ์„ ์–ธํ•œ๋‹ค. preview์—๋„ .environmentObject๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

NavigationView ๋‚ด๋ถ€์— List๋ฅผ ์ถ”๊ฐ€ํ•ด category์˜ key๊ฐ’์„ ๋ชฉ๋ก์œผ๋กœ ๋…ธ์ถœ์‹œํ‚จ๋‹ค.

image

Create a Category Row

์šฐ๋ฆฌ๊ฐ€ CategoryHome์—์„œ ์นดํ…Œ๊ณ ๋ฆฌ ์ด๋ฆ„๋งŒ ๋…ธ์ถœ์‹œํ‚จ๋‹ค๋ฉด ์ด๊ฑด Complex ํ•˜๋‹ค๊ณ  ํ•˜๊ธฐ์—” ์กฐ๊ธˆ ๋ถ€์กฑํ•˜๋‹ค. ๊ฐ ์นดํ…Œ๊ณ ๋ฆฌ์— ์†ํ•œ ๋žœ๋“œ๋งˆํฌ๋ฅผ ์Šคํฌ๋กค๋ทฐ๋กœ ๋…ธ์ถœ์‹œํ‚ค๋„๋ก ํ•ด๋ณด์ž.

์š”๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” Category Row.

Row์— ๋‚˜ํƒ€๋‚˜์•ผํ•˜๋Š” ๊ฒƒ์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด, ์นดํ…Œ๊ณ ๋ฆฌ ๋ช…๊ณผ ๋žœ๋“œ๋งˆํฌ์˜ ์ด๋ฏธ์ง€, ์ด๋ฆ„ ์ •๋„๊ฐ€ ์žˆ๋‹ค. ๋žœ๋“œ๋งˆํฌ์˜ ์ด๋ฏธ์ง€์™€ ์ด๋ฆ„์€ VStack์œผ๋กœ, ์ด VStack๊ณผ ์นดํ…Œ๊ณ ๋ฆฌ ๋ช…์„ ๋‹ค์‹œ VStack์œผ๋กœ ๋ฌถ์–ด์•ผ ํ•  ๊ฒƒ๋งŒ ๊ฐ™๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ขŒ์šฐ๋กœ ์Šคํฌ๋กค์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ๋žœ๋“œ๋งˆํฌ์˜ ์ด๋ฏธ์ง€์™€ ์ด๋ฆ„์ด ๋ฌถ์ธ VStack์„ ScrollView๋กœ ๋ฌถ์–ด๋ณด์ž.

  1. CategoryItem ๋งŒ๋“ค๊ธฐ

๋žœ๋“œ๋งˆํฌ์˜ ์ด๋ฏธ์ง€์™€ ์ด๋ฆ„์„ ๋ฌถ์–ด์„œ ๋ณด์—ฌ์ฃผ๋Š” CategoryItem์„ ๋งŒ๋“ค์–ด๋ณธ๋‹ค.

image

Item์„ ์ƒ์„ฑํ•  ๋•Œ ํ•„์š”ํ•œ landmark๋ฅผ ํ”„๋กœํผํ‹ฐ๋กœ ์„ ์–ธํ•˜๊ณ  VStack ๋‚ด๋ถ€์— ๋žœ๋“œ๋งˆํฌ์˜ ์ด๋ฏธ์ง€์™€ ์ด๋ฆ„์„ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค. ์–‘ ์˜† ๊ฐ„๊ฒฉ์„ ์œ„ํ•ด padding ๊ฐ’๋„ ์ ์šฉํ•œ๋‹ค.

  1. CategoryRow ๋งŒ๋“ค๊ธฐ

image

ScrollView ๋‚ด๋ถ€์—์„œ RowItem์„ ๊ฐ€๋กœ๋กœ ๋ฌถ์–ด์„œ ๋ณด์—ฌ์ฃผ๋„๋ก HStack์„ ์‚ฌ์šฉํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ScrollView์™€ ์นดํ…Œ๊ณ ๋ฆฌ ๋ช…์„ VStack์œผ๋กœ ๋ฌถ์–ด์ฃผ์—ˆ๋‹ค.

Complete the Category View

์ด์ œ ๋‹ค์‹œ CategoryHome์œผ๋กœ ๋Œ์•„๊ฐ€์„œ ๋ฉ”์ธํ™”๋ฉด ์ž‘์—…์„ ๋งˆ๋ฌด๋ฆฌํ•ด๋ณด์ž.

ํ˜„์žฌ๋Š” List ๋‚ด๋ถ€์— ์นดํ…Œ๊ณ ๋ฆฌ ๋ช…๋งŒ ๋…ธ์ถœ๋˜๊ณ  ์žˆ์ง€๋งŒ ์ด ๋ถ€๋ถ„์„ ์•„๊นŒ ๋งŒ๋“  CategoryRow๋ฅผ ๋…ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •ํ•œ๋‹ค. ๊ธฐ์กด์— Text๋กœ๋งŒ ์ถœ๋ ฅ๋˜๋˜ ๋ถ€๋ถ„์„ CategoryRow(categoryName: key, items: modelData.categories[key]!)๋กœ ๋ณ€๊ฒฝํ•˜์ž. ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”๋€๋‹ค.

image

๊ฐ€์žฅ ์œ„์—๋Š” featured ๋žœ๋“œ๋งˆํฌ ์ด๋ฏธ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ  ์‹ถ๋‹ค..! List ์œ„์— ์ด๋ฏธ์ง€๊ฐ€ ๋“ค์–ด๊ฐ€์•ผ ํ•˜๋Š”๋ฐ ๋†€๋ž๊ฒŒ๋„ json ํŒŒ์ผ์— isFeatured ๊ฐ’์ด ์žˆ๋„ค..?

์šฐ์„  Landmark ๊ตฌ์กฐ์ฒด์— ๊ฐ’์„ ์ถ”๊ฐ€ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ModelData์—์„œ ์šฐ๋ฆฌ๊ฐ€ ํ™”๋ฉด ๋…ธ์ถœ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก features: [Landmark] ๋ฐฐ์—ด์„ ๋งŒ๋“ ๋‹ค.

var features: [Landmark] {
     landmarks.filter { $0.isFeatured }
}

๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ CategoryHome์œผ๋กœ ๋Œ์•„๊ฐ€์„œ List์˜ ๊ฐ€์žฅ ์œ„์— featured์˜ ์ฒซ๋ฒˆ์งธ ์ด๋ฏธ์ง€๊ฐ€ ๋‚˜์˜ค๋„๋ก ์ˆ˜์ •ํ•˜๋ฉดโ€ฆ!

image

์š”๋ ‡๊ฒŒ ์›ํ•˜๋˜ ํ™”๋ฉด์ด ์™„์„ฑ๋œ๋‹ค ๐Ÿ˜Ž

.listRowInsets(EdgeInsets())๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด List์˜ Row๋“ค์ด ๊ฝ‰ ์ฐจ๊ฒŒ ๋‚˜์˜จ๋‹ค. (edge inset 0)

Add Navigation Between Sections

Home ํ™”๋ฉด๋„ ์™„์„ฑ๋˜์—ˆ์œผ๋‹ˆ ์ด์ œ Home๊ณผ Detail ํ™”๋ฉด์„ ์—ฐ๊ฒฐํ•ด ํ™”๋ฉด ์ด๋™์„ ํ•ด๋ณด์ž.

  1. CategoryRow์— navigation link ์ถ”๊ฐ€ image ๊ฐ CategoryItem์„ ๋ˆ„๋ฅด๋ฉด ์ด๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก NavigationLink๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

  2. tab ์„ ์ƒ์„ฑํ•ด ๋ฆฌ์ŠคํŠธ ํ™”๋ฉด๊ณผ ๋ฉ”์ธํ™”๋ฉด์„ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •

@State private var selection: Tab = .featured

enum Tab {
     case featured
     case list
}

ContentView์— ํƒญ์ด๋™์— ํ•„์š”ํ•œ ๊ฐ’์ธ Tab enum์„ ์ถ”๊ฐ€ํ•œ๋‹ค. ๊ธฐ๋ณธ ๊ฐ’์€ .featured๋กœ ์„ค์ •ํ•œ๋‹ค.

ํ˜„์žฌ ContentView์—๋Š” List๋ฅผ ๋…ธ์ถœํ•˜๋„๋ก body์— ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ TabView ๋ฅผ ์ถ”๊ฐ€ํ•ด ํƒญ์„ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •ํ•˜์ž.

TabView(selection: $selection) {
         CategoryHome()
             .tag(Tab.featured)

         LandmarkList()
             .tag(Tab.list)
}

์—ฌ๊ธฐ๊นŒ์ง€๋งŒ ํ•˜๋ฉด ์•„๋ž˜ ํƒญ๋ทฐ๋งŒ ์ƒ์„ฑ์ด ๋˜๊ณ  ์ด๋™์€ ์•ˆ๋˜๋Š” ํ™”๋ฉด์ด ๋œ๋‹ค. tabItem์„ ์„ค์ •ํ•ด ํƒญ๋ทฐ์— ์•„์ด์ฝ˜์ด ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

As Is Te Be
image image

ํ”„๋ฆฌ๋ทฐ๋ฅผ ์‹คํ–‰์‹œ์ผœ ์ž‘๋™ํ•ด๋ณด์ž. ๋””ํ…Œ์ผ๋ทฐ๋กœ, ํƒญ์ด๋™๋„ ์ž˜๋˜๋Š”์ง€ ๋ชจ๋‘ ํ™•์ธ..!

๋งˆ๋ฌด๋ฆฌ

SwiftUI๊ฐ€ ์ฒ˜์Œ ๋„์ž…๋˜๊ณ  ๋‚˜์„œ๋Š” ์ƒ์†Œํ•ด์„œ ๋ชป ์“ธ ๊ฒƒ ๊ฐ™์•˜๋Š”๋ฐ ์ด๋ ‡๊ฒŒ ํŠœํ† ๋ฆฌ์–ผ๋กœ ๋Œ๋ ค๋ณด๋‹ˆ๊นŒ ์ƒ๊ฐ๋ณด๋‹ค ๊ดœ์ฐฎ์€ ๊ฒƒ ๊ฐ™๋‹ค. ์•„์ง์€ ์ ์‘์ด ์•ˆ๋๊ธฐ๋„ ํ•˜๊ณ  ์ง€์›ํ•˜๋Š” ๋ฉ”์„œ๋“œ ๊ฐ™์€๊ฑธ ์ž์„ธํžˆ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์“ฐ๋Š”๋ฐ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ์ง€๋งŒ ์ด๊ฒƒ๋„ ์ ์‘ํ•˜๋ฉด ์ถฉ๋ถ„ํžˆโ€ฆ.? ์˜คํžˆ๋ ค ์ฝ”๋“œ๋กœ ์งœ์„œ ๋” ์ข‹์€ ๊ฒƒ ๊ฐ™๋‹ค. ๋ทฐ๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด ํŒŒ์ผ ๋ถ„๋ฆฌ๋ฅผ ์ž˜ ํ•ด์„œ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์„ ์žƒ์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด์ง€์ง€ ์•Š์„๊นŒ?

Leave a comment