Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] UIKit에서 SwiftUI의 Preview 사용 방법 본문

iOS 응용 (swift)

[iOS - swift] UIKit에서 SwiftUI의 Preview 사용 방법

jake-kim 2021. 5. 26. 22:40

UIKit에서 기존에 사용하던 preview

  • @IBINspectrable, @IBDesignable 추가하여, storyboard에서 수동으로 확인
    -> swiftUI의 preview 기능을 사용할경우 UIKit 앱의 코드를 한줄도 바꾸지 않고 preview 기능 사용 가능

Preview사용 조건

  • Xcode 11 이상
  • macOS Catalina 이상
  • iOS 13+

Preview 사용 원리

코드를 변경 -> 실시간으로 변경되는 UI확인 가능

  • dynamic replacement 기능 사용: Xcode에서 컴파일 없이 작성하고 있는 코드의 미리보기를 실시간으로 확인 가능

Preview - View 미리보기

  • Preview를 띄울 임의의 View 작성
import UIKit

class MyYellowButton: UIButton {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        fatalError("not support coder")
    }

    // MARK: - Private

    private func setupView() {
        backgroundColor = .yellow
        setTitleColor(.black, for: .normal)
    }
}
  • UIViewPreview 추가: UIView 서브클래스의 미리보기를 보여줄 수 있는 컨벤션을 지닌 클래스
#if canImport(SwiftUI) && DEBUG
import SwiftUI
struct UIViewPreview<View: UIView>: UIViewRepresentable {
    let view: View

    init(_ builder: @escaping () -> View) {
        view = builder()
    }

    // MARK: - UIViewRepresentable

    func makeUIView(context: Context) -> UIView {
        return view
    }

    func updateUIView(_ view: UIView, context: Context) {
        view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        view.setContentHuggingPriority(.defaultHigh, for: .vertical)
    }
}
#endif
  • 미리보기할 View 하단에 아래와같이 작성
// MyYellowButton

#if canImport(SwiftUI) && DEBUG
import SwiftUI

struct MyYellowButtonPreview: PreviewProvider{
    static var previews: some View {
        UIViewPreview {
            let button = MyYellowButton(frame: .zero)
            button.setTitle("buttonTest", for: .normal)
            return button
        }.previewLayout(.sizeThatFits)
    }
}
#endif
  • Resume 버튼 클릭하여 실시간 업데이트 활성화

  • default device에서 보는 방법:.previewLayout(.sizeThatFits) 제거
#if canImport(SwiftUI) && DEBUG
import SwiftUI

struct MyYellowButtonPreview: PreviewProvider{
    static var previews: some View {
        UIViewPreview {
            let button = MyYellowButton(frame: .zero)
            button.setTitle("buttonTest", for: .normal)
            return button
        }
    }
}
#endif

Preview - view 동시에 여러개 보기

  • UIViewPreview를 아래에 하나 더 작성

Preview - Dynamic Type Size 별 보기

button.titleLabel?.font = .preferredFont(forTextStyle: .body)
button.titleLabel?.adjustsFontForContentSizeCategory = true
  • 모든 Dynamic Type Size을 ForEach문을 통해 접근하여 표출
#if canImport(SwiftUI) && DEBUG
import SwiftUI

struct MyYellowButtonPreview: PreviewProvider{
    static var previews: some View {
        ForEach(ContentSizeCategory.allCases, id: \.self) { sizeCategory in
            UIViewPreview {
                let button = MyYellowButton(frame: .zero)
                button.setTitle("testButton", for: .normal)
                button.titleLabel?.font = .preferredFont(forTextStyle: .body)
                button.titleLabel?.adjustsFontForContentSizeCategory = true
                return button
            }.environment(\.sizeCategory, sizeCategory)
            .previewDisplayName("\(sizeCategory)")
        }.previewLayout(.sizeThatFits)
        .padding(10)

    }
}
#endif

Preview - 다크모드

  • 모든 ColorScheme(light, dark)을 ForEach문을 통해 접근하여 표출
#if canImport(SwiftUI) && DEBUG
import SwiftUI

struct MyYellowButtonPreview: PreviewProvider{
    static var previews: some View {

        ForEach(ColorScheme.allCases, id: \.self) { colorScheme in
            UIViewPreview {
                let button = MyYellowButton(frame: .zero)
                button.setTitle("buttonTest", for: .normal)
                button.backgroundColor = .tertiaryLabel
                return button
            }.environment(\.colorScheme, colorScheme)
            .previewDisplayName("\(colorScheme)")
        }.previewLayout(.sizeThatFits)
    }
}
#endif

light, dark mode

Preview - device위에 view가 그려지도록 설정 방법

  • Preview 정의
#if canImport(SwiftUI) && DEBUG
import SwiftUI
extension UIView {
    private struct Preview: UIViewRepresentable {
        typealias UIViewType = UIView

        let view: UIView

        func makeUIView(context: Context) -> UIView {
            return view
        }

        func updateUIView(_ uiView: UIView, context: Context) {
        }
    }

    func showPreview() -> some View {
        Preview(view: self).previewDevice(PreviewDevice(rawValue: "iPhone 12 Pro"))
    }
}
#endif
  • 사용 시 instance.showPreview()로 사용
class MyView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)

        backgroundColor = .blue
    }

    required init?(coder: NSCoder) {
        fatalError()
    }
}

#if DEBUG
import SwiftUI

struct ServiceUnavailableViewPreview: PreviewProvider {
    static var previews: some View {
        return MyView().showPreview()
    }
}
#endif

Preview - ViewController

  • 가장 간단한 방법: extension UIViewController하여서 showPreview()로 설정
  • extension
    • 주의: 디바이스는 simulator에 설치한 디바이스만 표출되므로, 설치되어 있지않은 iPhoneSE와 같은 것은 simulator 설치 필요
enum DeviceType {
    case iPhoneSE2
    case iPhone8
    case iPhone12Pro
    case iPhone12ProMax

    func name() -> String {
        switch self {
        case .iPhoneSE2:
            return "iPhone SE"
        case .iPhone8:
            return "iPhone 8"
        case .iPhone12Pro:
            return "iPhone 12 Pro"
        case .iPhone12ProMax:
            return "iPhone 12 Pro Max"
        }
    }
}

#if canImport(SwiftUI) && DEBUG
import SwiftUI
extension UIViewController {

    private struct Preview: UIViewControllerRepresentable {
        let viewController: UIViewController

        func makeUIViewController(context: Context) -> UIViewController {
            return viewController
        }

        func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        }
    }

    func showPreview(_ deviceType: DeviceType = .iPhone12Pro) -> some View {
        Preview(viewController: self).previewDevice(PreviewDevice(rawValue: deviceType.name()))
    }
}
#endif

  • 사용
#if canImport(SwiftUI) && DEBUG
import SwiftUI

struct ViewController_Preview: PreviewProvider {
    static var previews: some View {
        ViewController().showPreview(.iPhone8)
    }
}
#endif

  • Preview한 화면에 동시에 디바이스 여러개 표출
    • showPreview를 여러번 호출
#if canImport(SwiftUI) && DEBUG
import SwiftUI

struct ViewController_Preview: PreviewProvider {
    static var previews: some View {
        ViewController().showPreview(.iPhone8)
        ViewController().showPreview(.iPhone12Pro)
    }
}
#endif

Preview 관련 단축키

  • Canvas 닫기/열기: cmd + option + enter
  • Resume: cmd + option + P

* 참고

https://nshipster.co.kr/swiftui-previews/

https://developer.apple.com/documentation/swiftui/view/previewdevice(_:)

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

Comments