samsung-ga / woody-iOS-tip

🐶 iOS에 대한 소소한 팁들과 개발하다 마주친 버그 해결기, 그리고 오늘 배운 것들을 모아둔 레포
19 stars 0 forks source link

[TIL] 코드와 View의 동기화 #14

Open samsung-ga opened 2 years ago

samsung-ga commented 2 years ago

하나의 앱을 꾸준히 유지보수를 하게 되면 꾸준한 업데이트로 인해 빠른 UI 개선이 적용되고 삭제되어야 한다. 이럴 경우 기존의 Storyboard와 코드로 UI를 구성하는 것보다 SwiftUI의 preview를 이용하여 빠르게 개발이 가능하다.

기존 UI 구성 방법

Storyboard와 Code로 뷰를 작성하는 기존 방법의 장단점을 알아보자.

Storyboard

장점 단점
1. 한 눈에 볼 수 있는 UI
2. 직관적이기 떄문에 개발하기 쉬운 Interface Builder
1. 각각의 화면마다 Storyboard, 각각의 뷰마다 Storyboard가 필요
2. xml파일로 이루어져 있기 때문에 자주 일어나는 git conflict

Code

장점 단점
파일 하나로 View 개발 가능하기 떄문에 관리가 용이한 점 View 구성을 한 눈에 파악하기 어려운 점



SwiftUI의 preview가 등장하면서 UI 작성의 속도는 빨라졌다. 정량적으로 얼마나 빨라졌는지 측정은 불가하지만 개발을 하면서 직접 느껴볼 수 있다. 특히, 디바이스별로 다르게 나타나는 UI를 테스트해볼 때 가장 용이한 것 같다.



Preview in UIkit (UIKit에서 preview를 사용해보자)

예제를 살펴보면 이해가 쉽기 때문에 바로 예제로 넘어가자.

UICollectionViewCell을 preview로 UI 빌드없이 보기

#if DEBUG
import SwiftUI
// 1️⃣ preview는 swiftUI의 기능이기 떄문에 swiftUI의 view 안에 UIKit을 넣어주어야 한다. 
struct FriendCellRepresentable: UIViewRepresentable {
    typealias UIViewType = FriendCell

    func makeUIView(context: Context) -> FriendCell {
        FriendCell()
    }

    func updateUIView(_ uiView: FriendCell, context: Context) {

    }
}
#endif
// 2️⃣ PreviewProvider을 통해 확인한다.
struct FriendCellPreviews: PreviewProvider {
    static var previews: some View {
        FriendCellRepresentable()
    }
}

상황

  1. 디바이스별 각 사이즈에 맞는 cell을 테스트해볼 수 있다. 원하는 만큼 프레임과 패딩을 줄 수 있다.
struct FriendCellPreviews: PreviewProvider {
    static var previews: some View {
        FriendCellRepresentable()
            .frame(width: 320, height: 90) // ✅
            .previewLayout(.sizeThatFits) // ✅
            .padding(EdgeInsets(top: 10, leading: .zero, bottom: 10, trailing: .zero)) // ✅
    }
}

스크린샷 2022-06-25 오후 11 12 23

  1. 원하는 디바이스에 cell을 직접 넣어볼 수 있다.
struct FriendCellPreviews: PreviewProvider {
    static var previews: some View {
        FriendCellRepresentable()
            .previewDevice(PreviewDevice(rawValue: "iPhone 12")) // ✅
    }
}

스크린샷 2022-06-25 오후 11 09 13

  1. 여러가지 디바이스를 쉽게 테스트해볼 수 있다.
// The following values are supported:
//
//     "iPhone 8"
//     "iPhone 8 Plus"
//     "iPhone SE"
//     "iPhone 11"
//     "iPhone 11 Pro"
//     "iPhone 11 Pro Max"
//     "iPad mini 4"
//     "iPad Air 2"
//     "iPad Pro (9.7-inch)"
//     "iPad Pro (12.9-inch)"
//     "iPad (5th generation)"
//     "iPad Pro (12.9-inch) (2nd generation)"
//     "iPad Pro (10.5-inch)"
//     "iPad (6th generation)"
//     "iPad Pro (11-inch)"
//     "iPad Pro (12.9-inch) (3rd generation)"
//     "iPad mini (5th generation)"
//     "iPad Air (3rd generation)"
//     "Apple TV"
//     "Apple TV 4K"
//     "Apple TV 4K (at 1080p)"
//     "Apple Watch Series 2 - 38mm"
//     "Apple Watch Series 2 - 42mm"
//     "Apple Watch Series 3 - 38mm"
//     "Apple Watch Series 3 - 42mm"
//     "Apple Watch Series 4 - 40mm"
//     "Apple Watch Series 4 - 44mm"
struct FriendCellPreviews: PreviewProvider {
    static var previews: some View {
        FriendCellRepresentable()
            .previewDevice(PreviewDevice(rawValue: "iPhone 12")) // ✅

        FriendCellRepresentable()
            .previewDevice(PreviewDevice(rawValue: "iPhone 8")) // ✅ 
    }
}

스크린샷 2022-06-25 오후 11 08 05

  1. Group을 이용하여 옵션들을 한꺼번에 적용하여 여러 개를 테스트해 볼 수 있다.
struct FriendCellPreviews: PreviewProvider {
    static var previews: some View {
        Group { // ✅
            FriendCellRepresentable()
                .frame(width: 320, height: 90)
            FriendCellRepresentable()
                .frame(width: 375, height: 90)
        }
        .previewLayout(.sizeThatFits)
        .padding(EdgeInsets(top: 10, leading: .zero, bottom: 10, trailing: .zero))

    }
}

스크린샷 2022-06-26 오후 2 38 28

  1. 이러한 편안함 뿐만 아니라, 아래와 같은 장점도 있다.

Tip

Reference

samsung-ga commented 2 years ago

Extension 해서 사용하기

#if 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 toPreview() -> some View {
         Preview(viewController: self)
     }
 }
 #endif