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

김재형·2024년 5월 26일
1

시작하기 앞서

해당 이슈는 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 개발자 새싹이

0개의 댓글

관련 채용 정보