Live Preview功用能让咱们在开发过程中实时看到UI层面的改动作用,不用每次稍有改动就得运转整个APP,并且跳转到对应的页面才干看到实际作用,这必然会极大的提高开发功率。SwiftUIPreview是为SwiftUI规划的,但是在UIKit环境下也可以运用,本文针对这个场景的实践做一个记录。

新建一个swift项目,删去Storyboard

在info.plist中删去

<key>UISceneStoryboardFile</key>
<string>Main</string>

在SceneDelegate中,设置window的根控制器

guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let vc = ViewController()
let nav = UINavigationController(rootViewController: vc)
window.rootViewController = nav
window.makeKeyAndVisible()
self.window = window

给UIView和UIViewController 增加 Preview的extension

创立UIViewControllerExtension.swift文件

import UIKit
    import SwiftUI
    extension UIViewController {
        @available(iOS 13, *)
        private struct Preview: UIViewControllerRepresentable {
            // 用于注入当前的viewcontroller
            let viewController: UIViewController
            func makeUIViewController(context: Context) -> UIViewController {
                return viewController
            }
            func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
                //
            }
        }
        @available(iOS 13, *)
        func showPreview() -> some View {
            Preview(viewController: self)
        }
    }

创立UIViewExtension.swift

import UIKit
import SwiftUI
extension UIView {preview-uikit-views-in-xcode-3543
    @available(iOS 13, *)
    private struct Preview: UIViewRepresentable {
        typealias UIViewType = UIView
        let view: UIView
        func makeUIView(context: Context) -> UIView {
            return view
        }
        func updateUIView(_ uiView: UIView, context: Context) {}
    }
    @available(iOS 13, *)
    func showPreview() -> some View {
        // inject self (the current UIView) for the preview
        Preview(view: self)
    }
}

具体运用?

新建一个用户信息的view,增加imageview、和两个label,约束用的是SnapKit

import UIKit
import SnapKit
class UserInfo: UIView{
    override init(frame: CGRect) {
        super.init(frame: frame)
        setUpView()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setUpView()
    }
    lazy var titleLabel: UILabel = {
        let label = UILabel()
        label.text = "我的信息"
        label.font = .boldSystemFont(ofSize: 24)
        label.textAlignment = .left
        return label
    }()
    let imageWidth = 60.0
    lazy var profileImageView: UIImageView = {
        let imageView = UIImageView(image: UIImage(named: "yaofan"))
        imageView.clipsToBounds = true
        imageView.layer.cornerRadius = imageWidth / 2
        imageView.layer.borderWidth = 1
        imageView.layer.borderColor = UIColor.black.withAlphaComponent(0.1).cgColor
        return imageView
    }()
    lazy var nameLabel: UILabel = {
        let label = UILabel()
        label.font = .systemFont(ofSize: 18)
        label.text = "Luseike"
        return label
    }()
    lazy var roleLabel: UILabel = {
        let label = UILabel()
        label.font = .systemFont(ofSize: 15)
        label.textColor = .gray
        label.text = "iOS Engineer"
        return label
    }()
    lazy var nameStackView: UIStackView = {
        let stack = UIStackView(arrangedSubviews: [nameLabel, roleLabel])
        stack.axis = .vertical
        stack.alignment = .leading
        stack.spacing = 4
        return stack
    }()
    func setUpView(){
        self.addSubview(titleLabel)
        self.addSubview(profileImageView)
        self.addSubview(nameStackView)
        titleLabel.snp.makeConstraints { make in
            make.top.equalTo(self.safeAreaLayoutGuide.snp.top).offset(10)
            make.leading.trailing.equalToSuperview().inset(24)
        }
        profileImageView.snp.makeConstraints { make in
            make.width.height.equalTo(imageWidth)
            make.top.equalTo(titleLabel.snp.bottom).offset(16)
            make.leading.equalToSuperview().offset(20)
        }
        nameStackView.snp.makeConstraints { make in
            make.leading.equalTo(profileImageView.snp.trailing).offset(12)
            make.centerY.equalTo(profileImageView.snp.centerY)
            make.trailing.equalToSuperview().offset(-20)
        }   
    }
}

在view底部,增加一个承继PreviewProvider的struct,由于上面现已增加了UIView的扩展,会call到showPreview()使其有能力来preview咱们自定义的view

#if DEBUG
import SwiftUI
@available(iOS 13, *)
struct HeaderView_Preview: PreviewProvider {
    static var previews: some View {
        HeaderView().showPreview()
    }
}
#endif

swift中让UIViewController和UIView具有Live Preview效果

controller文件的preview运用跟view是相似的

#if DEBUG
import SwiftUI
@available(iOS 13, *)
struct ViewController_Preview: PreviewProvider {
    static var previews: some View {
        ViewController().showPreview()
    }
}
#endif