์์ํ๋ฉฐ
SwiftUI ๋ฅผ ์กธ์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๊ณ ๋์, ์ข ๋ ๊น์ด์๊ฒ, ๊ธฐ๋ณธ๊ธฐ๋ฅผ ํํํ ๊ณต๋ถํด์ผ๊ฒ ๋ค๋ ๊ฒฐ์ฌ๋ง ํ ์ง ์ด์ธ 5๊ฐ์์ด ์ง๋ฌ๊ณ , ๊ฐ์ธ์ฌ์ ํ์ฌ์ผ์ ํฉ์ธ๋ฆฌ๋ฉฐ ์ด๋ค๊ฐ ์ด์ ์ผ ์ ์ ์ฐจ๋ฆฌ๊ณ SwiftUI๋ฅผ ๋ค์ ๊ณต๋ถํ๊ณ ์ WWDC 2023 ์์ ์ค์ SwiftUI ์ ๋ํ ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
์ด๋ฒ์ SwiftUIํ์์ ์ฑ์ Focus(์ดํ ํฌ์ปค์ค, ์ด์ ์ด๋ผ๋ ์ข์ ํ๊ตญ์ด๊ฐ ์์ง๋ง API ์ด๋ฆ ์์ฒด๊ฐ ํฌ์ปค์ค์ด๊ธฐ ๋๋ฌธ์) ๊ฒฝํ์ ๋ง๋ค์ด์ค ์ ์๋ ๊ฐ๋ ฅํ ํด์ ๊ฐ์ง๊ณ ๋์์๋ค. ํฌ์ปค์ค ๋๋ฆฌ๋ธ ๊ฒฝํ์ด๋ผ๋๊ฒ ์ด๋ค๊ฑด์ง, ์ปค์คํ ๋ทฐ์ ํฌ์ปค์ค ์ธํฐ๋์ ๊ณผ ํค๋ณด๋ ์ธํ์ ๋ํด ์์๋ณด๊ณ ์ ํ๋ค.
์ด ๊ธ์ ์๋ WWDC2023 ์์์ ์ฐธ๊ณ ํ์ฌ ์ ๋ฆฌํ์์ต๋๋ค.
https://developer.apple.com/videos/play/wwdc2023/10162/
Focus API๊ฐ ๋ฌด์์ผ๊น?
ํฌ์ปค์ค๋ ํค๋ณด๋์ ํค๋ฅผ ๋๋ ์ ๋, ์ ํ TV ๋ฆฌ๋ชจ์ฝ์ ์ค์์ดํ ํ์ ๋, ์์น์์ ๋์งํธ ํฌ๋ผ์ด์ ์ผฐ์ ๋ ๋ฑ์ ์ด๋ป๊ฒ ๋ฐ์ํ ์ง๋ฅผ ๊ฒฐ์ ํ๋ ํด์ด๋ค. ์ด๋ฌํ ์ธํ ๋ฐฉ๋ฒ์ ๊ณตํต์ ์ผ๋ก ์ค์ํ ๋ํ ์ผ์ ๊ฐ์ง๊ณ ์๋๋ฐ, ์ธํ ์ฅ์น์์๋ ์จ์คํฌ๋ฆฐ์์ ์ด๋ค ์ปจํธ๋กค์ด ๋๋์ง์ ๋ํด ์ถฉ๋ถํ ์ ๋ณด๋ฅผ ์ ๊ณตํ์ง๋ ์๋๋ค๋ ์ ์ด๋ค.
๋ง์ฐ์ค๋ ํธ๋ํจ๋๋ฅผ ์ฌ์ฉํ ๋ ์จ ์คํฌ๋ฆฐ ์ปค์๋ ์ธํฐ๋์ ํ ํ๊ฒ์ ์ฐพ๊ธฐ ์ํด ํ๋ฉด ์ขํ์ ์ฐ๊ฒฐ๋๋ค. ํฌ์ปค์ค๋ ์ปค์ ์์ด๋ ์ง์ ์ ๋ ฅํ ์ ์๋ ์ถ๊ฐ์ ์ธ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ค. ๋ทฐ์ ํฌ์ปค์ค๊ฐ ์์ผ๋ฉด ์์คํ ์ด ์ ๋ ฅ์ ์๋ตํ๊ธฐ ์ํ ์์์ง์ ์ผ๋ก ์ก๊ฒ ๋๋ค.
ํฌ์ปค์ค๊ฐ ๋ณด์ฌ์ง๋ ํํ
ํฌ์ปค์ค๋ ๋จ์ํ ๋ํ ์ผ์ ๊ตฌํ ๋ฟ ์๋๋ผ ์ฑ์ ์ฌ์ฉํ๋ ์ฌ๋๋ค์๊ฒ๋ ์ค์ํ๊ธฐ ๋๋ฌธ์ ํฌ์ปค์ค๋ ๋ทฐ๋ ํน๋ณํ๊ฒ ๊ฐ์กฐ๋์ ํ์๊ฐ ๋๋ค.
macOS ์์๋ ํฌ์ปค์ค๋ ๋ทฐ์ ๋ณด๋๊ฐ ์๊ธฐ๊ณ ํค๋ณด๋ ์ธํ์ ๋ฐ์๊ฑฐ๋ผ๋ ๊ฑธ ๋ณด์ฌ๊ณ , watchOS๋ ์ด๋ก์ ๋ณด๋๊ฐ ๊ทธ๋ ค์ง๊ณ tvOS๋ ๋ค๋ฅธ ์ปจํธ๋กค๋ณด๋ค ๋์์ ธ ์๋(=ํ๋ฒ) ๋ชจ์์๋ก ํ์๋๋ค.
ํฌ์ปค์ค์ ์ญํ
- ์ฌ์ฉ์ ์ ๋ ฅ(ํค๋ณด๋, ๋ฆฌ๋ชจ์ฝ ๋ฑ) ์ด ์ด๋๋ก ๊ฐ์ง ์๋ ค์ค
- ์ฌ์ฉ์๊ฐ ์ฑ์ ์ด๋ค ๋ถ๋ถ๊ณผ ์ธํฐ๋ํ ํ๊ณ ์๋์ง ๋น์ฃผ์ผ ๋ ํผ๋ฐ์ค ํฌ์ธํธ๋ฅผ ์ ๊ณตํจ
ํฌ์ปค์ค์ ๋์
ํฌ์ปค์ค๋ ์ปค์์ ๋ง์ด ์ ์ฌํ๊ฒ ๋์ํ๊ฒ ๋จ. ๋ง์ฐ์ค ์ปค์๋ก ํ๋ฉด์ ์ง์ ์ ๊ณ์ ์ถ์ ํ๋ ๊ฒ ๋์ ์, ํฌ์ปค์ค๋ UI์ ์ด๋ค ํํธ๊ฐ ํฌ์ปค์ค ์ธํ์ ํ๊ฒ์ด ๋๋์ง๋ฅผ ์ถ์ ํจ.
ํฌ์ปค์ค์ ๊ด๋ จ๋ ์์๋ค
Focusable Views
ํฌ์ปค์ค ์ธํ์ ์๋ตํ ๋ ์์์ง์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ทฐ. ๋ค๋ฅธ ์ํฉ๊ณผ ๋ค๋ฅธ ์ด์ ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ปจํธ๋กค์ด ํฌ์ปค์ค๋ ์ ์๋ค.
macOS์ ipadOS์ ํ ์คํธํ๋์ ๋ฒํผ์ ๋น๊ตํด๋ณผ ๋, ํ ์คํธํ๋๋ ํญ์ ํฌ์ปค์ค๊ฐ ์์. ํญ ํ๊ฑฐ๋ ํน์ Tab ํค๋ฅผ ์ด์ฉํด ์ด์ ์ปจํธ๋กค์์ ์ฎ๊ฒผ๋ , ์ด๋ฐ ์ข ๋ฅ์ ์ปจํธ๋กค์ ํธ์งํ๊ธฐ ์ํด ํฌ์ปค์ค๊ฐ ์ ๊ณต๋จ. ์๋ํ๋ฉด ๊ณ์ํด์ ํฌ์ปค์ค ์ธํ์ ์บก์ณํด์ผ ๋๊ธฐ ๋๋ฌธ์.
๋ฒํผ์ ๊ฒฝ์ฐ๋ ์ข ๋ค๋ฅธ๋ฐ, ๋ฒํผ์ ํด๋ฆญ๊ณผ ํญ์ ๋ค๋ฃจ๊ธฐ ๋๋ฌธ์ macOS์ iPad์์๋ ๋ฒํผ์ ํญํ๋ค๊ณ ํฌ์ปค์ค๊ฐ ๊ฐ์ง ์๋๋ค. ํฌ์ปค์ค๋ฅผ ์ฃผ๊ธฐ ์ํด์๋ ์์คํ ์ค์ ์์ ํค๋ณด๋ ์ธํ ์์ ํค๋ณด๋ ๋ค๋น๊ฒ์ด์ ์ ์ผ ํ Tabํค๋ฅผ ํตํด ๋ฒํผ์ ํฌ์ปค์ค๋ฅผ ์ค ์ ์๋ค.
๊ทธ ๋ค์ ์คํ์ด์ค ๋ฐ๋ฅผ ๋๋ฌ์ ๋ฒํผ์ ํ์ฑํ ํ ์ ์๋ค.
ํ์ฑํํ ๋ ๋ฒํผ์ ์ด์ ์ด ์๊ธฐ๋๋ฐ ์ฌ์ค ์ด๋ฐ ์ข ๋ฅ์ ์ปจํธ๋กค์ ๋ณธ์ธ๋ค ์์ ์ ํ๊ธฐ ์ํด ํฌ์ปค์ค๊ฐ ํ์ํ์ง ์๋ค. ๊ทธ๋ ์ง๋ง ์์คํ ์ด ํ์ฉํ ๊ฒฝ์ฐ, ํด๋ฆญ๊ณผ ํญ์ ํฌ์ปค์ค ๋๋ฆฌ๋ธ์ผ๋ก ํ ์ ์๊ฒ ํฌ์ปค์ค๋ฅผ ์ทจํ ์ ์์.
iOS 17, macOS Sonoma์์, ํฌ์ปค์ค ์์คํ ์ ์ปค์คํ ์ปจํธ๋กค์ด ๋์ฐธํ ์ ์๋ ์ API๋ฅผ ์ ๊ณตํจ. focusable ๋ทฐ ๋ชจ๋ํ์ด์ด๋ฅผ ์ ์ฉํ๋ฉด ์ปจํธ๋กค์์ ์ ๊ณตํ๋ ํฌ์ปค์ค ์ธํฐ๋์ ์ ์ข ๋ฅ๋ฅผ ์ง์ ํจ์ผ๋ก์จ ๊ฒฐ๊ณผ๋ก ๋์ค๋ ๋์์ ๋ฏธ์ธ ์กฐ์ ํ ์ ์์.
๊ณ์ํด์ ํฌ์ปค์ค๋ฅผ ์ ๋ฐ์ดํธํ๋ ์ปจํธ๋กค์ ๊ฒฝ์ฐ .focusable(interactions: .edit) ์ผ๋ก ์ค์ ํ๊ณ , ์ง์ ์ ์ธ ํฌ์ธํฐ ํ์ฑํ๋ฅผ ๋์ ํ๊ธฐ ์ํด์ ํฌ์ปค์ค๋ฅผ ์ฌ์ฉํ๋ ์ปจํธ๋กค์ .focusable(interactions.activate)์ผ๋ก ์ค์ ํจ.
// Focusable views
struct RecipeGrid: View {
var body: some View {
LazyVGrid(columns: [GridItem(), GridItem()]) {
ForEach(0..<4) { _ in Capsule() }
}
.focusable(interactions: .edit) // ๐
}
}
struct RatingPicker: View {
var body: some View {
HStack { Capsule() ; Capsule() }
.focusable(interactions: .activate) // ๐
}
}
์ธ์๋ฅผ ์ ๊ณตํ์ง ์์ผ๋ฉด, ์์คํ ์ ๋ชจ๋ ์ธํฐ๋์ ์ ํฌ์ปค์ค๋ฅผ ์ปจํธ๋กคํ๋๋ก ํจ.
macOS Sonoma ์ด์ ์, focusable ๋ชจ๋ํ์ด์ด๋ ํ์ฑํ๋ ๋งฅ๋ฝ์์๋ง ์ ๊ณตํ๋ค. ์ด๋ฏธ macOS ์ฝ๋์์ focuasable ๋ชจ๋ํ์ด์ด๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฉด ์ ๋์์ด ์ ์ฆ์ผ์ด์ค์ ๋ง๋์ง ๊ฒ์ฆํด์ผ๋ ๊ฒ.
interactions ์ธ์๋ฅผ ์ถ๊ฐํด์ ์ ๋ฐ์ดํธ ํด์ผ๋ ์ ์์.
ํฌ์ปค์ค์ ์ํ
์๊ฐ๊ณผ ์๊ฐ์ ํฌ์ปค์ค ์์คํ ์ํ. FocusState ์์คํ ์ ์ด๋ค ๋ทฐ๊ฐ ํฌ์ปค์ค๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ๊ณ์ ์ถ์ ํ๊ณ , ์ฑ์ ์ด ์ ๋ณด๋ฅผ ๊ณ์ ๊ฐ์ง๊ณ ์์ผ๋ฉด์ ์ธํ์ ์ด๋ป๊ฒ ๊ด๋ฆฌํ๊ณ ๋ทฐ๋ฅผ ์ด๋ป๊ฒ ์คํ์ผํ ์ง ๊ฒฐ์ ํจ. ์์คํ ์ํ๋ฅผ ๊ด์ธกํ๊ธฐ ์ํด์ ํน์ ๋ทฐ์์์ ํฌ์ปค์ค์ ์ฐ๊ฒฐํ๋ ๋ฐ์ธ๋ฉ์ ๋ง๋ค์ด์ฃผ์ด์ผ ํ๋ค. ๋ทฐ์์ ์ด ๋ฐ์ธ๋ฉ์ ์ฝ๊ณ ํฌ์ปค์ค๊ฐ ๋ฐ๋๋ฉด ์๋ฆผ์ ๋ฐ๊ฒ ๋๋ค. ์๋ฅผ ๋ค๋ฉด ๋ทฐ๊ฐ ํฌ์ปค์ค๋๋ฉด, ๋๋ ํฌ์ปค์ค๊ฐ ์ฌ๋ผ์ง๋ ์๋ฆผ.
// Focus state
struct GroceryListView: View {
@FocusState private var isItemFocused
@State private var itemName = ""
var body: some View {
TextField("Item Name", text: $itemName)
.focused($isItemFocused)
Button("Done") { isItemFocused = false }
.disabled(!isItemFocused)
}
}
ํฌ์ปค์ค ์ํ ํ๋กํผํฐ์ธ Boolean ๊ฐ์ ํ๋์ ๋ทฐ๊ฐ ํฌ์ปค์ค ๋์ด ์๋์ง๋ง์ ์๋ ค์ค๋ค.
๋ง์ฝ ๋ ๋ณต์กํ ์ผ์ด์ค์ ๊ฒฝ์ฐ, ์ปค์คํ ๋ฐ์ดํฐ ํ์ ์ ์ฌ์ฉํด์ผ ํจ.
Focused Values
Focused Value API๋ ์ฌ์ฉ์ ์ธํฐํ์ด์ค์ ๋ฆฌ๋ชจํธ ๋ถ๋ถ์ ์ฐ๊ฒฐ์ ๋ฐ์ดํฐ ์์กด์ฑ์ ์ด๋ป๊ฒ ๊ตฌ์ถํ๋์ง์ ๋ํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด์ค๋ค.
์ด API๋ฅผ ์ฌ์ฉํด์ ์กํฐ๋ธ ์ฌ์์ ์ผ์ด๋๋ ์ผ๋ค์ ๊ธฐ๋ฐ์ผ๋ก ์ฑ์ ์ปค๋งจ๋๋ฅผ ์ ๋ฐ์ดํธ ํ ์ ์๋ค. Focused Value๋ ์๋ก ๋ค๋ฅธ ์์๋ค์ ๋ฐ์ดํฐ ํ๋ก์ฐ๊ฐ ๊ฐ๋ฅํ๊ฒ ํ๋ค.
์ปค์คํ ์ ํ๋ ๋ง๋ค์ด์ ๋ฉ์ธ ๋ฉ๋ด ์ฝํ ํธ์ ์ ์ฉํ๋๋ก ํด๋ณผ ๊ฒ.
Focused Values๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ ๊ฑด ์ปค์คํ Environment ํค์ ์ค๋ธ์ ํธ๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๋น์ทํจ.
FocusedValueKey ํ๋กํ ์ฝ์ ์ฌ์ฉํด์ ์ ํค๋ฅผ ๋ง๋ค๊ณ ํ์ฅํด์ ์๋ก์ด ํค๋ฅผ ๊ฐ์ ธ์ค๊ณ ์ธํ ํ ์ ์๋ ์ฐ์ฐ ํ๋กํผํฐ๋ฅผ ์ ์ธํ๋ค.
// Focused values
struct SelectedRecipeKey: FocusedValueKey {
typealias Value = Binding<Recipe>
}
extension FocusedValues {
var selectedRecipe: Binding<Recipe>? {
get { self[SelectedRecipeKey.self] }
set { self[SelectedRecipeKey.self] = newValue }
}
}
struct RecipeView: View {
@Binding var recipe: Recipe
var body: some View {
VStack {
Text(recipe.title)
}
.focusedSceneValue(\.selectedRecipe, $recipe)
// ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋, ํด๋น ์ฌ์ ๋ทฐ์์ ๊ฐ์ ธ์ค๋ ๊ฐ์ ๊ฐ์ด๋ ๋ฐ์ธ๋ฉ, ObservableObject๊ฐ ๋ ์ ์๋ค.
}
}
struct RecipeCommands: Commands {
@FocusedBinding(\.selectedRecipe) private var selectedRecipe: Recipe?
var body: some Commands {
CommandMenu("Recipe") {
Button("Add to Grocery List") {
if let selectedRecipe {
addRecipe(selectedRecipe)
}
}
.disabled(selectedRecipe == nil)
}
}
private func addRecipe(_ recipe: Recipe) { /* ... */ }
}
struct Recipe: Hashable, Identifiable {
let id = UUID()
var title = ""
var isFavorite = false
}
๋ทฐ ๋ชจ๋ํ์ด์ด๋ฅผ ํด๋น ๋ฐ์ดํฐ์ ์ฐ๊ฒฐํด์, ํฌ์ปค์ค๊ฐ ๋ทฐ ๊ณ์ธต์ ์ํ๊ฒ ํ๋ค.
Environment ๊ฐ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, ๋ค์ด๋๋ฏน ํ๋กํผํฐ๋ฅผ ์ ์ธํจ์ผ๋ก์จ Focused Value์ ์ ๊ทผํ ์ ์๋ค. ์ฌ๊ธฐ์๋ Focused Value๊ฐ ๋ฐ์ธ๋ฉ์ด๋ฏ๋ก @FocusedBinding ํ๋กํผํฐ ๋ํผ๋ฅผ ์ฌ์ฉํด์ ์ฌ๊ธฐ์ ์ปค์คํ ํค ํจ์ค๋ฅผ ์ ๊ณตํ ๊ฒ.
@FocusedBinding ์ ํฌ์ปค์ค๋ ๋ทฐ์ ๋ทฐ์ ์กฐ์์ ์ดํผ๊ณ ํค์ ์ฐ๊ด๋ ๋ฐ์ธ๋ฉ์ด ์๋์ง ํ์ธํ๋ค. ํ๋กํผํฐ ๋ํผ๋ ์๋์ ์ผ๋ก ๋ฐ์ธ๋ฉ์ ํ์ด์ ๋ฐ์ฐ๋ฉ ๊ฐ์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๊ฒ ํด์ค๋ค.
์ด์ ์ด ์ ํ๋กํผํฐ๋ฅผ view์ ๋ฐ๋์์ ์ฌ์ฉํ๋ ๊ฒ. ์๊ฐ์ด ์ง๋๋ฉด์ ํฌ์ปค์ค๊ฐ ๋ค๋ฅธ ์ปจํธ๋กค๊ณผ ํน์ ํ์ฑํ๋ ๋ค๋ฅธ ์๋์ฐ๋ก ์ด๋ํ๊ฒ ๋๋๋ฐ, ์์คํ ์ ์๋ก์ด ์ปจํ ์คํธ์์ ์ฐพ์ ๊ฐ๋ค์ ๋ฐ์ํ์ฌ ๋ทฐ๋ฅผ ์ ๋ฐ์ดํธ ํ๊ฒ ๋ ๊ฒ.
Focused Sections
ํฌ์ปค์ค ์น์ ์ ์ด์ฉํ๋ฉด, ์ฌ์ฉ์ ์ธํฐ๋์ (์ ํ TV ๋ฆฌ๋ชจ์ฝ์ ์ค์์ดํํ๊ฑฐ๋, ํค๋ณด๋์์ Tabํค๋ฅผ ๋๋ ์ ๋ ๋ฑ) ํฌ์ปค์ค ์ด๋๋ฐฉ์์ ์ํฅ์ ์ค ์ ์์.
๊ธฐ๋ณธ์ ์ผ๋ก ํฌ์ปค์ค๋ ํ๋ฉด์ leading ๊ฐ์ฅ์๋ฆฌ์์ ๊ฐ์ฅ ๊ฐ๊น์ด ์ต์๋จ์ ์ปจํธ๋กค๋ถํฐ ์์ํ๋ค.
์ฌ๊ธฐ์ Tab์ ๋๋ฅด๋ฉด ํฌ์ปค์ค๊ฐ ํ์ฌ ๋ก์ผ์ผ์ ๋ ์ด์์ ์์์ ๋ฐ๋ผ์ ๋ค์ ์ปจํธ๋กค๋ก ์ด๋ํ๋ค.
Leading
: ํ๊ตญ์ด, ์์ด์ฒ๋ผ Left-to-right๋ก ์ฝ๋ ๊ณณ์์๋ ์ผ์ชฝ์ด์ง๋ง ์๋์ด๊ฐ์ด Right-to-left ์ธ ๋๋ผ์ ๊ฒฝ์ฐ ์ค๋ฅธ์ชฝ
Locale
: ์ฅ์. ์ข ๋๋ ํด๋น ์ง์ญ๊ณผ ๋ฌธํ๊ถ์ ์๋ฏธํจ. ์์ฒ๋ผ RTL์ธ ์๋์ด๊ถ ๊ตญ๊ฐ์ ๊ฒฝ์ฐ, ๋ค์์ผ๋ก ์ด๋ํ๊ฒ ๋๋ฉด ์ค๋ฅธ์ชฝ์์ → ์ผ์ชฝ์ผ๋ก ์ด๋ํ๊ฒ ๋ ๊ฒ.
์คํฌ๋ฆฐ์ ๋ง์ง๋ง ์ปจํธ๋กค์ ๋๋ฌํ๋ฉด, Tab์ ๋๋ฅผ๋ ์ํ์ค์ ๋งจ ์ฒ์์ผ๋ก ๋ค์ ์์ํ๊ฒ ๋๋ค.
์ ํ TV์์ ํฌ์ปค์ค ์ด๋์ ๋ฐฉํฅ์ฑ์ด ์๋ค. ์, ์๋, ์ข, ์ฐ๋ก ์ค์์ดํํด์ ์ปจํธ๋กค์ ์ด๋ํ ์ ์๋ค.
์ข์ฐ๋ก ๋์ ํธ๊ฐ์ ์ด๋ํ ์ ์์ง๋ง, ์ฅ๋ณด๊ธฐ ๋ฆฌ์คํธ์ ํฌ๋ ๋ธ๋ฅ๋ ๋ฅผ ๋ฃ๊ณ ์ถ์๋ฐ ์ค์์ดํ ๋ค์ดํ ์ ์์. ์๋ํ๋ฉด ์ด ๋ฒํผ์ด ๋ฐ๋ก ํฌ๋ ๋ธ๋ฅ๋ ์๋์ ์์นํ์ง ์๊ธฐ ๋๋ฌธ์.
ํฌ์ปค์ค ํ๊ฒ์ ์ ๋ ฌํ๊ธฐ ์ํด์, ์๋ ๋ฒํผ์ ์ปจํ ์ด๋๋ฅผ ํฌ์ปค์ค ์น์ ์ผ๋ก ๋งํฌํ ๊ฒ. ํฌ์ปค์ค ์น์ ์ด ์ด๋ ์ ์ค์ณ์ ํ๊ฒ์ด ๋์ง๋ง ํฌ์ปค์ค๊ฐ ๋๋๊ฒ ์๋๋ผ ๊ฐ์ฅ ๊ฐ๊น์ด ํฌ์ปค์ค๋ ์ ์๋ ์ฝํ ํธ๋ฅผ ๊ฐ๋ฆฌํค๋ ๊ฐ์ด๋๊ฐ ๋๋ค.
ํฌ์ปค์ค ์น์ ์ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๋ ค๋ฉด ํฌ์ปค์ค ์น์ ์ด ์ฝํ ํธ๋ค๋ณด๋ค ๋ ๋ง์ ๊ณต๊ฐ์ ์ฐจ์งํ๊ณ ์์ด์ผ ํ๋ค.
์ง๊ธ ๊ฐ์ ๊ฒฝ์ฐ ๋ฒํผ ์ ํ๋ก Spacer()๋ฅผ ์ถ๊ฐํด์ ์คํ์ด ์คํฌ๋ฆฐ ๋๋น์ ๋ง๊ฒ ์ปค์ง๋๋ก ํ ๊ฒ.
๋ ํฐ ํฌ์ปค์ค ํ๊ฒ์ด ๋์์ผ๋ฏ๋ก, ์ด๋๋๊ณ ์ค์์ดํ ๋ค์ดํด์ ์๋ ๋ฒํผ์ ์ ๊ทผํ ์ ์๊ฒ ๋๋ค.
// Focus sections
struct ContentView: View {
@State private var favorites = Recipe.examples
@State private var selection = Recipe.examples.first!
var body: some View {
VStack {
HStack {
ForEach(favorites) { recipe in
Button(recipe.name) { selection = recipe }
}
}
Image(selection.imageName)
HStack {
Spacer()
Button("Add to Grocery List") { addIngredients(selection) }
Spacer()
}
.focusSection()
}
}
private func addIngredients(_ recipe: Recipe) { /* ... */ }
}
struct Recipe: Hashable, Identifiable {
static let examples: [Recipe] = [
Recipe(name: "Apple Pie"),
Recipe(name: "Baklava"),
Recipe(name: "Crème Brûlée")
]
let id = UUID()
var name = ""
var imageName = ""
}
Focus๋ฅผ ์ปจํธ๋กคํ๊ธฐ
์ด ๋ถ๋ถ์ ์๋ 2ํธ์์ ๋ง์ ๋ค๋ฃจ์์ต๋๋ค.
https://kimdee.tistory.com/entry/SwiftUI-Focus-์-๊ดํ์ฌ-2-WWDC-2023-์์-์ ๋ฆฌ
๊ฐ์ฌํฉ๋๋ค.
์ค๋๋ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
๊ถ๊ธํ๊ฑฐ๋ ๋๋๊ณ ์ถ์ ์๊ธฐ๊ฐ ์์ผ์๋ฉด ๋๊ธ๋ก ์๋ ค์ฃผ์ธ์!
์ฌ๋ฐ๊ฒ ์ฝ์ผ์ จ๋ค๋ฉด ๊ณต๊ฐ๊ณผ ๊ตฌ๋ ์ ํฐ ํ์ด ๋ฉ๋๋ค.
ํญ์ ๊ฐ์ฌํฉ๋๋ค.
์ด ๊ธ์ doy.oopy.io ์๋ ๋ฐํ๋์ด ์์ต๋๋ค.
๋๊ธ