이 포스트는 ChatGPT가 작성하였습니다.

소개

최근, iOS 앱을 macOS에서 실행할 때 Stepper UI 요소가 충돌하는 문제를 겪었습니다. 이 문제는 주로 Apple Silicon Mac에서 발생하며, 특히 iOS 앱을 “Mac (Designed for iPhone)” 타겟으로 실행할 때 발생합니다. Stepper는 iOS에서 잘 작동하지만, macOS에서는 크래시가 발생하는 현상이 있었습니다. 이 문제는 SwiftUI의 호환성 문제로 보였고, 이를 해결하기 위한 과정을 공유하려고 합니다.

iOS에서 Stepper

 

문제의 원인

문제의 핵심은 Stepper UI 컴포넌트가 macOS에서 실행될 때 발생하는 충돌입니다. 해당 오류는 Thread 1: EXC_BAD_ACCESS 오류로, 앱이 메모리에 접근하려고 할 때 잘못된 주소를 참조하면서 발생합니다. 이는 특히 iOS App On Mac 환경에서 발생할 수 있습니다.

 

해결 방법

1. @ViewBuilder와 AnyView 활용

Stepper는 기본적으로 iOS에서 잘 작동하지만, macOS 환경에서 충돌을 피하려면, 직접 구현한 커스텀 Stepper를 사용하는 것이 좋습니다. 이를 위해 @ViewBuilderAnyView를 활용하여 뷰를 조건에 맞게 처리했습니다. 아래는 ForMacStepper라는 커스텀 Stepper 구현을 사용한 방법입니다.

 

2. ForMacStepper 커스텀 뷰 구현

ForMacStepper는 value, range, step을 받아 이를 기반으로 값을 증감시키는 커스텀 Stepper입니다. label을 클로저로 받아 해당 UI를 동적으로 정의할 수 있도록 했습니다. 이 때, @ViewBuilder로 받은 뷰를 AnyView로 감싸서 반환하여 타입 문제를 해결했습니다.

struct ForMacStepper: View {
  @Binding var value: Int
  let range: ClosedRange<Int>
  let step: Int
  let label: () -> AnyView
  
  var body: some View {
    HStack {
      // 라벨 부분
      label()
      Button("-") {
        if value > range.lowerBound {
          value -= step
        }
      }
      .buttonStyle(.bordered)
      Button("+") {
        if value < range.upperBound {
          value += step
        }
      }
      .buttonStyle(.bordered)
    }
  }
}

extension ForMacStepper {
  init(value: Binding<Int>, range: ClosedRange<Int>, step: Int, @ViewBuilder label: @escaping () -> some View) {
    self._value = value
    self.range = range
    self.step = step
    self.label = {
      AnyView(label()) // 클로저로 받은 뷰를 AnyView로 감싸서 반환
    }
  }
}

 

3. ProcessInfo.processInfo.isiOSAppOnMac 사용

macOS 환경에서만 ForMacStepper를 사용하도록 조건을 추가했습니다. ProcessInfo.processInfo.isiOSAppOnMac을 활용하여, 앱이 macOS에서 실행될 때는 ForMacStepper를, 그렇지 않으면 기본 Stepper를 사용하도록 했습니다.

private var stepperLabel: some View {
  HStack {
    Text("문제풀이 타이머")
    Spacer()
    Text(cfgTimerSeconds == 0 ? "제한없음" : "\(cfgTimerSeconds)초")
      .foregroundColor(.gray)
  }
}

Section {
  if ProcessInfo.processInfo.isiOSAppOnMac {
    ForMacStepper(value: $cfgTimerSeconds, range: 0...60, step: 5) {
      stepperLabel
    }
  } else {
    Stepper(value: $cfgTimerSeconds, in: 0...60, step: 5) {
      stepperLabel
    }
  }
} header: {
  Text("타이머")
}

이제 위의 코드를 통해, Stepper가 macOS에서 충돌하는 문제를 해결할 수 있습니다. ForMacStepper는 iOS 앱이 Mac에서 실행될 때 발생하는 충돌을 방지하고, iOS와 macOS에서 모두 정상적으로 동작할 수 있도록 했습니다. 결과적으로, Stepper가 iOS 및 macOS 모두에서 안정적으로 작동하는 앱을 만들 수 있게 되었습니다.

macOS에서 커스텀 타이머

 

결론

iOS 앱을 macOS에서 실행할 때 발생하는 Stepper UI 충돌 문제는 AnyView@ViewBuilder를 활용한 커스텀 컴포넌트로 해결할 수 있었습니다. 이 과정에서 ProcessInfo.processInfo.isiOSAppOnMac을 활용하여 iOS와 macOS에서 각각 적절한 뷰를 사용하도록 조건을 추가하는 방법을 통해 문제를 해결할 수 있었습니다. 이와 같은 방법을 사용하면 Stepper와 같은 일반적인 UI 컴포넌트도 iOS와 macOS에서 모두 잘 작동하게 할 수 있습니다.

 

참고

 


 🤖 이 포스트는 ChatGPT-4o 등의 생성형 AI의 도움을 받아 작성되었습니다. 🤖

문의 | 코멘트 또는 yoonbumtae@gmail.com


카테고리: Swift


0개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다