Skip to content

Instantly share code, notes, and snippets.

@ryanlintott
Last active December 8, 2023 15:14
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ryanlintott/d03140dd155d0493a758dcd284e68eaa to your computer and use it in GitHub Desktop.
Save ryanlintott/d03140dd155d0493a758dcd284e68eaa to your computer and use it in GitHub Desktop.
An alternative to ViewThatFits. Updated version can be found here: https://github.com/ryanlintott/LayoutThatFits
//
// LayoutThatFits.swift
// WWDC22Experiments
//
// Created by Ryan Lintott on 2022-06-08.
//
import SwiftUI
struct LayoutThatFits: Layout {
let axes: Axis.Set
let layoutPreferences: [any Layout]
init(in axes: Axis.Set = [.horizontal, .vertical], _ layoutPreferences: [any Layout]) {
self.axes = axes
self.layoutPreferences = layoutPreferences
}
var layouts: [AnyLayout] {
layoutPreferences.map { AnyLayout($0) }
}
func layoutThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> AnyLayout? {
layouts.first(where: { layout in
var cache = layout.makeCache(subviews: subviews)
let size = layout.sizeThatFits(proposal: proposal, subviews: subviews, cache: &cache)
let widthFits = size.width <= (proposal.width ?? .infinity)
let heightFits = size.height <= (proposal.height ?? .infinity)
return (widthFits || !axes.contains(.horizontal)) && (heightFits || !axes.contains(.vertical))
})
}
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
let layout = layoutThatFits(proposal: proposal, subviews: subviews, cache: &cache) ?? layouts.last!
var cache = layout.makeCache(subviews: subviews)
return layout.sizeThatFits(proposal: proposal, subviews: subviews, cache: &cache)
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
let layout = layoutThatFits(proposal: proposal, subviews: subviews, cache: &cache) ?? layouts.last!
var cache = layout.makeCache(subviews: subviews)
layout.placeSubviews(in: bounds, proposal: proposal, subviews: subviews, cache: &cache)
}
}
struct ViewThatFitsExample: View {
@State private var width: CGFloat = 300
var content: some View {
Group {
Text("View")
Text("That")
Text("Fits")
}
.lineLimit(1)
.padding()
.foregroundColor(.white)
.background(Color.blue)
.fixedSize(horizontal: true, vertical: false)
}
var body: some View {
VStack {
Slider(value: $width, in: 50...400)
Text("ViewThatFits").font(.title)
Text("- Duplicate sets of subviews")
Text("- Animation not possible")
Text(
"""
ViewThatFits {
HStack {
content
}
VStack {
content
}
}
"""
)
.font(.body.monospaced())
.padding()
Spacer()
ViewThatFits {
HStack {
content
}
VStack {
content
}
}
.frame(width: width)
.background(Color.gray)
.animation(.default, value: width)
Spacer()
}
.padding()
}
}
struct LayoutThatFitsExample: View {
@State private var width: CGFloat = 300
var content: some View {
Group {
Text("Layout")
Text("That")
Text("Fits")
}
.lineLimit(1)
.padding()
.foregroundColor(.white)
.background(Color.blue)
.fixedSize(horizontal: true, vertical: false)
}
var body: some View {
VStack {
Slider(value: $width, in: 50...400)
Text("LayoutThatFits").font(.title)
Text("- One set of subviews")
Text("- Animation works!")
Text(
"""
LayoutThatFits(
in: [.horizontal],
[HStack(), VStack()]
) {
content
}
"""
)
.font(.body.monospaced())
.padding()
Spacer()
LayoutThatFits(in: [.horizontal], [HStack(), VStack()]) {
content
}
.frame(width: width)
.background(Color.gray)
.animation(.default, value: width)
Spacer()
}
.padding()
}
}
struct LayoutThatFits_Previews: PreviewProvider {
static var previews: some View {
ViewThatFitsExample()
LayoutThatFitsExample()
}
}
@ryanlintott
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment