Apple provides us two ways to use UIKit views in SwiftUI:
The way those Views interact with the layout system are not really well-documented and can be a pain to implement correctly.
RepresentableKit seeks to minimize the hurdle of using UIKit views, especially for previews where it is not reasonable to spend time fixing layout issues manually. We provide a wrapper over UIViewRepresentable
that abstracts the internal SwiftUI layout process and has the most useful (opinionated) default layout handling.
A minimal usage example would be previewing a simple UILabel
with multiple lines of text.
import SwiftUI
import RepresentableKit
struct UILabel_Previews: PreviewProvider {
static var previews: some View {
UIViewAdaptor {
let view = UILabel()
view.numberOfLines = 0
view.text = "To love the journey is to accept no such end. I have found, through painful experience, that the most important step a person can take is always the next one."
return view
}
.padding()
}
}
Note that had we chosen to write this naively with UIViewRepresentable
without further customizing layout, it would render as a single line of text.
For further examples, see the provided Examples
project.
Use Swift Package Manager to install RepresentableKit.
- Use Xcode and add this repository as a dependency.
- Alternatively, add this repository as a
dependency
to yourPackage.swift
.
dependencies: [
.package(url: "https://github.com/yumemi-inc/RepresentableKit.git", .upToNextMajor(from: "0.1.0"))
]
Let's look at how UIViewRepresentable
chooses an appropriate size for the contained UIView.
First thing we need to know is the general SwiftUI layout procedure (from WWDC19: Building Custom Views with SwiftUI).
- Parent proposes a size for child
- Child chooses its own size
- Parent places child in parent's coordinate space
The second bit of knowledge is that SwiftUI views have 3 size values for each dimension: min, ideal, max. Min and max are used to clamp the proposed size, while ideal size is used when the proposed size is nil (achived by .fixedSize() view modifier).
UIViewRepresentable
uses Auto Layout intrinsic content size and layout priorities to map the contained UIView size.
Intrinsic Content Size | Compression Resistance | Hugging | Result size |
---|---|---|---|
(-1) | - | - | 0 … 0 … ∞ |
x | < 750 | < 750 | 0 … x … ∞ |
x | < 750 | >= 750 | 0 … x … x |
x | >= 750 | < 750 | x … x … ∞ |
x | >= 750 | >= 750 | x … x … x |
- The result size is shown as
min … ideal … max
-1
=UIView.noIntrinsicMetric
750
=UILayoutPriority.defaultHigh
UIViewRepresentable
will also listen to invalidateIntrinsicContentSize() to update its size.
Using UILabel as an example, with default priorities and intrinsic size it will map to size x … x … ∞
, e.g. it will not become smaller than the intrinsic size, but can grow to any size; with fixedSize()
applied it will become its intrinsic content size.
Most custom UIViews, however, don't have an intrinsic size, so they will map to 0 … 0 … ∞
, e.g. growing and shrinking to any value SwiftUI proposes and vanishing (taking size 0) with fixedSize()
applied.
RepresentableKit abstracts over those concepts in two ways:
- UIViewFlexibility to control min/max sizes via the layout priorities.
- UIViewIdealSizeCalculator to control the ideal size.
You can specify a custom sizing behavior by modifying UIViewAdaptor.init
attributes.
For now, RepresentableKit does not provide any compatibility with UIViewControllerRepresentable
.