[TIL] zPosition

Eden·2025년 9월 9일

zPosition화면의 앞/뒤(깊이) 순서를 결정하는 값입니다. 값이 클수록 화면의 앞쪽에, 작을수록 뒤쪽에 그려집니다. 주로 CALayer(모든 UIView의 백킹 레이어)와 SpriteKit에서 사용됩니다.


1) CALayer / UIKit 관점

기본

  • 대상: CALayer.zPosition (기본값 0)
  • 의미: 동일한 부모 레이어(sibling) 사이에서 페인터의 순서를 정함
  • 규칙: zPosition큰 레이어가 위, 같다면 sublayers 배열 순서가 위
// 두 뷰가 같은 superview를 공유한다고 가정
redView.layer.zPosition = 10   // 더 위에 보임
blueView.layer.zPosition = 0

중요한 특징

  • 좌표(2D 위치)에는 영향 없음: frame, center와 무관하게 그려지는 순서만 바뀜
  • 터치 처리와는 별개: UIView의 터치 hit-testing은 subviews 배열 순서로 처리되며, zPosition 값이 터치 우선순위를 바꾸지 않음
    • 뷰 터치 우선순위가 필요하면 bringSubviewToFront(_:), sendSubviewToBack(_:), isUserInteractionEnabled 조정 등으로 해결
  • 음수 가능: 음수는 더 뒤로 보낸다는 뜻. 단, 너무 큰 음/양수는 관리가 어려워짐
// 터치 우선순위는 zPosition이 아니라 subviews 순서 영향
view.addSubview(redView)   // 먼저 추가
view.addSubview(blueView)  // 나중에 추가 → 터치 우선
// 시각적으로만 redView를 위로
redView.layer.zPosition = 5

3D와의 관계

  • zPosition원근(perspective) 이 적용되면 깊이감에 영향을 줍니다.
  • 원근을 적용하려면 부모 레이어의 sublayerTransform.m34를 설정합니다.
var t = CATransform3DIdentity
t.m34 = -1.0 / 500.0                  // 원근감
containerView.layer.sublayerTransform = t

card.layer.zPosition = 50             // 앞쪽으로 튀어나와 보임

디버깅 팁

  • 겹침 문제가 있을 때는 다음 순서로 확인
    1) 같은 부모인지 확인 (다르면 zPosition로 못 겹칩니다. 서로 다른 계층은 각자의 부모 내부에서만 정렬됨)
    2) zPosition 값 비교
    3) sublayers/subviews 추가 순서 확인
    4) clipsToBounds/masksToBounds로 잘려 보이는지 확인
    5) 투명도(alpha)와 isHidden 확인

2) SpriteKit 관점

  • 대상: SKNode.zPosition (기본값 0)
  • 의미: 형제 노드 사이의 그리기 순서
  • 규칙: zPosition큰 노드가 위. 같다면 추가/계층 순서가 적용
let hero = SKSpriteNode(imageNamed: "hero")
let ui   = SKSpriteNode(imageNamed: "ui_panel")

hero.zPosition = 10   // 캐릭터를 UI 뒤/앞으로 조정
ui.zPosition   = 100  // UI를 항상 맨 위로

성능/관리 팁

  • 너무 큰 범위의 값 사용을 피하고, 의미 있는 레이어 구간을 정해서 사용
    • 예: 배경 0~99, 캐릭터 100~199, 투사체 200~299, UI 1000 이상 등
  • 부모가 다르면 서로의 zPosition을 비교하지 않습니다. 부모 내부에서만 정렬됩니다.

3) 자주 하는 질문(FAQ)

Q1. 뷰가 가려져서 안 보여요. zPosition만 올리면 되나요?

  • 같은 부모 아래의 형제라면 zPosition으로 해결 가능
  • 부모가 다르면 각 부모의 정렬 규칙에 따르므로, 계층 구조 재검토가 먼저

Q2. zPosition을 올렸는데 터치는 여전히 아래 뷰로 들어갑니다.

  • 정상 동작입니다. 터치는 subviews 순서가 우선입니다.
  • bringSubviewToFront로 순서를 바꾸거나, hitTest(_:with:)/point(inside:with:)를 커스터마이징하세요.

Q3. 그림자/라운드 때문에 이상하게 보입니다.

  • masksToBounds = true이면 그림자도 잘립니다.
  • 그림자를 쓰면 오프스크린 렌더링이 발생할 수 있으니 성능에 유의하세요.
  • 필요하면 shadowPath를 지정해 비용을 줄입니다.

4) 체크리스트

  • 같은 부모의 형제 간 시각적 순서만 바꿈 (좌표/레이아웃/터치에는 직접 영향 없음)
  • 터치는 뷰 계층(subviews) 순서가 우선
  • SpriteKit도 동일 원칙: 큰 zPosition이 위, 같으면 계층/추가 순서
  • 부모가 다르면 서로의 zPosition은 비교되지 않음
  • 원근을 쓰고 싶다면 sublayerTransform.m34 활용

5) 간단 예제 모음

// 1) 버튼을 이미지 위로 올리기
imageView.layer.zPosition = 0
ctaButton.layer.zPosition = 1

// 2) 배지 뷰를 항상 맨 위로 (같은 부모라면)
badgeView.layer.zPosition = 999

// 3) SpriteKit에서 계층 구간으로 관리
background.zPosition = 0
map.zPosition        = 100
npc.zPosition        = 200
player.zPosition     = 300
ui.zPosition         = 1000

결론

zPosition그려지는 순서(깊이) 를 제어하는 간단하고 강력한 수단입니다.
터치/레이아웃과는 분리되어 있다는 점을 기억하고, 같은 부모-형제 관계에서 시각적 우선순위를 조정하는 용도로 사용하면 문제를 빠르게 해결할 수 있습니다.

profile
iOS Dev

0개의 댓글