Live preview for your UIKit views
One of the best features of SwiftUI is being able to preview your code during development, but did you know that you can also use SwiftUI to preview your old-fashioned UIKit ViewControllers and Views?
Here’s how…
We will be using a protocol called UIViewControllerRepresentable
, which allows us to create and manage a UIViewController
from SwiftUI.
Start by creating a simple iOS App in Xcode, and choosing a UIKit App Delegate
Lifecycle.
Then, in your app main ViewController
, create a struct that conforms to the UIViewControllerRepresentable
protocol, and accepts a closure with a ViewController
return type.
import UIKit
import SwiftUI
struct ViewControllerPreview: UIViewControllerRepresentable {
let viewControllerGenerator: () -> UIViewController
init(_ viewControllerGenerator: @escaping () -> UIViewController) {
self.viewControllerGenerator = viewControllerGenerator
}
func makeUIViewController(context: Context) -> some UIViewController {
return viewControllerGenerator()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
The protocol requires two methods, that will take care of the creation and updating of your ViewController, but as we will only be using this for creating previews, you can leave the update method empty.
Now update the ViewController
by adding a simple colored UILabel
and some constraints.
final class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let helloView = UILabel()
helloView.text = "Hello World"
helloView.textAlignment = .center
helloView.textColor = .black
view.addSubview(helloView)
helloView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
helloView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
helloView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
helloView.widthAnchor.constraint(equalTo: view.widthAnchor),
helloView.heightAnchor.constraint(equalTo: view.widthAnchor),
])
}
}
And then, we just need to create a PreviewProvider that passes the ViewController
to our previewer.
struct ProfileViewController_Previews: PreviewProvider {
static var previews: some View {
ViewControllerPreview {
ViewController()
}
}
}
And that’s it! After you add your previewer, Xcode will automatically start generating real-time previews for your UIKit ViewController!
Here’s a demo
What about an extension?
Now, that everything works, we can write a simple UIViewController
extension, to enable previews for all UIViewControllers
in your app.
// UIViewController+Preview.swift
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() -> some View {
Preview(viewController: self)
}
}
And then, for any UIKit
ViewController
you want to enable previews, just do as follows:
#if DEBUG
import SwiftUI
struct ViewController_Preview: PreviewProvider {
static var previews: some View {
ViewController().showPreview()
}
}
#endif
What about UIViews?
For UIViews
, we have the UIVIewRepresentable
protocol, which has the same skills as UIViewControllerRepresentable
, so we can also write a quick extension to enable previews for any UIView
, like this:
// UIView+Preview.swift
import UIKit
import SwiftUI
extension UIView {
private struct Preview: UIViewRepresentable {
let view: UIView
func makeUIView(context: Context) -> UIView {
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
func showPreview() -> some View {
Preview(view: self)
}
}
That way, you can also use the showPreview()
method from any UIView in your app.
Conclusion & sample project
Now that you know how to preview your UIKit
stuff its time to go back to that old app of yours and preview the hell out of everything.
As usual, you can grab a copy of the project from Github here, and reach out via Twitter or the comments below with questions or ideas.
See you soon!
Photo by: Amza Andrei