How to preview UIView in Xcode Previews
Table of Contents
What is Xcode Preview
Apple introduces Xcode Previews and SwiftUI in WWDC 2019 (Xcode 11 and iOS 13).
You can see how SwiftUI views look in real-time, and you can interact with it without running your app using Xcode Previews.
Here is an example of Xcode Previews.
As you can see, once we add a new code, the update reflects on the preview.
And you can tap a button to change the view's state.
You can easily support sarunw.com by checking out this sponsor.
AI Grammar: Correct grammar, spell check, check punctuation, and parphrase.
How to use Xcode Previews with UIView
Since it was released at the same time as SwiftUI, you might not know you can use Xcode Previews with UIView
.
If you use UIKit, you can preview your view using Interface Builder. But this might benefit you if you create a custom view programmatically without Storyboard or nib.
In this article, I will show you how to preview UIView using Xcode Previews.
To make your views work with Xcode previews, you need to do three things.
- Import
SwiftUI
module. - Create a struct that conforms to the
PreviewProvider
protocol. - Wrap a view into a SwiftUI view using
UIViewRepresentable
.
// 1
import SwiftUI
// 2
struct UIView_Preview: PreviewProvider {
// 3
static var previews: some View {
// return any UIViewRepresentable here
}
}
1 Xcode preview works hand in hand with PreviewProvider
. It discovers preview providers and generates previews for us. PreviewProvider
is a part of the SwiftUI
module, so we need to import SwiftUI
to use it.
2 We create a struct that conforms to the PreviewProvider
.
3 Normally, we will return a SwiftUI view from the previews
static property. But we want to preview a view here, so we need to wrap it using the UIViewRepresentable
protocol.
- Steps 1 and 2 are the same regardless of SwiftUI or UIKit.
- And we have learned step 3 in how to convert a view to SwiftUI.
So, we got everything ready. Let's jump to the implementation detail.
UIViewRepresentable
First, we need to convert a view into a SwiftUI view using UIViewRepresentable
.
The process is straightforward. You just need to initialize your view and return it from makeUIView()
. You can read more about this in detail in how to convert a view to SwiftUI.
struct SwiftUIHelloWorldView: UIViewRepresentable {
// 1
func makeUIView(context: Context) -> HelloWorldView {
return HelloWorldView()
}
func updateUIView(_ view: HelloWorldView, context: Context) {}
}
1 We initialize our view and return that view from func makeUIView(context: Context) -> HelloWorldView
.
Preview Provider
After converting a view to a SwiftUI view, we are halfway done.
The only thing left is to use it for preview.
import SwiftUI
struct HelloWorldView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIHelloWorldView()
}
}
Here is the result.
Caveat
There is a limitation when previewing UIView
using UIViewRepresentable
.
It doesn't work very well with .previewLayout(.sizeThatFits)
. UIViewRepresentable
will always render using the currently selected device size.
This example won't show the SwiftUIHelloWorldView
intrinsic size but will render the same size as a device.
struct HelloWorldView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIHelloWorldView()
.previewLayout(.sizeThatFits)
}
}
You can easily support sarunw.com by checking out this sponsor.
AI Grammar: Correct grammar, spell check, check punctuation, and parphrase.
Helpers
Having to create UIViewRepresentable
for every view you want to preview is cumbersome.
If you have a lot of view controller classes, you should come up with a helper method for easily wrapping your view into UIViewRepresentable
.
There are many ways to tackle this problem. I can give you one example here.
import SwiftUI
import UIKit
// 1
struct PreviewContainer<T: UIView>: UIViewRepresentable {
// 2
let view: T
// 3
init(_ viewBuilder: @escaping () -> T) {
view = viewBuilder()
}
// MARK: - UIViewRepresentable
func makeUIView(context: Context) -> T {
// 4
return view
}
// 5
func updateUIView(_ view: T, context: Context) {
view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
view.setContentHuggingPriority(.defaultHigh, for: .vertical)
}
}
1 I create a generic struct that can accept any type that is a subclass of UIView
. We make this struct conform to UIViewRepresentable
since it will act as our SwiftUI view wrapper for our view.
2 We define an instance property that will hold a view.
3 We create an initializer that accepts a closure that returns a view. We execute it to get the resulting view.
4 We return the resulting view from makeUIView()
.
5 We set content hugging priority to .defaultHigh
to mitigate the limitation mentioned in the previous section.
This is how we use PreviewContainer
.
import UIKit
import SwiftUI
class HelloWorldView: UIView {
...
}
struct HelloWorldView_Previews: PreviewProvider {
static var previews: some View {
// 1
PreviewContainer {
return HelloWorldView()
}
}
}
1 We provide a closure that returns a view as an argument for PreviewContainer
.
We can use this PreviewContainer
to preview any view without a need to recreate UIViewRepresentable
.
Read more article about Xcode, Xcode Previews, or see all available topic
Enjoy the read?
If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.
Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.
If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.
Become a patron Buy me a coffee Tweet ShareSwiftUI Button can't tap on a background
Learn how to make the whole area of a button tappable.
Xcode Previews with UIKit and AppKit in Xcode 15
Learn about the Xcode Previews improvement in Xcode 15.