Apple provides us two ways to use UIKit views in SwiftUI

Overview

RepresentableKit

Apple provides us two ways to use UIKit views in SwiftUI:

  1. UIViewRepresentable
  2. UIViewControllerRepresentable

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.

Installation

Use Swift Package Manager to install RepresentableKit.

  • Use Xcode and add this repository as a dependency.
  • Alternatively, add this repository as a dependency to your Package.swift.
dependencies: [
    .package(url: "https://github.com/yumemi-inc/RepresentableKit.git", .upToNextMajor(from: "0.1.0"))
]

UIViewRepresentable

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).

  1. Parent proposes a size for child
  2. Child chooses its own size
  3. 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

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:

  1. UIViewFlexibility to control min/max sizes via the layout priorities.
  2. UIViewIdealSizeCalculator to control the ideal size.

You can specify a custom sizing behavior by modifying UIViewAdaptor.init attributes.

UIViewControllerRepresentable

For now, RepresentableKit does not provide any compatibility with UIViewControllerRepresentable.

You might also like...
SwiftUI views that arrange their children in a flow layout.
SwiftUI views that arrange their children in a flow layout.

SwiftUI Flow SwiftUI views that arrange their children in a flow layout. HFlow A view that arranges its children in a horizontal flow. Usage ScrollVie

SwiftUI views that arrange their children in a Pinterest-like layout
SwiftUI views that arrange their children in a Pinterest-like layout

SwiftUI Masonry SwiftUI views that arrange their children in a Pinterest-like layout. HMasonry A view that arranges its children in a horizontal mason

Reframing SwiftUI Views. A collection of tools to help with layout.
Reframing SwiftUI Views. A collection of tools to help with layout.

Overview A Swift Package with a collection of SwiftUI framing views and tools to help with layout. Size readers like WidthReader, HeightReader, and on

SwiftUI stack views with paged scrolling behaviour.

SwiftUI PageView SwiftUI stack views with paged scrolling behaviour. HPageView A view that arranges its children in a horizontal line, and provides pa

In SwiftUI, a property-wrapper provides velocity in pt/s from gesture
In SwiftUI, a property-wrapper provides velocity in pt/s from gesture

swiftui-GestureVelocity In SwiftUI, a property-wrapper provides velocity in pt/s from gesture Instructions @GestureVelocity private var velocity: CGVe

Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]
Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]

Extremely Fast views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainabl

Simple static table views for iOS in Swift.
Simple static table views for iOS in Swift.

Simple static table views for iOS in Swift. Static's goal is to separate model data from presentation. Rows and Sections are your “view models” for yo

The fast path to autolayout views in code
The fast path to autolayout views in code

NorthLayout The fast path to autolayout views in code Talks https://speakerdeck.com/banjun/auto-layout-with-an-extended-visual-format-language at AltC

Compose is a library that helps you compose complex and dynamic views.

Compose is a data driven library that will help compose your complex and dynamic Views. It helps you create complex and dynamic Views using easy and s

Comments
  • Modifying state during view update, this will cause undefined behavior.

    Modifying state during view update, this will cause undefined behavior.

    Hello,

    First of all I like the idea behind this library! However in practice it doesn't look usable yet: image

    This is what I get in some toy project, however in a real use case this issue is actually hitting hard with a crash both in debug and release due to this precondition failure:

    [error] precondition failure: setting value during update: 2072
    

    As far as I see, all methods exposed by UIViewRepresentable are called during the view update, so no state can be changed there and the current approach seems to hit a dead end.

    I'm using Xcode 13.2.1 on macOS 12.1.

    opened by Ceylo 3
  • Make UIViewHolder immutable

    Make UIViewHolder immutable

    What

    • Resolves #1

    Trying to update ideal size state right after UIView generation caused a run-time warning, since this was effectively modifying a @State during body access.

    How

    • Don't update ideal size on UIView generation
      • We didn't really need this anyway, as we will always get at least one more update after the initial layout pass.
    • Don't mutate UIViewHolder state, instead hold and update the calculated ideal size inside UIViewAdaptor

    Review Points

    • Does this PR resolve the issue?
    • Are there any other problems with this fix?
    opened by auramagi 0
  • Broken constraints when using custom view with `UIStackView` inside.

    Broken constraints when using custom view with `UIStackView` inside.

    Something wrong with UIStackView inside UIViewAdaptor, but I can't figure out what exactly. I tried to use different flexibility, idealSizeCalculator but still got this errors in console:

    Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. ( "<NSLayoutConstraint:0x28241fed0 UIView:0x13dd10ce0.height == 100 (active)>", "<NSLayoutConstraint:0x28241fb60 'UISV-alignment' UIView:0x13dd0ec50.bottom == UIView:0x13dd10ce0.bottom (active)>", "<NSLayoutConstraint:0x28241fb10 'UISV-alignment' UIView:0x13dd0ec50.top == UIView:0x13dd10ce0.top (active)>", "<NSLayoutConstraint:0x28241fc00 'UISV-canvas-connection' UIStackView:0x13dd10950.top == UIView:0x13dd0ec50.top (active)>", "<NSLayoutConstraint:0x28241fbb0 'UISV-canvas-connection' V:[UIView:0x13dd0ec50]-(0)-| (active, names: '|':UIStackView:0x13dd10950 )>", "<NSLayoutConstraint:0x28241f610 'UIView-Encapsulated-Layout-Height' UIStackView:0x13dd10950.height == 711 (active)>" )

    Will attempt to recover by breaking constraint <NSLayoutConstraint:0x28241fed0 UIView:0x13dd10ce0.height == 100 (active)>

    Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

    var body: some View {
        VStack {
            UIViewAdaptor {
                let stack = UIStackView()
                
                let firstView = UIView()
                firstView.backgroundColor = .red
                
                let secondView = UIView()
                secondView.backgroundColor = .blue
                
                
                [firstView, secondView].forEach {
                    $0.translatesAutoresizingMaskIntoConstraints = false
                }
            
                NSLayoutConstraint.activate([
                    firstView.heightAnchor.constraint(equalToConstant: 100),
                    secondView.heightAnchor.constraint(equalToConstant: 100)
                ])
                
                stack.addArrangedSubview(firstView)
                stack.addArrangedSubview(secondView)
                
                return stack
            }
            Spacer()
        }
    }
    
    opened by denandreychuk 0
Releases(v0.1.2)
Owner
YUMEMI Inc.
YUMEMI Inc.
Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Not Apple Autolayout wrapper. Provides placeholders. Linux support.

CGLayout Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Has cross-hierarchy coordinate space. Implemen

Koryttsev Denis 45 Jun 28, 2022
Arrange views in your app’s interface using layout tools that SwiftUI provides.

Composing custom layouts with SwiftUI Arrange views in your app's interface using layout tools that SwiftUI provides. Overview This sample app demonst

Apple Sample Code 0 Jun 9, 2022
A Swift utility to make updating table views/collection views trivially easy and reliable.

ArrayDiff An efficient Swift utility to compute the difference between two arrays. Get the removedIndexes and insertedIndexes and pass them directly a

Adlai Holler 100 Jun 5, 2022
Controls-Practice-UIKit- - Controls Practice (UIKit)

Controls Practice (UIKit) Change a number 0 to 255 different ways: Button (+1) I

null 1 Feb 13, 2022
A Demo for Shimmer Effect with two methods

ShimmerDemo A Demo for Shimmer Effect with two methods Use Blend Mode + CAGradie

SketchK 9 Feb 9, 2022
FreeOTP is a two-factor authentication application for systems utilizing one-time password protocols

FreeOTP FreeOTP is a two-factor authentication application for systems utilizing one-time password protocols. Tokens can be added easily by scanning a

FreeOTP 551 Dec 28, 2022
✂ Easy to use and flexible library for manually laying out views and layers for iOS and tvOS. Supports AsyncDisplayKit.

ManualLayout Table of Contents Installation Usage API Cheat Sheet Installation Carthage Add the following line to your Cartfile. github "isair/ManualL

Baris Sencan 280 Sep 29, 2022
ResponderChainDemo - Learned how to use responder chain for communication between the views

ResponderChainDemo Learned how to use responder chain for communication between

Prashant Gaikwad 1 Sep 30, 2022
Expose layout margins and readable content width to SwiftUI's Views

SwiftUI Layout Guides This micro-library exposes UIKit's layout margins and readable content guides to SwiftUI. Usage Make a view fit the readable con

Thomas Grapperon 26 Dec 23, 2022
An experiment creating a particle emitter using the new TimelineView and Canvas views in SwiftUI

Particle Emitter An experiment creating a particle emitter using the new Timelin

Emilio Peláez 8 Nov 11, 2022