Skip to content

Instantly share code, notes, and snippets.

@vincefried
Created September 8, 2023 06:58
Show Gist options
  • Save vincefried/286231cfde0583a33703ea78f30d2bed to your computer and use it in GitHub Desktop.
Save vincefried/286231cfde0583a33703ea78f30d2bed to your computer and use it in GitHub Desktop.
A small demonstration of my problems with sizing a wrapped UIButton in a UIViewRepresentable properly.
import SwiftUI
struct ContentView: View {
var body: some View {
ButtonRepresentable(title: "Test") {
print("press")
}
// .frame(maxWidth: .infinity) // Comment in or out for the two cases I want to support.
}
}
struct ButtonRepresentable: UIViewRepresentable {
let title: String
let action: () -> Void
func makeUIView(context: Context) -> UIButton {
let button = UIButton(primaryAction: UIAction(handler: { _ in action() }))
// Setting high content hugging prio makes button use its intrinsic content size when using the default
// sizing algo or not implementing sizeThatFits or returning nil in sizeThatFits.
button.setContentHuggingPriority(.defaultHigh, for: .horizontal)
button.setContentHuggingPriority(.defaultHigh, for: .vertical)
button.backgroundColor = .red
button.setTitle(title, for: .normal)
return button
}
func updateUIView(_ uiView: UIButton, context: Context) {
uiView.setTitle(title, for: .normal)
}
func sizeThatFits(_ proposal: ProposedViewSize, uiView: UIButton, context: Context) -> CGSize? {
switch proposal {
case .infinity:
// This case never gets called, even when using .frame(maxWidth: .infinity)
guard let width = proposal.width, let height = proposal.height else {
return uiView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
}
return CGSize(width: width, height: height)
default:
// This case always gets called.
// The algorithm passes a proposed size when not explicitly specifying a frame but also when explicitly specifying one. That makes it impossible to distinguish.
// I would like a way to determine:
//
// 1. If frame(maxWidth: .infinity) was used -> make the view expand to full size
// 2. If an explicit size e.g. frame(width: 200) was used -> use that size
// 3. If nothing was used -> use default sizing algo and make the wrapped UIButton use its intrinsicContentSize
guard let width = proposal.width, let height = proposal.height else {
return nil
}
return CGSize(width: width, height: height)
}
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment