(SwiftUI Issue) Invalid frame dimension (negative or non-finite) 과 Layout 시스템

김재형·2024년 5월 26일
1
post-custom-banner

시작하기 앞서

해당 이슈는 Swift UI 를 학습하며 미니 프로젝트(ShopY)를 구성하며 생긴 트러블 슈팅 글입니다.

Invalid frame dimension (negative or non-finite) 누구냐 넌.

해당 오류는 뷰의 크기 설정 과 관련한 문제로, 프레임 크기를 잘못 계산 혹은 설정 할때 발생하는 오류입니다. 영어를 직영해 보면, 잘못된 프레임 치수(음성-음수 또는 비한정) 이라고 번역이 되는데, 즉 해당하는 오류가 발생되는 곳에선 음수가 발생 했다는 의미이죠!

그럴수가 있나?

문제의 예시 코드를 살펴보며 이해해 보도록 하겠습니다.

struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .frame(width: -100, height: 50) // 잘못된 프레임 값 (음수)
    }
}

해당 하는 코드에선 width를 -100 즉 음수를 설정하였기 때문에 오류가 발생하게 됩니다.
하지만 이런 경우는 거의 없을 것 입니다. 다른 예시를 들어보도록 하죠

실제 문제의 코드

현재 위 사진의 탭바를 구현하기 위하여 아래와 같은 코드를 구현 하였었습니다.

...  생략  ....

extension CustomTabbarView {
    
    func CustomTabItem(
        imageName: String,
        title: String,
        isActive: Bool
    ) -> some View {
        VStack {
            HStack(spacing: 10) {
                Spacer()
                Image(systemName: imageName)
                    .resizable()
                    .renderingMode(.template)
                    .foregroundColor(isActive ? JHColor.onlyWhite : JHColor.gray)
                    .frame(width: 20, height: 20)
                
                if isActive {
                    Text(title)
                        .font(.system(size: 14))
                        .foregroundColor(isActive ? JHColor.onlyWhite : JHColor.gray)
                }
                Spacer()
            }
            .frame(width:.infinity , height: 40) // 문제의 부분!
            .background(isActive ? JHColor.likeColor.opacity(0.8) : .clear)
            .modifier(cornerRadiusVersion(cornerRadius: 24))
        }
    }
}
}

위와 같이 작성해도 문제가 발생 하게 됩니다
문제가 뭐였을까요?
바로 무한대 라는 이유에 있습니다.

한번 다시 생각해 봅시다.
frame"틀"(width: 넓이, height: 높이 ) 인데 넓이에다 최대로 넓혀 보아라( 무한 ) 을 명령한다면,
과연 컴퓨터는 그것을 이해 할수 있을까요?

물론 .Infinity 가 Spacer() 와 비슷한 기능 ( 가능한 공간을 차지하게 ) 을 가지고 있지만
엄현히 다르기 때문에 .Infinity 만 다시 생각해 본다면 문제가 발생하는게 맞습니다.

레이아웃 시스템 (대원칙) 을 학습해보자

SwiftUI의 레이아웃 시스템 (대원칙)

  • 제안 단계 (Proposal Phase): 부모 뷰가 자식 뷰에게 자신의 사이즈를 그대로 제안합니다.
  • 크기 조정 단계 (Sizing Phase): 자식 뷰가 부모 뷰의 제안을 받고, 자신에게 적절한 크기(display contents)를 고려하며, 계산후 반환합니다.
  • 위치 지정 단계 (Positioning Phase): 부모 뷰가 자식 뷰의 크기를 반영하여 자식 뷰의 위치를 지정합니다.

자 레이아웃 시스템을 살펴보고 다시 코드를 생각해 보겠습니다.

Spacer()

VStack {
    Text("직녀")
    Spacer()
    Text("견우")
}

해당 하는 경우에는 문제가 없겠죠? 왜죠?
부모뷰가 자식 뷰에게 사이즈를 주었고 그 사이즈를 동일 선상에 있는 두
Text의 크기를 빼면 Spacer() 값이 나옵니다. 즉 스유는 어떻게 처리할지 할고 있게되죠.

  • VStack은 자식 뷰들에게 각각의 크기를 제안
  • Text("직녀")와 Text("견우")는 고정된 크기
  • Spacer는 남은 모든 공간을 차지
HStack {
    Text("견우")
    Text("직녀")
        .frame(width: .infinity) // 문제 발생
}
  • HStack 제안: HStack은 자식 뷰들에게 적절한 크기를 제안합니다.
    첫 번째 Text("견우")는 고정된 크기를 가집니다.
  • 두 번째 Text("직녀")의 프레임 설정: frame(width: .infinity)는 무한대를 의미합니다. 유효한 크기로 변환할 수 없어 오류가 발생합니다.
  • 크기 계산의 실패: 무한대 값을 처리할 수 없기 때문에, "Invalid frame dimension (negative or non-finite)" 오류를 발생시킵니다.

다시 정리하면,

  • 무한대 값의 의미: .infinity는 무한대를 의미합니다.
    즉 실질적인 크기가 아니여서, 처리가 불가합니다.
  • 부모 뷰의 제안 실패: 부모 뷰(HStack)는 자식 뷰에게 유효한 크기를 제안해야 하는데 자식 뷰가 무한대 크기를 요구하면, 부모 뷰는 이를 어떻게 제안해야 할지 알 수가 없습니다.

그래서 해결법은...?

 .frame(maxWidth: .infinity)
 .frame(height: 46)

해결법은 매우 간단합니다. maxWidth 에다가 .infinity

마무리 하며...

이제 SwiftUI에서 "Invalid frame dimension (negative or non-finite)" 오류가 왜 발생하고, 무엇이 문제였던 것인가를 경험과 이해할수 있었던 시간이였습니다.

SwiftUI의 레이아웃 시스템을 이해하고 적절히 활용함으로써, "Invalid frame dimension" 오류를 예방하고 효율적인 레이아웃을 구현해 주세용!!

profile
IOS 개발자 새싹이
post-custom-banner

0개의 댓글