/iOS 📱

[UIKit] UIKit의 스토리보드 Preview 적용하기(Storyboard, Preview, 미리보기)

아렉스_Arex 2024. 12. 8. 14:35
반응형

안녕하세요 아렉스입니다 :>
 
WWDC 23 이후로 프리뷰를 하는 방식은 2가지가 되었습니다.
 
UIKit 환경에서도 Preview를 이용하니 매우 편한 개발 경험이 생겼습니다. 코드베이스로 된 경우에는 잘 사용하셨는데, 스토리보드로 된 뷰컨에서는 활용 못하는 분들이 있어서 글을 작성해봅니다.
 
스토리보드로 된 뷰컨을 Preview 하기 전 현재 사용되는 방법들에 대해서 간략히 설명하겠습니다.
 


Preview 방식 - 1

매크로가 소개 되기 전 사용했던 방식입니다.
UIViewControllerRepresentable 를 SwiftUI 뷰로 통합하기 위해 사용되는 프로토콜입니다.
결과적으로만 보면 간단해보이지만, 처음 이 코드를 접했을 때, Preview에 활용하는 아이디어에 놀랐던 기억이 있네요

import SwiftUI

extension UIViewController {
    struct Preview: UIViewControllerRepresentable {
        let viewController: UIViewController
        
        func makeUIViewController(context: Context) -> UIViewController {
            viewController
        }
        
        func updateUIViewController(_ uiView: UIViewController,context: Context) {}
    }
    
    func toPreview() -> some View {
        Preview(viewController: self)
    }
}

 
사용 방식은 아래와 같습니다.

struct ViewController_PreviewProvider: PreviewProvider {
    static var previews: some View {
        ViewController()
            .toPreview()
    }
}

Preview 방식 - 2

WWDC 23에 발표 된 매크로를 이용한 프리뷰입니다.
직접적으로 SwiftUI 프레임워크를 import 하지도, extension을 별도 구현하지않아도 되는 점이 매우 편리합니다.

#Preview {
    ViewController()
}

위에 코드들을 스토리보드에 적용시

현재 스토리보드에는 Alert를 표시해주는 Button이 하나 있습니다.
왼쪽 사진은 스토리보드, 우측 사진은 Preview 입니다.
위 두개의 방식을 예제를 통해 적용해봐도 안되는 것을 볼 수 있습니다 !
-> 아 ! 스토리보드 이래서 안 쓰는구나 !

좌: 스토리보드, 우: 프리뷰


Why ?

방식 1 코드

extension UIViewController {
    struct Preview: UIViewControllerRepresentable {
        let viewController: UIViewController
        // 이 메서드에서 반환 된 인스턴스를 Preview 해줌
        func makeUIViewController(context: Context) -> UIViewController {
            viewController
        }
    }
}

 
 

방식 2에서 사용 된 매크로 구문

@available(iOS 17.0, macOS 14.0, tvOS 17.0, *)
@freestanding(declaration) public macro Preview(
    _ name: String? = nil,
    traits: PreviewTrait<Preview.ViewTraits>...,
    @PreviewMacroBodyBuilder<UIViewController> body: @escaping @MainActor () -> UIViewController
    ) = #externalMacro(module: "PreviewsMacros", type: "KitViewMacro")

 
방식 1,2 를 살펴보니 Preview를 해주는 것은 반환 된 UIViewController를 사용합니다.
위 코드에서 반환 해준 것이 스토리보드를 통해 생성 된 인스턴스가 아니기 때문입니다.


How to ?

스토리보드의 뷰컨트롤러를 인스턴스화하여 화면 전환했던 경험이 있으신가요 ?
그 아이디어를 그대로 사용합니다. 마치 UIViewControllerRepresentable 를 Preview에 사용했던 아이디어처럼 말이죠

struct ViewController_PreviewProvider: PreviewProvider {
    static var previews: some View {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(
            withIdentifier: "ViewController"
        )
        return vc.toPreview()
    }
}
#Preview {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewController(
        withIdentifier: "ViewController"
    )
    return vc
}

결과

UIButton과, UIButton에 대한 @IBAction 도 Preview에서 작동하는 것을 볼 수 있습니다.
 

 
 


etc

1. #Preview 쓰고싶은데 버전이 안 맞아서 에러가 나와서 사용 못하고 있어요. 
-> @available 를 사용

@available(iOS 17.0, *)
#Preview {}

 
2. 매번 쓰는게 귀찮아요
스토리보드 identifier 지정시 클래스 이름과 동일하게 지정하는 관례를 통해 아래와 같은 인터페이스를 구현할 수 있습니다.

protocol Previewable {
    associatedtype T
    static func createViewController(
        with storyboard: String
    ) -> T
}
// 기본 구현
extension Previewable {
    static func createViewController(
        with storyboard: String = "Main"
    ) -> T {
        let storyboard = UIStoryboard(name: storyboard, bundle: nil)
        let vc = storyboard.instantiateViewController(
            withIdentifier: String(describing: T.self)
        ) as! T
        return vc
    }
}

 
사용 방법

1. Previewable 채택하기
2. 스토리보드 identifier로 사용할 타입 지정
class ViewController: UIViewController, Previewable {
    typealias T = ViewController
}

 
https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable

 

UIViewControllerRepresentable | Apple Developer Documentation

A view that represents a UIKit view controller.

developer.apple.com