SwiftUI TabView를 사용하는 뷰에서 내부 컨텐츠들의 개별 offsetX를 탭뷰 기준으로 구하고 싶다.
원하는 것은 탭뷰의 좌측 상단으로 부터 컨텐츠가 얼마나 이동했는가 이다.
GeometryReader 추가 전 예제 코드
좌우 패딩 20을 갖는 간단한 탭뷰 코드
struct TabViewCoordinateSpaceExample: View {
@State private var selection: Int = 0
private let tabs: [Color] = [
.red.opacity(0.2),
.blue.opacity(0.2),
.green.opacity(0.2),
.brown.opacity(0.2),
.yellow.opacity(0.2),
.cyan.opacity(0.2)
]
var body: some View {
tabView
}
var tabView: some View {
TabView(selection: $selection) {
ForEach(0..<tabs.count, id: \.self) { i in
tabs[i]
}
}
.tabViewStyle(.page)
.padding(.horizontal, 20)
.frame(height: 300)
}
}
GeometryReader 추가
offset 측정을 위해 GemoetryReader를 추가
import SwiftUI
struct TabViewCoordinateSpaceExample: View {
@State private var selection: Int = 0
private let tabs: [Color] = [
.red.opacity(0.2),
.blue.opacity(0.2),
.green.opacity(0.2),
.brown.opacity(0.2),
.yellow.opacity(0.2),
.cyan.opacity(0.2)
]
private let tabViewCoordinateSpaceName: String = "tabCoordinateSpace"
var body: some View {
tabView
}
var tabView: some View {
TabView(selection: $selection) {
ForEach(0..<tabs.count, id: \.self) { i in
tabs[i]
.background(geometryReader) // 3️⃣
}
}
.tabViewStyle(.page)
.coordinateSpace(name: tabViewCoordinateSpaceName) // 1️⃣
.padding(.horizontal, 20)
.frame(height: 300)
}
var geometryReader: some View { // 2️⃣
GeometryReader { proxy in
let frame = proxy.frame(in: .named(tabViewCoordinateSpaceName))
Text("\(frame.minX)")
.frame(width: frame.width)
}
}
}
- TabView를 coordinatorSpace 모디파이어를 이용하여 커스텀 좌표계로 지정했다.
- GeometryReader로 해당 좌표계를 이용한 origin을 가져왔다.
- 탭뷰 내 컨텐츠의 background에 적용했다.
그런데 TabView 내부에서 사용하는 .named(tabViewCoordinateSpaceName)은 제대로 동작하지 않고 항상 .global 처럼 동작한다.
세로 모드일 때는 20부터 시작 / 가로모드일 때는 79부터 시작
뭔가 잘못 알고 있나 싶어서 HStack으로 동일한 예제를 만들어 봤는데, 0부터 잘 시작한다.
struct VStackCoordinateSpaceExample: View {
private let hStackCoordinateSpaceName: String = "vStackCoordinateSpace"
private let items: [Color] = [
.red.opacity(0.2),
.blue.opacity(0.2),
.green.opacity(0.2),
.brown.opacity(0.2),
.yellow.opacity(0.2),
.cyan.opacity(0.2)
]
var body: some View {
HStack {
ForEach(0..<items.count, id: \.self) { i in
items[i]
.background(geometryReader)
}
}
.tabViewStyle(.page)
.coordinateSpace(name: hStackCoordinateSpaceName)
.padding(.horizontal, 20)
.frame(height: 400)
}
var geometryReader: some View {
GeometryReader { proxy in
let originX = proxy.frame(in: .named(hStackCoordinateSpaceName)).origin.x
Text("\(originX)")
}
}
}
해결
한참을 끙끙대다가 결국 포기하고, GeometryReader를 외부와 내부 각각 두어 직접 계산하였다.
TabView 내의 뷰들은 커스텀 좌표계를 인지 못하는 듯 하다.
import SwiftUI
struct TabViewContentOffsetExample: View {
@State private var selection: Int = 0
private let tabs: [Color] = [
.red.opacity(0.2),
.blue.opacity(0.2),
.green.opacity(0.2),
.brown.opacity(0.2),
.yellow.opacity(0.2),
.cyan.opacity(0.2)
]
var body: some View {
tabView
}
var tabView: some View {
GeometryReader { proxy in
TabView(selection: $selection) {
ForEach(0..<tabs.count, id: \.self) { i in
tabs[i]
.background(geometryReader(containerMinX: proxy.frame(in: .global).minX))
}
}
}
.tabViewStyle(.page)
.padding(.horizontal, 20)
.frame(height: 300)
}
func geometryReader(containerMinX: CGFloat) -> some View {
GeometryReader { proxy in
let minX = proxy.frame(in: .global).minX
let offsetX = minX - containerMinX
Text("\(offsetX)")
.frame(width: proxy.size.width)
}
}
}
가로 / 세로 모두 0부터 시작
흑흑 더 좋은 방법을 아시는 분은 코멘트 부탁드립니다
'iOS > SwiftUI' 카테고리의 다른 글
SwiftUI - GeometryReader, CoordinateSpace (0) | 2024.08.20 |
---|---|
SwiftUI 뷰가 다시 그려지는 조건 탐구 (Redraw) (1) | 2022.11.20 |