뷰의 레이아웃을 위치시킬 때, 오토레이아웃을 사용하면 사실 frame, anchorPoint 등 레이아웃 관련 프로퍼티는 크게 신경 쓰지 않아도 됩니다. 그런데, 이번 프로젝트에서 frame을 이용해야 하는 조금 어려운 애니메이션을 구현해보는 구현 내용이 있었고 구현을 하다보니 이전에 공부하다 헷갈렸던 내용과 똑같은 곳에서 헷갈렸습니다.
그래서 이 글은 저와 비슷하게 헷갈리는 분들이 있을 수 있기 때문에 작성했습니다.
layer프로퍼티에 있는 position은 무엇을 나타내는 값일까요? 라는 질문이 나오기까지의 과정을 설명하겠습니다.
먼저, Core Animation Basics 문서를 읽다보면 중간에 아래 그림이 나옵니다.
"anchor point가 layer의 position에 얼마나 영향을 미치는지"에 대한 사진입니다. 그리고 위에 부가설명이 적혀있었습니다.
Figure 1-5 demonstrates how changing the anchor point from its default value to a different value affects the position property of a layer. Even though the layer has not moved within its parents’ bounds, moving the anchor point from the center of the layer to the layer’s bounds origin changes the value in the position property
위 말을 마지막 문장을 보시면, "앵커포인트를 (0.5, 0.5), 즉 중앙에서 다른 곳으로 옮기는 행위는 position 프로퍼티 값을 바꾼다" 라고 이해가 됩니다. 하지만 실제로 코드를 작성해보면 그렇지 않았습니다.
아래 코드에서 position 프로퍼티 값의 결과는 (0,0)으로 일정했습니다.
let layer = CAShapeLayer()
layer.path = UIBezierPath(
rect: .init(origin: .init(x: view.center.x-100, y: view.center.y-100),
size: .init(width: 200, height: 200)
)).cgPath
layer.fillColor = UIColor.blue.cgColor
print(layer.position)
layer.anchorPoint = .init(x: 0, y: 0)
print(layer.position)
view.layer.addSublayer(layer)
먼저, position의 정의를 다시 알아봤습니다.
position은 레이어의 프로퍼티이고 자신의 부모 레이어의 좌표계내에서의 위치를 나타낸다고 합니다.
그리고 Discussion에서 새로운 레이어는 기본적으로 position이 (0.0, 0.0)으로 설정된다고 합니다.
아, 그럼 새로운 레이어를 추가했기 때문에 (0,0)으로 초기화되었고 새 레이어를 추가해서 예제를 만들면 안되겠다는 사실을 알았습니다.
그럼 이제 새로운 뷰를 추가하여 실험을 해보았습니다.
let view1 = UIView(frame: .init(
origin: .init(x: view.center.x-100, y: view.center.y-100),
size: .init(width: 200, height: 200)
))
view1.backgroundColor = .blue
view.addSubview(view1)
위와 같이 작성한 후, 레이어의 앵커포인트를 (0,0)으로 수정했습니다.
view1.layer.anchorPoint = .init(x: 0, y: 0)
그랬더니 웬걸, 뷰가 오른쪽 아래로 배치되었습니다.
position을 앵커포인트가 바뀌기 전과 바뀐 후와 비교해보니, (195.0, 422.0)로 일정했습니다. 해당 좌표는 핸드폰 중앙입니다.
여기서, position은 anchorPoint를 변하게해도 일정하다는 사실을 알 수 있습니다.
그럼 위 문서에서 사진은 무슨 의미일까요?
사진에선, anchorPoint가 (0.5, 0.5) -> (0,0)으로 바뀌었을 때, position은 (100.0, 100.0) -> (40.0, 60.0) 으로 좌표값이 변했습니다. 하지만 이것은 완전히 상대적 값입니다. "position은 이제 anchorPoint가 바뀌면서 중앙에서 왼쪽 위로 변했다"라고 사진에서는 보여 준것입니다. 그것을 이해가 쉽게 하기 위해 좌표를 통해 보여준 것입니다. 처음에 봤을 때, 아마 모든 사람들이 position 값이 변하는 줄 알았을 것이라고 생각합니다.. 이 생각이 맞는 지 실험을 해보았습니다.
위 상황처럼 뷰의 anchorPoint를 (0.0, 0.0)으로 수정한 상황에서, position을 (0,0)으로 수정해봤습니다. (혹시나, 레이어와 뷰가 따로 노는가 싶어서 레이어의 테두리에도 색깔을 주었습니다.)
let view1 = UIView(frame: .init(
origin: .init(x: view.center.x-100, y: view.center.y-100),
size: .init(width: 200, height: 200)
))
view1.backgroundColor = .blue
view.addSubview(view1)
view1.layer.anchorPoint = .init(x: 0, y: 0)
view1.layer.position = .init(x: 0, y: 0)
view1.layer.borderColor = UIColor.red.cgColor
view1.layer.borderWidth = 4
네모가 왼쪽위에 붙어있는 것을 알 수 있습니다. (왜 왼쪽 위에 붙지? 라고 생각하신다면 레이어트리를 생각하시면 이해하실 수 있습니다.)
다시 정리하면,
position은 anchorPoint를 명시적으로 나타내는 좌표라고 할 수 있습니다. 따라서 position을 변경하면 뷰는 움직입니다. 하지만 anchorPoint를 바꾼다고 뷰가 움직이지는 않습니다.
이 anchorPoint는 transform에서 의미를 가집니다. 해당 문서를 더 읽어보면 아래의 사진을 보실 수 있습니다.
anchor point가 transform 영향을 어떻게 미치는지에 대한 사진으로 회전축이라고 생각하시면 됩니다. 이 회전축은 뷰의 비율(0~1)로 생각하고 정의를 하고 비율이므로 실제 anchorPoint의 좌표는 position이 되는 것입니다.
SwiftUI?
그럼 아주 조금만 더 나아가서, SwiftUI에서도 position과 anchorPoint라는 개념이 있는 지 궁금해져서 조금 더 찾아봤습니다.
position이라는 뷰모디파이어는 찾을 수 있었습니다. 하지만, anchorPoint는 찾을 수 없었습니다.
Text("Position by passing a CGPoint()")
.position(CGPoint(x: 175, y: 100))
.border(Color.gray)
그럼 SwiftUI에서는 뷰의 anchorPoint를 바꾸기 위해서는 어떻게 할까요? 라는 질문이 자연스레 나올 수 있습니다.. 네.. 이게 정상입니다..
그래서 검색해봤고.. 스택오버플로우에서 하나의 예시를 찾을 수 있었습니다. 하나의 예시라고 한 이유는 SwiftUI에서는 관점을 바꿔서 생각해야했기 때문입니다. (그래서 다양한 방법으로 애니메이션을 줄 수 있기 때문입니다.)
https://stackoverflow.com/questions/57442995/anchor-point-in-swiftui
(UIKit언어로) anchorPoint가 (0.5, 0.5)일 때,
VStack {
Color.green
.frame(width: bigger ? self.squareWidth * 2 : self.squareWidth, height: self.squareHeight)
.animation(.default, value: bigger)
Button(action: {
self.bigger.toggle()
}) {
Text("Click me!")
}
}
(UIKit언어로) anchorPoint가 (0.0, 0.5) 일 때, 😅
HStack {
VStack {
Color.green
.frame(width: bigger ? self.squareWidth * 2 : self.squareWidth)
.frame(height: self.squareHeight)
.animation(.default, value: bigg
Button("Click me!") { self.bigger.toggle() }
.animation(.default, value: bigger)
Space()
}
정말 트위터에서 최근에 본 사진이 생각납니다 ㅎㅎ
https://developer.apple.com/documentation/quartzcore/calayer/1410791-position
https://developer.apple.com/documentation/swiftui/view/position(x:y:)
'iOS' 카테고리의 다른 글
[CoreAnimation] Layer에 CoreAnimation을 무한으로 체이닝할 수 있을까? (2) | 2022.10.23 |
---|---|
[CoreAnimation] 물체를 path 따라 움직이기 (feat. 우주비행사 날리기) (0) | 2022.10.22 |
[CoreAnimation] Layer Masking (feat. 키 모양 만들기) (0) | 2022.10.20 |
[CoreAnimation] CAReplicatorLayer 알아보기 (0) | 2022.10.20 |
[Swift] 영문자로만 이루어진 String 알아내기 + String Index 쉽게 접근하기 (1) | 2022.08.16 |