๋ค์ด๊ฐ๊ธฐ์ ์
๋งค๋ฒ ์กฐ๊ธ์ UI ์์ ์ ํ๋๋ผ ๋ช ๋ฒ์ ๋น๋๋ฅผ ํ์๋์? ๋ช ๋ฒ์ CMD+R ํค๋ฅผ ๋๋ฅด์์ฃ ?
์ ๋ ํ๋์ ํ๋ก์ ํธ๋ฅผ ํ๋๋ฐ๋ ์ ๋ง ๋ง์ด ์๋ฎฌ๋ ์ดํฐ๋ฅผ ๋๋ ค๋ณด๋๋ฐ์.
๋ฐ๋ก๋ฐ๋ก ๋ณํ๋ฅผ ๋ณผ ์๊ฐ ์์ผ๋๊น ๋ถํธํ๋๋ผ๊ณ ์...
๊ทผ๋ฐ ๊ทธ๊ฑฐ ์์๋์? SwiftUI๋ ์ฝ๋๋ฅผ ์ง๋ฉด์ ๋ฐ๋ก ์์ ํ๋ฆฌ๋ทฐ๋ฅผ ๋ณผ ์๊ฐ ์์ฃ !
์ด๊ฑธ UIKit์๋ ์ ์ฉ์์ผ๋ณด๋๊ฒ๋๋ค.
์๋ ๋ถํธํ ๊ฑธ ํด๊ฒฐํ๋ฉด์ ๋ฐ์ ํ๋๊ฑฐ์ฃ ~๐
ํ์์ ์ฝ๋๋ก ui ๊ฐ๋ฐ์ ์ฆ๊ฒผ๋ค๋ฉด ํจ์ฌ ์ ์ฉํ ๋ฐฉ๋ฒ์ผ ๊ฒ ๊ฐ์ต๋๋ค :)
๐ Preview๋ฅผ ์กฐ๊ธ ๋ ํธํ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํ Extensions
โ ํ์ํ ๋ toPreview() ๋ฉ์๋๋ง ํธ์ถํด์ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
// UIViewControllerRepresentable extension
//
// UIViewController+Preview.swift
// PreviewPractice
//
// Created by taehy.k on 2021/05/27.
//
import UIKit
#if DEBUG
import SwiftUI
@available(iOS 13, *)
extension UIViewController {
private struct Preview: UIViewControllerRepresentable {
// this variable is used for injecting the current view controller
let viewController: UIViewController
func makeUIViewController(context: Context) -> UIViewController {
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}
func toPreview() -> some View {
// inject self (the current view controller) for the preview
Preview(viewController: self)
}
}
#endif
๋ ๋์ Extensions!
- UIView๋ ์ ๋จ์ ์์ ํ ๋๋ ์๋์ ์๋๊ฒ ๋ ๋์๋ณด์ธ๋ค..!
//
// UIViewPreview.swift
// PreviewPractice
//
// Created by taehy.k on 2021/05/27.
//
#if canImport(SwiftUI) && DEBUG
import SwiftUI
public struct UIViewPreview<View: UIView>: UIViewRepresentable {
public let view: View
public init(_ builder: @escaping () -> View) {
view = builder()
}
// MARK: - UIViewRepresentable
public func makeUIView(context: Context) -> UIView {
return view
}
public func updateUIView(_ view: UIView, context: Context) {
view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
view.setContentHuggingPriority(.defaultHigh, for: .vertical)
}
}
public struct UIViewControllerPreview<ViewController: UIViewController>: UIViewControllerRepresentable {
public let viewController: ViewController
public init(_ builder: @escaping () -> ViewController) {
viewController = builder()
}
// MARK: - UIViewControllerRepresentable
public func makeUIViewController(context: Context) -> ViewController {
viewController
}
@available(iOS 13.0, tvOS 13.0, *)
@available(OSX, unavailable)
@available(watchOS, unavailable)
public func updateUIViewController(_ uiViewController: ViewController, context: UIViewControllerRepresentableContext<UIViewControllerPreview<ViewController>>) {
return
}
}
#endif
์ฌ์ฉ๋ฐฉ๋ฒ
- ๋ณํ๋ฅผ ํ์ธํ๊ณ ์ถ์ ๋ทฐ ์ปจํธ๋กค๋ฌ ํ๋จ ๋ถ์ ํด๋น ์ฝ๋๋ฅผ ์ ์ด์ฃผ๊ณ ์ฌ์ฉํฉ๋๋ค.
#if DEBUG
import SwiftUI
@available(iOS 13.0, *)
struct VCPreview: PreviewProvider {
// Device ๋ฐฐ์ด๋ก ์ฌ๋ฌ ๊ฐ์ ๋๋ฐ์ด์ค์ ์ ์ฉ๋ ๋ชจ์ต์ ๊ฐ์ด ํ์ธํ ์ ์์ต๋๋ค.
// ์ ๋ ์ง๊ธ 3๊ฐ์ง์ Device๋ฅผ ์ฌ์ฉํ๊ณ ์์ฃ .
static var devices = ["iPhone SE", "iPhone 11 Pro Max", "iPhone 12"]
static var previews: some View {
ForEach(devices, id: \.self) { deviceName in
UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ViewController")
// ์ต์คํ
์
์์ ๋ง๋ toPreview() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ณ ์์ฃ !
.toPreview()
.previewDevice(PreviewDevice(rawValue: deviceName))
.previewDisplayName(deviceName)
}
}
}
#endif
//
// BorderedButton.swift
// PreviewPractice
//
// Created by taehy.k on 2021/05/27.
//
import UIKit
class MyBaseButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
func setupView() {
layer.cornerRadius = 4
clipsToBounds = true
}
}
#if DEBUG
import SwiftUI
@available(iOS 13.0, *)
struct BorderedButton_Preview: PreviewProvider {
static var previews: some View {
// ์ด๋ฐ์์ผ๋ก ์ฌ์ฉํฉ๋๋คโผ๏ธ
UIViewPreview {
let button = MyBaseButton(frame: .zero)
button.setTitle("Follow", for: .normal)
button.setTitleColor(.blue, for: .normal)
return button
}
.previewLayout(.sizeThatFits)
.padding(10)
}
}
#endif
Device rawValue
- ์ง์ํ๋ rawValue ์ ๋๋ค.
- ์๊น ๋ง๋ค์ด๋์๋ Device ๋ฐฐ์ด์ ์ถ๊ฐํด์ ์ฌ์ฉํ์ธ์!
// 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"
- ์ ์ฒด์ฝ๋๊ฐ ๊ถ๊ธํ๋ฉด ์๋๋ฅผ ํผ์ณ์ ํ์ธํ์ธ์.
- ์ ์ฒด์ฝ๋
์ฌ์ฉ๋ชจ์ต (๋ทฐ ์ปจํธ๋กค๋ฌ, ํ ์ด๋ธ์ )
[Preview ์ฌ์ฉ๋ชจ์ต 1] ํ๋ฆฌ๋ทฐ๋ก ์ค์๊ฐ ๋ณํํ๋ ๋ชจ์ต์ ํ์ธํ ์ ์์ต๋๋ค.
[Preview ์ฌ์ฉ๋ชจ์ต 2] ์ ์ ๋ง๋ ๋ค๋ฉด ์ด๋ฐ์์ผ๋ก Preview๋ฅผ ํ์ฉํ ์ ์๊ฒ ์ฃ ?
Cell(์ ) ์ํ์ ๋ํด์ ์ฝ๋๋ฅผ ์ ์ฉํ๊ณ ์ถ์ผ๋ฉด
// XIB๋ก ๋ฐ๋ก ๋นผ์ ์
์ ์ ์ํ๋ค๋ฉด ๋ฐ๋์ ์ด๋ฐ์์ผ๋ก ์ ๊ทผํด์ค์ผ ํ๋ฉด์ ์ ๋์ต๋๋ค.
UINib(nibName: "InstagramDMTVC", bundle: nil)
.instantiate(withOwner: nil, options: nil).first as! InstagramDMTVC
#if DEBUG
import SwiftUI
struct InstagramDMTVCRepresentable: UIViewRepresentable {
typealias UIViewType = InstagramDMTVC
func makeUIView(context: Context) -> InstagramDMTVC {
return UINib(nibName: "InstagramDMTVC", bundle: nil)
.instantiate(withOwner: nil, options: nil).first as! InstagramDMTVC
}
func updateUIView(_ uiView: InstagramDMTVC, context: Context) {
uiView.setData(imageName: "profile",
name: "test_account",
message: "Sounds good ๐๐๐",
time: ".now")
}
}
@available(iOS 13.0, *)
struct InstagramDMTVCPreview: PreviewProvider {
static var previews: some View {
Group {
InstagramDMTVCRepresentable()
.frame(width: 375, height: 72)
InstagramDMTVCRepresentable()
.frame(width: 320, height: 100)
InstagramDMTVCRepresentable()
.frame(width: 320, height: 100)
}
.previewLayout(.sizeThatFits)
.padding(10)
}
}
#endif
์ฐธ๊ณ ์๋ฃ
[4์ ์ฐ์ํํ ํฌ์ธ๋ฏธ๋] ๋งํ๊ฒฝ ์ฑ ๊ฐ๋ฐ๊ธฐ
SwiftUI ์์ด Xcode Preview ์ฌ์ฉํ๊ธฐ
https://gist.github.com/Koze/d7ad0b172794fd3b55fb76ebc6f03051
'๐ iOS & Swift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
iOS Issue(1) - Build input file cannot be found (0) | 2021.07.30 |
---|---|
[iOS] ๋๋ ๋ชจ๋ฅด๊ฒ ์ฌ์ฉํ๊ณ ์์๋ Generic (0) | 2021.07.29 |
[iOS] CollectionView์ PageControl๋ก Pager ๊ตฌํํ๊ธฐ (9) | 2021.07.26 |
[iOS] ๋ฐฐ์ด์ ๊ณ ์ฐจ ํจ์ - Higher Order Fuctions (0) | 2021.07.24 |
[Alamofire Mapper] URLRequestConvertible ์ฌ์ฉํด๋ณด๊ธฐ (0) | 2021.07.23 |