본문 바로가기

# 02/Swift

[SwiftUI] 회전된 Text를 VStack에 올바르게 정렬하는 방법

반응형
struct RotatedTextTestView: View {

    var body: some View {
        VStack() {
            Text("Horizontal text")
            Text("Vertical text").rotationEffect(.degrees(-90))
        }
    }
}

 

SwiftUI에서 Text를 회전(rotationEffect)하면 원래 배치된 위치 기준으로 회전되므로,
👉 VStack 내부에서 회전한 크기를 반영하지 않고 배치되는 문제가 발생할 수 있다.
👉 이를 해결하기 위해 PreferenceKey를 사용하여 뷰의 크기를 측정하고, 회전 후의 올바른 위치를 계산해야 한다.

 

 

VStack with two Text views (one rotated)

 

Text("Vertical text") with rotationEffect(-90°)

 

 

✅ Text 회전 후 올바르게 정렬하는 방법

1️⃣ PreferenceKey를 사용하여 뷰의 크기 측정

뷰의 크기를 측정하기 위해 SizeKey라는 PreferenceKey를 만든다.

 
private struct SizeKey: PreferenceKey {
    static let defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

 

이 PreferenceKey를 사용하면, 특정 뷰의 크기를 onPreferenceChange를 통해 동적으로 추적할 수 있다.


2️⃣ View 확장(extension)으로 크기 측정 함수 추가

captureSize(in:)를 활용하여 뷰의 크기를 Binding<CGSize>에 저장할 수 있도록 만든다.

 

extension View {
    func captureSize(in binding: Binding<CGSize>) -> some View {
        overlay(GeometryReader { proxy in
            Color.clear.preference(key: SizeKey.self, value: proxy.size)
        })
        .onPreferenceChange(SizeKey.self) { size in binding.wrappedValue = size }
    }
}

 

이제 View에서 .captureSize(in: $size)를 사용하여 크기를 저장할 수 있다.


3️⃣ 회전된 뷰를 자동으로 크기 조정하도록 수정

rotationEffect를 사용하면 원래 위치에서 회전하기 때문에 뷰의 배치를 다시 계산해야 한다.
👉 이를 위해 CGRect를 사용하여 회전 후의 새로운 프레임을 계산한다.

 

extension View {
    func rotated(_ angle: Angle = .degrees(-90)) -> some View {
        Rotated(self, angle: angle)
    }
}
 

그리고 회전된 뷰의 크기를 반영할 Rotated 뷰를 만든다.

 

struct Rotated<Rotated: View>: View {
    var view: Rotated
    var angle: Angle

    init(_ view: Rotated, angle: Angle = .degrees(-90)) {
        self.view = view
        self.angle = angle
    }

    @State private var size: CGSize = .zero

    var body: some View {
        // 회전된 후의 새로운 프레임을 계산
        let newFrame = CGRect(origin: .zero, size: size)
            .offsetBy(dx: -size.width / 2, dy: -size.height / 2)
            .applying(.init(rotationAngle: CGFloat(angle.radians)))
            .integral

        return view
            .fixedSize()                    // ✅ 원래의 뷰 크기를 유지
            .captureSize(in: $size)         // ✅ 뷰의 크기를 측정하여 저장
            .rotationEffect(angle)          // ✅ 뷰를 회전
            .frame(width: newFrame.width,   // ✅ 회전된 후의 새로운 크기를 적용
                   height: newFrame.height)
    }
}
 

이제 View.rotated()를 사용하면 회전 후의 크기를 자동으로 반영하여 배치할 수 있다.
✅ VStack에서 정렬될 때 회전 후의 오프셋이 반영되므로, 정확한 위치에 배치 가능


4️⃣ RotatedTextView 구현

Text 뷰를 Rotated를 활용하여 자동으로 크기를 반영하는 RotatedTextView를 구현한다.

 

struct RotatedTextView: View {
    let text: String

    var body: some View {
        Text(text)
            .rotated() // ✅ 자동 크기 조정 및 정렬 반영
    }
}
 

이제 RotatedTextView를 사용하면 VStack 내부에서도 회전된 오프셋이 자동으로 조정되어 정렬된다.


📌 정리

📍 SwiftUI에서 Text를 회전할 때 발생하는 문제

  • rotationEffect를 사용하면 뷰의 원래 위치를 기준으로 회전하기 때문에 VStack 내부에서 정렬이 깨질 수 있다.
  • 회전 후의 크기가 반영되지 않으므로, 오프셋이 엉뚱한 위치에 배치될 수 있다.

📍 해결 방법

✅ PreferenceKey를 사용하여 뷰의 회전 전 크기를 측정
CGRect 변환을 통해 회전 후의 크기를 계산
✅ .rotationEffect()를 사용한 후 새로운 크기로 frame()을 업데이트

📍 최종 적용 방법

RotatedTextView를 사용하면, VStack 내부에서도 회전된 크기가 반영된 채로 올바르게 배치됨! 🚀

 

 

struct RotatedTextTestView: View {

    var body: some View {
        VStack() {
            Text("Horizontal text")
            RotatedTextView(text: "Vertical text")
        }
    }
}

 

RotatedTextTestView

 

 

 

 


https://stackoverflow.com/questions/58494193/swiftui-rotationeffect-framing-and-offsetting

반응형

'# 02 > Swift' 카테고리의 다른 글

[Swift] 기초 입문 Study  (0) 2023.07.21
[Swift] RxSwift  (0) 2023.01.11
[Swift] DispatchQueue / Serial / Concurrent  (0) 2023.01.11
[Swift] DFS/BFS  (0) 2022.12.18
[Swift] MaxHeap  (0) 2022.12.12