iOS/SwiftUI

@NameSpace

Hamp 2025. 10. 25. 16:10
반응형

🏁 학습할 내용

  • coordinateSpace
  • @NameSpace
  • 활용

🧭 CoordinateSpace

 

 

🤔 어디서 봤을까?

 

GeometryReader proxy 쪽에서 frame정보를 얻을 때, 어떤 Coordinate로부터 가져올 지

결정하게 되는데, 거기서 처음 접했던 것 같다.

 

🧱 구조

CoordinateSpace

public enum CoordinateSpace {

    // 뷰 계층구조의 최상단을 기준으로하는 global 좌표계   
    case global

    // 현재 뷰를 기준으로한 local 좌표계
    case local

    // Hashable한 값으로 구분된 뷰의 좌표계
    case named(AnyHashable)
}

 

CoordinateSpaceProtocol

  • 특정 좌표계를 갖고 있어야함.
public protocol CoordinateSpaceProtocol {

    /// The resolved coordinate space.
    var coordinateSpace: CoordinateSpace { get }
}

 

NamedCoordinateSpace

  • iOS17부터 사용가능한 객체로
  • CoordinateSpaceProtoclEquatable을 채택하고 있음
  • 같음을 이용할 수 있는 형태
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
public struct NamedCoordinateSpace : CoordinateSpaceProtocol, Equatable {

    /// The resolved coordinate space.
    public var coordinateSpace: CoordinateSpace { get }
    public static func == (a: NamedCoordinateSpace, b: NamedCoordinateSpace) -> Bool
}

 

마찬가지로 iOS17부터는 CoordinateSpace를 바로 쓰기보다는,
CoordinateSpaceProtocol을 채택하고 있는 구조체들을 씀

  • GlobalCoordinateSpace,
  • LocalCoordinateSpace,
  • NamedCoordinateSpace

 


🚀 @NameSpace

 

눈치챘겠지만, 이 녀석은 NamedCoordinateSpace와 관려이 있을 것 같다..

쓸 수는 있지만, 실제로는 matchedGeometryEffect 같이 매개변수에 in: NameSpace.ID가 있는 녀석들과 매우 밀접하다.

 

🧱 구조

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
@frozen @propertyWrapper public struct Namespace : DynamicProperty, Sendable {
    @inlinable public init()

    public var wrappedValue: Namespace.ID { get }

    @frozen public struct ID : Hashable {

        public func hash(into hasher: inout Hasher)
        public static func == (a: Namespace.ID, b: Namespace.ID) -> Bool
        public var hashValue: Int { get }
    }
}

 

  • 영구적인 Identity로 정의된 객체에 접근할 수 있는 namespace
  • propertyWrapper기 때문에, wrappedValue를 자세히보면 ID, 즉 Hashable한 타입
  • DynamicProperty를 채택하고 있는 것을 보면, View Update 사이클과도 관련이 있음

♻️ 활용

iOS17부터, 기본적으로 유용하게 사용되는 NamedCoordinateSpace 제공해주는데 바로 scrollView다.

@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
extension CoordinateSpaceProtocol where Self == NamedCoordinateSpace {
    public static func scrollView(axis: Axis) -> Self
    public static var scrollView: NamedCoordinateSpace { get }
}

 

 

아래와 같이, 원하는 axis을 명시해주면, 스크롤 axis가 일치하는 가장 가까운 조상의
ScrollView의 좌표공간을 얻을 수 있다.

GeometryReader { proxy in
  let frame = proxy.frame(in: .scrollView(axis: .vertical)) // 기준이 되는 건?
  Text("Item \(index)\nY: \(Int(frame.minY))")
        .frame(width: 120, height: 120)
        .background(Color.blue.opacity(0.3))
        .cornerRadius(10)
}

 

 

 

그런데 여기서 중요한점은 iOS17부터 가능하다는거다... 

이 기능을 똑같이 만들려면 @NameSpace + CoordinateSpace의 .named를 이용하면 만들 수 있다.

struct NestedScrollViewExample: View {
  @Namespace var namespace

    var body: some View {
        ScrollView(.vertical) {  // 바깥 스크롤뷰
            VStack(spacing: 40) {
                Text("Outer ScrollView")
                    .font(.headline)

                ScrollView(.horizontal) {  // 안쪽 스크롤뷰
                    HStack {
                        ForEach(0..<5) { index in
                            GeometryReader { proxy in
                              let frame = proxy.frame(in: .named(namespace)) //namespace로 부터 좌표공간 얻기 
                              Text("Item \(index)\nY: \(Int(frame.minY))")
                                    .frame(width: 120, height: 120)
                                    .background(Color.blue.opacity(0.3))
                                    .cornerRadius(10)
                            }
                            .frame(width: 120, height: 120)
                        }
                    }
                    .padding()
                }
                .background(Color.yellow.opacity(0.2))
            }
        }
        .coordinateSpace(name: namespace) // ✅ 얻고싶은 coordinateSpace 표시
    }
}

📘 요약 비교

항목 coordinateSpace @Namespace
역할 좌표 계산 기준 정의 뷰 간 애니메이션 매칭
관련 함수 GeometryReader, .frame(in:) matchedGeometryEffect
주요 용도 위치 추적, 스크롤, 정렬 자연스러운 뷰 전환 애니메이션
범위 좌표계 이름 단위 애니메이션 그룹 단위

출처

반응형