Skip to content

Instantly share code, notes, and snippets.

@shaps80
Last active May 13, 2024 12:23
Show Gist options
  • Save shaps80/0940a814d3704e9201a9477d610589b1 to your computer and use it in GitHub Desktop.
Save shaps80/0940a814d3704e9201a9477d610589b1 to your computer and use it in GitHub Desktop.
import SwiftUI
public extension View {
/// Automatically sizes the view to match its content size
/// - Parameters:
/// - width: An optional binding to a width property. Pass nil to opt-out of auto-sizing
/// - height: A optional binding to a height property. Pass nil to opt-out of auto-sizing
/// - alignment: The alignment of this view inside the resulting frame. Note that most alignment values have no apparent effect when the size of the frame happens to match that of this view.
func frame(width: Binding<CGFloat>? = nil, height: Binding<CGFloat>? = nil, alignment: Alignment = .center) -> some View {
onSizeChange {
width?.wrappedValue = $0.width
height?.wrappedValue = $0.height
}
.frame(width: width?.wrappedValue, height: height?.wrappedValue, alignment: alignment)
}
/// Adds an action to perform when the view's size changes.
/// - Parameter perform: The action to perform when the size changes. The action closure passes the new value as its parameter.
func onSizeChange(_ perform: @escaping (CGSize) -> Void) -> some View {
overlay(
GeometryReader { geo in
Color.clear.preference(key: SizePreferenceKey.self, value: geo.size)
}
).onPreferenceChange(SizePreferenceKey.self) { value in
perform(value)
}
}
}
private struct SizePreferenceKey: PreferenceKey {
public static var defaultValue: CGSize = .zero
public static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment